Compare commits
395 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 940aa17049 | |||
| 536c360cd3 | |||
| 419173f859 | |||
| 3c31f748f1 | |||
| b0fc844ba2 | |||
| 3a692f8080 | |||
| c15e459968 | |||
| db39006c44 | |||
| 7c74cd3910 | |||
| d09e145e84 | |||
| 642ce70984 | |||
| 6e1442bfa7 | |||
| d523a66834 | |||
| d530cd12b3 | |||
| 949e0caf94 | |||
| 98c0290a13 | |||
| 35f84c0e43 | |||
| 612ba453d0 | |||
| 45e68195b9 | |||
| e9018ca76a | |||
| 8d0e2cb079 | |||
| 1cb6257bab | |||
| 3820d5d2be | |||
| 7d47ced769 | |||
| ea9d2e6050 | |||
| a01c6de010 | |||
| 020b7dcb49 | |||
| d74e8be2b5 | |||
| 28944a18db | |||
| d866e7315b | |||
| 217767bb7f | |||
| 7ac5548953 | |||
| 77d95a2ccb | |||
| 025a00781e | |||
| c7489c6d4d | |||
| fe0a150658 | |||
| bed733c86d | |||
| c2d4052ffc | |||
| eac204a06d | |||
| c270851b98 | |||
| f5e4c44183 | |||
| b4241111de | |||
| c0c1997a6d | |||
| 063d798f37 | |||
| 52d5599ff2 | |||
| e972cd3ef3 | |||
| b949f7ae45 | |||
| 95f07d4a29 | |||
| 2efa7007da | |||
| 0d31f8cf80 | |||
| 8ede6eb071 | |||
| 89ee8b5544 | |||
| 889ced5063 | |||
| bd642555ce | |||
| a014760344 | |||
| faf358bfe0 | |||
| 6e2580ca50 | |||
| 4aaf81b43d | |||
| 8e6ccd11b1 | |||
| 5d9da95fc8 | |||
| 9525024349 | |||
| 58f86af654 | |||
| 7459866078 | |||
| c122a4abc7 | |||
| 368b66502a | |||
| a9cfe079a3 | |||
| 6c7657a8f2 | |||
| 9fc24562b5 | |||
| e3bd76b13e | |||
| a470b60534 | |||
| 99fae65ea3 | |||
| 4deccac46a | |||
| 453d4d1846 | |||
| 0a4e899e01 | |||
| 10dee69379 | |||
| ed10e934d3 | |||
| 153e4369e1 | |||
| 5c9b17c5f1 | |||
| dd353c7247 | |||
| b41a56380e | |||
| f3a5e2a31a | |||
| e05d1c34cd | |||
| fd43070bbc | |||
| dd8fa044d9 | |||
| 57f962a915 | |||
| 050ab7d479 | |||
| d20bd99b07 | |||
| 8ac197d07f | |||
| 593b942437 | |||
| 25dcb6f193 | |||
| 98f33548d4 | |||
| feff218f2f | |||
| 668cba6e7e | |||
| 2bc8f72dff | |||
| 4d504d86b7 | |||
| 10e40f0366 | |||
| 53bc7655aa | |||
| 1c127290cf | |||
| dd8f4b46d4 | |||
| 920fb333c0 | |||
| 4c24a03e08 | |||
| 2c0c7e5e71 | |||
| 26a05fb33f | |||
| 6f7aea5351 | |||
| c66e551c11 | |||
| 526fbf8a47 | |||
| 64d99256ef | |||
| 8b39496a87 | |||
| 577b37cb2c | |||
| 205201f7b3 | |||
| 08c86b9e9b | |||
| 3aa4bf022a | |||
| 1f2d7d154a | |||
| 5884f9a07b | |||
| 27d0c2181b | |||
| c6b6921b4e | |||
| 1328297d48 | |||
| b5c18dedf0 | |||
| 4c9c8e00db | |||
| 0c79de9112 | |||
| 829c7c9a1b | |||
| abd981c6a7 | |||
| 4d10388df2 | |||
| d89e40d7d6 | |||
| 3efab60a57 | |||
| 1d7543be8c | |||
| 116d8541d2 | |||
| 78d51b1fc6 | |||
| fd753889a2 | |||
| f6822347de | |||
| dcd13e2b08 | |||
| 1137b7d072 | |||
| c7a5fc2f35 | |||
| 167a8dc276 | |||
| bf569af16b | |||
| 7c34492487 | |||
| 9d1ded28c2 | |||
| a9749a86b8 | |||
| 030b9db219 | |||
| 3cbb18b105 | |||
| b50f1289b6 | |||
| 23c4e59166 | |||
| ddc5472056 | |||
| 3139dba346 | |||
| 768057b4db | |||
| e91e315d19 | |||
| c8238b4d45 | |||
| dcb601fede | |||
| 1fc5f07ed3 | |||
| 1652dfe456 | |||
| 3a4b7e8995 | |||
| 716143b33c | |||
| 74ac275d08 | |||
| 7a902dea3a | |||
| 88b7f71a50 | |||
| a78ed9eed1 | |||
| b4f299ec91 | |||
| 74d9dbf696 | |||
| 567fa6adc3 | |||
| 4c3a2ea83c | |||
| c3cad10fcf | |||
| e6993ed682 | |||
| ac2b9ee95c | |||
| 55749ae39f | |||
| d56954a828 | |||
| 4eb1e079b3 | |||
| d673f8abd7 | |||
| 398dde36b3 | |||
| c92e401361 | |||
| ebb4054cd6 | |||
| 7a68e34f61 | |||
| 9c82b32001 | |||
| 99c79db0c7 | |||
| 3f26d44a6f | |||
| 48878c24fd | |||
| 8dd23a1375 | |||
| d1bd09fa41 | |||
| db88f1f307 | |||
| 03f1265690 | |||
| 8fd70353e6 | |||
| 9d57a8057f | |||
| 1f77d72592 | |||
| e2442de0b6 | |||
| 5d5d2e6e42 | |||
| 4112274408 | |||
| 1aabe01886 | |||
| 734a176cf0 | |||
| d1d8f42cde | |||
| 0123b42676 | |||
| cb63696054 | |||
| a3bc3ce5b6 | |||
| d029483cde | |||
| 19115e20a7 | |||
| 8cda42019a | |||
| 4aca40f697 | |||
| 84a8b1b2df | |||
| 880dcd0f94 | |||
| 269bb26b3c | |||
| 7d87eef1fc | |||
| de672b1357 | |||
| f48f788115 | |||
| a0a3400c59 | |||
| 49592cea31 | |||
| b4bc353659 | |||
| f074a0ee86 | |||
| 9c2abf9a02 | |||
| 17d08f6cc5 | |||
| 85fec2dfe4 | |||
| cd99faeb67 | |||
| 9e0c4e13d7 | |||
| ee2c305e37 | |||
| 4a3222dc50 | |||
| 0abefe1f2b | |||
| caa6f445e4 | |||
| 907312d17a | |||
| 14b13ec478 | |||
| 9e687b46e9 | |||
| 5cd189b68d | |||
| 5a774101e0 | |||
| 6019d849b2 | |||
| 3b1d619baf | |||
| 9eb88d21ba | |||
| f2d10a9bb9 | |||
| 580d8124c0 | |||
| af46f59162 | |||
| e194ef059f | |||
| a9be3e27e3 | |||
| 0f9ed5c5e0 | |||
| 989faa4667 | |||
| 45d210a442 | |||
| d0577075b6 | |||
| 96e9075ac9 | |||
| d714e336b6 | |||
| 10bf14282c | |||
| fa963675f4 | |||
| 7511cf3462 | |||
| 78be4e8724 | |||
| 86a58a82d8 | |||
| 17342a4ff3 | |||
| 3e50a250e9 | |||
| 0143e8bcf4 | |||
| 21ee8ef703 | |||
| 77ccb0f467 | |||
| d12b2d9822 | |||
| 6a81c53788 | |||
| e2a4284326 | |||
| ed9c55b892 | |||
| 4462ed30c7 | |||
| badb6a92c4 | |||
| c6f435069e | |||
| ff9be7de2c | |||
| eec220f2fc | |||
| 8694eaac19 | |||
| 8aeddd78bf | |||
| 3df36d632c | |||
| 7c31894342 | |||
| 8f89e0bde5 | |||
| 102e20c11b | |||
| ff698a03ec | |||
| c917c9860c | |||
| fdf8e3668f | |||
| 6d791cfa86 | |||
| e3d4c8d5a4 | |||
| 6594812c15 | |||
| ece21a4a2b | |||
| 479eda9446 | |||
| d498f9b7da | |||
| 976ad2a7de | |||
| 507928af8a | |||
| 6b8b0e2468 | |||
| df05ccb9af | |||
| 1d82d302bd | |||
| 10a1109b79 | |||
| d6a6c93a88 | |||
| d6fdedd4ab | |||
| a3de11a763 | |||
| 051e6b38e2 | |||
| 2d1b49b953 | |||
| 71f49c970e | |||
| 3bef0a6cf2 | |||
| 168a1fed2a | |||
| 3016cb824d | |||
| 9b1c66d4e5 | |||
| 496eeb0f73 | |||
| 77414d0d57 | |||
| d1dbe6db4a | |||
| 7070a23d97 | |||
| dec2ad8603 | |||
| c89a1b99fd | |||
| f30091c8dc | |||
| 6f32cfee2b | |||
| d6f442bf41 | |||
| 896db11641 | |||
| fb23adbcd3 | |||
| 9bdca4fbe0 | |||
| 6442cf75ee | |||
| ed3fc39176 | |||
| eaca013a20 | |||
| 864cd27438 | |||
| 0aa44e4539 | |||
| 4897a4a166 | |||
| 0bad3d746d | |||
| 67125ee5f4 | |||
| 9d30d31afb | |||
| cff6fab1cd | |||
| d3843bbc7e | |||
| f47a98b31f | |||
| 48d8fb92ff | |||
| d5f83a1a23 | |||
| a29971b021 | |||
| 16ec7c1d7c | |||
| 4f4d28b56b | |||
| 6de29467ce | |||
| cfc96245ba | |||
| 935d259396 | |||
| 9cfbcde257 | |||
| 9d7fd4b062 | |||
| f1659f3f40 | |||
| c63863df2b | |||
| 2fb35f856c | |||
| 6709fdbb6d | |||
| 7f9e015071 | |||
| da01d735a1 | |||
| 8ad9815afc | |||
| a1ed46a130 | |||
| c62a946756 | |||
| 0846707178 | |||
| 9b6d7fc1e0 | |||
| 51e71e8aa9 | |||
| e46ad4a1a1 | |||
| dec33c29aa | |||
| e44811963c | |||
| cb5ae56e63 | |||
| 10f73026e9 | |||
| d30be99a47 | |||
| 9467ac7707 | |||
| 4bac2a4bdb | |||
| 790cae52ca | |||
| e943107851 | |||
| 3ff1ef47f8 | |||
| 305111bcd3 | |||
| aadce0acfa | |||
| fd0a5bef1e | |||
| e8c2c263b7 | |||
| 878e330148 | |||
| fd4de847ce | |||
| a25615ff66 | |||
| 56d043a636 | |||
| 1ed4e401f1 | |||
| f1246ad631 | |||
| b91f9efddc | |||
| 4c03ce0895 | |||
| 470e23e25b | |||
| 2af29ca628 | |||
| 5cb6386af1 | |||
| 0ac32aff37 | |||
| 9400966aa9 | |||
| 55f56db61f | |||
| 09b691381c | |||
| 37e349dd89 | |||
| ddd9d6e95e | |||
| 34a5bd4223 | |||
| 734cd75ed3 | |||
| ee73cabac3 | |||
| 9455300763 | |||
| 5f4a113242 | |||
| b680e40303 | |||
| d1221854c8 | |||
| ec77087e21 | |||
| af06a498dc | |||
| 2ba7a4066d | |||
| 3475c90d37 | |||
| f6c82c8a86 | |||
| 8a65065551 | |||
| ec372682b8 | |||
| 85b30e2ac5 | |||
| 0c7478e4fa | |||
| 1b52ec70f0 | |||
| f4f245e011 | |||
| 7d8ad43bd2 | |||
| a3de6b7961 | |||
| 153502a7f5 | |||
| d5bb3640d5 | |||
| 5c37c61b42 | |||
| 926ff49e12 | |||
| 90b9c2e2d3 | |||
| c50947b75e | |||
| c61928e007 | |||
| 6f09229a3d | |||
| 16291ec5bd | |||
| d96c728295 | |||
| 17da93d42b | |||
| e49444d535 | |||
| 19cedc5bf8 | |||
| 9d2fbd521b |
+97
-88
@@ -514,100 +514,105 @@ if ($interactive) {
|
||||
$database = $databases[$CFG->dbtype];
|
||||
|
||||
|
||||
// ask for db host
|
||||
if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('databasehost', 'install'));
|
||||
if ($options['dbhost'] !== '') {
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbhost']);
|
||||
// We cannot do any validation until all DB connection data is provided.
|
||||
$hintdatabase = '';
|
||||
do {
|
||||
echo $hintdatabase;
|
||||
|
||||
// Ask for db host.
|
||||
if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('databasehost', 'install'));
|
||||
if ($options['dbhost'] !== '') {
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbhost']);
|
||||
} else {
|
||||
$prompt = get_string('clitypevalue', 'admin');
|
||||
}
|
||||
$CFG->dbhost = cli_input($prompt, $options['dbhost']);
|
||||
|
||||
} else {
|
||||
$prompt = get_string('clitypevalue', 'admin');
|
||||
$CFG->dbhost = $options['dbhost'];
|
||||
}
|
||||
$CFG->dbhost = cli_input($prompt, $options['dbhost']);
|
||||
|
||||
} else {
|
||||
$CFG->dbhost = $options['dbhost'];
|
||||
}
|
||||
// Ask for db name.
|
||||
if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('databasename', 'install'));
|
||||
if ($options['dbname'] !== '') {
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbname']);
|
||||
} else {
|
||||
$prompt = get_string('clitypevalue', 'admin');
|
||||
}
|
||||
$CFG->dbname = cli_input($prompt, $options['dbname']);
|
||||
|
||||
// ask for db name
|
||||
if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('databasename', 'install'));
|
||||
if ($options['dbname'] !== '') {
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbname']);
|
||||
} else {
|
||||
$prompt = get_string('clitypevalue', 'admin');
|
||||
$CFG->dbname = $options['dbname'];
|
||||
}
|
||||
$CFG->dbname = cli_input($prompt, $options['dbname']);
|
||||
|
||||
} else {
|
||||
$CFG->dbname = $options['dbname'];
|
||||
}
|
||||
// Ask for db prefix.
|
||||
if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('dbprefix', 'install'));
|
||||
//TODO: solve somehow the prefix trouble for oci.
|
||||
if ($options['prefix'] !== '') {
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['prefix']);
|
||||
} else {
|
||||
$prompt = get_string('clitypevalue', 'admin');
|
||||
}
|
||||
$CFG->prefix = cli_input($prompt, $options['prefix']);
|
||||
|
||||
// ask for db prefix
|
||||
if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('dbprefix', 'install'));
|
||||
//TODO: solve somehow the prefix trouble for oci
|
||||
if ($options['prefix'] !== '') {
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['prefix']);
|
||||
} else {
|
||||
$prompt = get_string('clitypevalue', 'admin');
|
||||
$CFG->prefix = $options['prefix'];
|
||||
}
|
||||
$CFG->prefix = cli_input($prompt, $options['prefix']);
|
||||
|
||||
} else {
|
||||
$CFG->prefix = $options['prefix'];
|
||||
}
|
||||
// Ask for db port.
|
||||
if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('databaseport', 'install'));
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbport']);
|
||||
$CFG->dboptions['dbport'] = (int) cli_input($prompt, $options['dbport']);
|
||||
|
||||
// ask for db port
|
||||
if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('databaseport', 'install'));
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbport']);
|
||||
$CFG->dboptions['dbport'] = (int)cli_input($prompt, $options['dbport']);
|
||||
|
||||
} else {
|
||||
$CFG->dboptions['dbport'] = (int)$options['dbport'];
|
||||
}
|
||||
if ($CFG->dboptions['dbport'] <= 0) {
|
||||
$CFG->dboptions['dbport'] = '';
|
||||
}
|
||||
|
||||
// ask for db socket
|
||||
if ($CFG->ostype === 'WINDOWS') {
|
||||
$CFG->dboptions['dbsocket'] = '';
|
||||
|
||||
} else if ($interactive and empty($CFG->dboptions['dbport'])) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('databasesocket', 'install'));
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbsocket']);
|
||||
$CFG->dboptions['dbsocket'] = cli_input($prompt, $options['dbsocket']);
|
||||
|
||||
} else {
|
||||
$CFG->dboptions['dbsocket'] = $options['dbsocket'];
|
||||
}
|
||||
|
||||
// ask for db user
|
||||
if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('databaseuser', 'install'));
|
||||
if ($options['dbuser'] !== '') {
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbuser']);
|
||||
} else {
|
||||
$prompt = get_string('clitypevalue', 'admin');
|
||||
$CFG->dboptions['dbport'] = (int) $options['dbport'];
|
||||
}
|
||||
if ($CFG->dboptions['dbport'] <= 0) {
|
||||
$CFG->dboptions['dbport'] = '';
|
||||
}
|
||||
$CFG->dbuser = cli_input($prompt, $options['dbuser']);
|
||||
|
||||
} else {
|
||||
$CFG->dbuser = $options['dbuser'];
|
||||
}
|
||||
// Ask for db socket.
|
||||
if ($CFG->ostype === 'WINDOWS') {
|
||||
$CFG->dboptions['dbsocket'] = '';
|
||||
|
||||
} else if ($interactive and empty($CFG->dboptions['dbport'])) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('databasesocket', 'install'));
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbsocket']);
|
||||
$CFG->dboptions['dbsocket'] = cli_input($prompt, $options['dbsocket']);
|
||||
|
||||
} else {
|
||||
$CFG->dboptions['dbsocket'] = $options['dbsocket'];
|
||||
}
|
||||
|
||||
// Ask for db user.
|
||||
if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('databaseuser', 'install'));
|
||||
if ($options['dbuser'] !== '') {
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbuser']);
|
||||
} else {
|
||||
$prompt = get_string('clitypevalue', 'admin');
|
||||
}
|
||||
$CFG->dbuser = cli_input($prompt, $options['dbuser']);
|
||||
|
||||
} else {
|
||||
$CFG->dbuser = $options['dbuser'];
|
||||
}
|
||||
|
||||
// Ask for db password.
|
||||
if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('databasepass', 'install'));
|
||||
|
||||
// ask for db password
|
||||
if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('databasepass', 'install'));
|
||||
do {
|
||||
if ($options['dbpass'] !== '') {
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbpass']);
|
||||
} else {
|
||||
@@ -615,19 +620,23 @@ if ($interactive) {
|
||||
}
|
||||
|
||||
$CFG->dbpass = cli_input($prompt, $options['dbpass']);
|
||||
if (function_exists('distro_pre_create_db')) { // Hook for distros needing to do something before DB creation
|
||||
$distro = distro_pre_create_db($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbport'=>$CFG->dboptions['dbport'], 'dbsocket'=>$CFG->dboptions['dbsocket']), $distro);
|
||||
if (function_exists('distro_pre_create_db')) { // Hook for distros needing to do something before DB creation.
|
||||
$distro = distro_pre_create_db($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix,
|
||||
array('dbpersist' => 0, 'dbport' => $CFG->dboptions['dbport'], 'dbsocket' => $CFG->dboptions['dbsocket']),
|
||||
$distro);
|
||||
}
|
||||
$hint_database = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbport'=>$CFG->dboptions['dbport'], 'dbsocket'=>$CFG->dboptions['dbsocket']));
|
||||
} while ($hint_database !== '');
|
||||
$hintdatabase = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix,
|
||||
array('dbpersist' => 0, 'dbport' => $CFG->dboptions['dbport'], 'dbsocket' => $CFG->dboptions['dbsocket']));
|
||||
|
||||
} else {
|
||||
$CFG->dbpass = $options['dbpass'];
|
||||
$hint_database = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbport'=>$CFG->dboptions['dbport'], 'dbsocket'=>$CFG->dboptions['dbsocket']));
|
||||
if ($hint_database !== '') {
|
||||
cli_error(get_string('dbconnectionerror', 'install'));
|
||||
} else {
|
||||
$CFG->dbpass = $options['dbpass'];
|
||||
$hintdatabase = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix,
|
||||
array('dbpersist' => 0, 'dbport' => $CFG->dboptions['dbport'], 'dbsocket' => $CFG->dboptions['dbsocket']));
|
||||
if ($hintdatabase !== '') {
|
||||
cli_error(get_string('dbconnectionerror', 'install'));
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ($hintdatabase !== '');
|
||||
|
||||
// ask for fullname
|
||||
if ($interactive) {
|
||||
|
||||
@@ -2451,6 +2451,7 @@
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.0.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_74" message="unsupportedphpversion74" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
@@ -2632,6 +2633,7 @@
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.1.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_74" message="unsupportedphpversion74" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
@@ -2980,4 +2982,185 @@
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.9" requires="3.5">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="10.2.29" />
|
||||
<VENDOR name="mysql" version="5.6" />
|
||||
<VENDOR name="postgres" version="9.5" />
|
||||
<VENDOR name="mssql" version="11.0" />
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.2.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="mbstringrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
@@ -46,7 +46,7 @@ $error = optional_param('error', '', PARAM_ALPHANUM);
|
||||
admin_externalpage_setup('registrationmoodleorg');
|
||||
|
||||
if ($url !== HUB_MOODLEORGHUBURL) {
|
||||
// Allow other plugins to confirm registration on hubs other than moodle.net . Plugins implementing this
|
||||
// Allow other plugins to confirm registration on custom hubs. Plugins implementing this
|
||||
// callback need to redirect or exit. See https://docs.moodle.org/en/Hub_registration .
|
||||
$callbacks = get_plugins_with_function('hub_registration');
|
||||
foreach ($callbacks as $plugintype => $plugins) {
|
||||
|
||||
@@ -32,5 +32,4 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
debugging('Support for alternative hubs has been removed from Moodle in 3.4. For communication with moodle.net ' .
|
||||
'see lib/classes/hub/ .', DEBUG_DEVELOPER);
|
||||
debugging('Support for alternative hubs has been removed from Moodle in 3.4.', DEBUG_DEVELOPER);
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
|
||||
* @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This page displays the site registration form for Moodle.net.
|
||||
* This page displays the site registration form.
|
||||
* It handles redirection to the hub to continue the registration workflow process.
|
||||
* It also handles update operation by web service.
|
||||
*/
|
||||
@@ -48,7 +48,7 @@ if ($unregistration && \core\hub\registration::is_registered()) {
|
||||
}
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading(get_string('unregisterfrom', 'hub', 'Moodle.net'), 3, 'main');
|
||||
echo $OUTPUT->heading(get_string('registerwithmoodleorgremove', 'core_hub'), 3, 'main');
|
||||
$siteunregistrationform->display();
|
||||
echo $OUTPUT->footer();
|
||||
exit;
|
||||
@@ -82,7 +82,7 @@ if ($fromform = $siteregistrationform->get_data()) {
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
// Current status of registration on Moodle.net.
|
||||
// Current status of registration.
|
||||
|
||||
$notificationtype = \core\output\notification::NOTIFY_ERROR;
|
||||
if (\core\hub\registration::is_registered()) {
|
||||
@@ -104,11 +104,11 @@ if (\core\hub\registration::is_registered()) {
|
||||
|
||||
// Heading.
|
||||
if (\core\hub\registration::is_registered()) {
|
||||
echo $OUTPUT->heading(get_string('updatesite', 'hub', 'Moodle.net'));
|
||||
echo $OUTPUT->heading(get_string('registerwithmoodleorgupdate', 'core_hub'));
|
||||
} else if ($isinitialregistration) {
|
||||
echo $OUTPUT->heading(get_string('completeregistration', 'hub'));
|
||||
echo $OUTPUT->heading(get_string('registerwithmoodleorgcomplete', 'core_hub'));
|
||||
} else {
|
||||
echo $OUTPUT->heading(get_string('registerwithmoodleorg', 'admin'));
|
||||
echo $OUTPUT->heading(get_string('registerwithmoodleorg', 'core_hub'));
|
||||
}
|
||||
|
||||
$renderer = $PAGE->get_renderer('core', 'admin');
|
||||
|
||||
@@ -40,7 +40,7 @@ $token = optional_param('token', '', PARAM_TEXT);
|
||||
admin_externalpage_setup('registrationmoodleorg');
|
||||
|
||||
if ($url !== HUB_MOODLEORGHUBURL) {
|
||||
// Allow other plugins to renew registration on hubs other than moodle.net . Plugins implementing this
|
||||
// Allow other plugins to renew registration on custom hubs. Plugins implementing this
|
||||
// callback need to redirect or exit. See https://docs.moodle.org/en/Hub_registration .
|
||||
$callbacks = get_plugins_with_function('hub_registration');
|
||||
foreach ($callbacks as $plugintype => $plugins) {
|
||||
@@ -56,7 +56,7 @@ if ($url !== HUB_MOODLEORGHUBURL) {
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading(get_string('renewregistration', 'hub'), 3, 'main');
|
||||
$hublink = html_writer::tag('a', 'Moodle.net', array('href' => HUB_MOODLEORGHUBURL));
|
||||
$hublink = html_writer::tag('a', HUB_MOODLEORGHUBURL, array('href' => HUB_MOODLEORGHUBURL));
|
||||
|
||||
$deletedregmsg = get_string('previousregistrationdeleted', 'hub', $hublink);
|
||||
|
||||
|
||||
+27
-4
@@ -1449,9 +1449,11 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
$class = 'requires-failed';
|
||||
$label = html_writer::span(get_string('dependencyfails', 'core_plugin'), 'label label-important');
|
||||
}
|
||||
$requires[] = html_writer::tag('li',
|
||||
html_writer::span(get_string('moodleversion', 'core_plugin', $plugin->versionrequires), 'dep dep-core').
|
||||
' '.$label, array('class' => $class));
|
||||
if ($reqinfo->reqver != ANY_VERSION) {
|
||||
$requires[] = html_writer::tag('li',
|
||||
html_writer::span(get_string('moodleversion', 'core_plugin', $plugin->versionrequires), 'dep dep-core').
|
||||
' '.$label, array('class' => $class));
|
||||
}
|
||||
|
||||
} else {
|
||||
$actions = array();
|
||||
@@ -2090,6 +2092,27 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
* @return string
|
||||
*/
|
||||
public function moodleorg_registration_message() {
|
||||
return format_text(get_string('registermoodlenet', 'admin'), FORMAT_HTML, ['noclean' => true]);
|
||||
|
||||
$out = format_text(get_string('registerwithmoodleorginfo', 'core_hub'), FORMAT_MARKDOWN);
|
||||
|
||||
$out .= html_writer::link(
|
||||
new moodle_url('/admin/settings.php', ['section' => 'moodleservices']),
|
||||
$this->output->pix_icon('i/info', '').' '.get_string('registerwithmoodleorginfoapp', 'core_hub'),
|
||||
['class' => 'btn btn-link', 'role' => 'opener', 'target' => '_href']
|
||||
);
|
||||
|
||||
$out .= html_writer::link(
|
||||
HUB_MOODLEORGHUBURL,
|
||||
$this->output->pix_icon('i/stats', '').' '.get_string('registerwithmoodleorginfostats', 'core_hub'),
|
||||
['class' => 'btn btn-link', 'role' => 'opener', 'target' => '_href']
|
||||
);
|
||||
|
||||
$out .= html_writer::link(
|
||||
HUB_MOODLEORGHUBURL.'/sites',
|
||||
$this->output->pix_icon('i/location', '').' '.get_string('registerwithmoodleorginfosites', 'core_hub'),
|
||||
['class' => 'btn btn-link', 'role' => 'opener', 'target' => '_href']
|
||||
);
|
||||
|
||||
return $this->output->box($out);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,10 @@ $options = array(
|
||||
GETREMOTEADDR_SKIP_HTTP_CLIENT_IP => 'HTTP_X_FORWARDED_FOR, REMOTE_ADDR',
|
||||
GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR => 'HTTP_CLIENT, REMOTE_ADDR',
|
||||
GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR|GETREMOTEADDR_SKIP_HTTP_CLIENT_IP => 'REMOTE_ADDR');
|
||||
$temp->add(new admin_setting_configselect('getremoteaddrconf', new lang_string('getremoteaddrconf', 'admin'), new lang_string('configgetremoteaddrconf', 'admin'), 0, $options));
|
||||
$temp->add(new admin_setting_configselect('getremoteaddrconf', new lang_string('getremoteaddrconf', 'admin'),
|
||||
new lang_string('configgetremoteaddrconf', 'admin'),
|
||||
GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR|GETREMOTEADDR_SKIP_HTTP_CLIENT_IP, $options));
|
||||
$temp->add(new admin_setting_configtext('reverseproxyignore', new lang_string('reverseproxyignore', 'admin'), new lang_string('configreverseproxyignore', 'admin'), ''));
|
||||
|
||||
$temp->add(new admin_setting_heading('webproxy', new lang_string('webproxy', 'admin'), new lang_string('webproxyinfo', 'admin')));
|
||||
$temp->add(new admin_setting_configtext('proxyhost', new lang_string('proxyhost', 'admin'), new lang_string('configproxyhost', 'admin'), '', PARAM_HOST));
|
||||
|
||||
@@ -48,7 +48,6 @@ class behat_admin extends behat_base {
|
||||
* @param TableNode $table
|
||||
*/
|
||||
public function i_set_the_following_administration_settings_values(TableNode $table) {
|
||||
|
||||
if (!$data = $table->getRowsHash()) {
|
||||
return;
|
||||
}
|
||||
@@ -57,17 +56,12 @@ class behat_admin extends behat_base {
|
||||
|
||||
// We expect admin block to be visible, otherwise go to homepage.
|
||||
if (!$this->getSession()->getPage()->find('css', '.block_settings')) {
|
||||
$this->getSession()->visit($this->locate_path('/'));
|
||||
$this->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
|
||||
$this->execute('behat_forms::i_am_on_homepage');
|
||||
}
|
||||
|
||||
// Search by label.
|
||||
$searchbox = $this->find_field(get_string('searchinsettings', 'admin'));
|
||||
$searchbox->setValue($label);
|
||||
$submitsearch = $this->find('css', 'form.adminsearchform input[type=submit]');
|
||||
$submitsearch->press();
|
||||
|
||||
$this->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
|
||||
$this->execute('behat_forms::i_set_the_field_to', [get_string('searchinsettings', 'admin'), $label]);
|
||||
$this->execute("behat_general::i_click_on_in_the", [get_string('search', 'admin'), 'button', '.block_settings', 'css_element']);
|
||||
|
||||
// Admin settings does not use the same DOM structure than other moodle forms
|
||||
// but we also need to use lib/behat/form_field/* to deal with the different moodle form elements.
|
||||
@@ -83,35 +77,17 @@ class behat_admin extends behat_base {
|
||||
"@id=//span[contains(normalize-space(.), $label)]/preceding-sibling::label[1]/@for]";
|
||||
$fieldnode = $this->find('xpath', $fieldxpath, $exception);
|
||||
|
||||
$formfieldtypenode = $this->find('xpath', $fieldxpath . "/ancestor::div[@class='form-setting']" .
|
||||
"/child::div[contains(concat(' ', @class, ' '), ' form-')]/child::*/parent::div");
|
||||
|
||||
} catch (ElementNotFoundException $e) {
|
||||
|
||||
// Multi element settings, interacting only the first one.
|
||||
$fieldxpath = "//*[label[normalize-space(.)= $label]|span[normalize-space(.)= $label]]/" .
|
||||
"ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' form-item ')]" .
|
||||
"/descendant::div[@class='form-group']/descendant::*[self::input | self::textarea | self::select]" .
|
||||
"[not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]";
|
||||
$fieldnode = $this->find('xpath', $fieldxpath);
|
||||
|
||||
// It is the same one that contains the type.
|
||||
$formfieldtypenode = $fieldnode;
|
||||
}
|
||||
|
||||
// Getting the class which contains the field type.
|
||||
$classes = explode(' ', $formfieldtypenode->getAttribute('class'));
|
||||
foreach ($classes as $class) {
|
||||
if (substr($class, 0, 5) == 'form-') {
|
||||
$type = substr($class, 5);
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiating the appropiate field type.
|
||||
$field = behat_field_manager::get_field_instance($type, $fieldnode, $this->getSession());
|
||||
$field->set_value($value);
|
||||
|
||||
$this->find_button(get_string('savechanges'))->press();
|
||||
$this->execute('behat_forms::i_set_the_field_with_xpath_to', [$fieldxpath, $value]);
|
||||
$this->execute("behat_general::i_click_on", [get_string('savechanges'), 'button']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
@tool_behat
|
||||
Feature: Behat steps for interacting with form work
|
||||
In order to test my Moodle code
|
||||
As a developer
|
||||
I need the Behat steps for form elements to work reliably
|
||||
|
||||
@javascript
|
||||
Scenario: Test fields in containers
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
When I log in as "admin"
|
||||
And I am on "Course 1" course homepage
|
||||
# Just get to any form.
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I set the field "Course full name" in the "General" "fieldset" to "Frog"
|
||||
And I set the following fields in the "Appearance" "fieldset" to these values:
|
||||
| Show activity reports | Yes |
|
||||
| Number of announcements | 1 |
|
||||
Then the field "Show activity reports" in the "Appearance" "fieldset" matches value "Yes"
|
||||
And the field "Show activity reports" in the "Appearance" "fieldset" does not match value "No"
|
||||
And the following fields in the "region-main" "region" match these values:
|
||||
| Course full name | Frog |
|
||||
| Number of announcements | 1 |
|
||||
And the following fields in the "region-main" "region" do not match these values:
|
||||
| Course full name | Course 1 |
|
||||
| Number of announcements | 5 |
|
||||
@@ -222,6 +222,48 @@ class api {
|
||||
}
|
||||
}
|
||||
|
||||
// Clean the legacy role assignments which are stale.
|
||||
$paramsclean['usercontext'] = CONTEXT_USER;
|
||||
$paramsclean['component'] = 'tool_cohortroles';
|
||||
$sql = 'SELECT DISTINCT(ra.id), ra.roleid, ra.userid, ra.contextid, ctx.instanceid
|
||||
FROM {role_assignments} ra
|
||||
JOIN {context} ctx ON ctx.id = ra.contextid AND ctx.contextlevel = :usercontext
|
||||
JOIN {cohort_members} cm ON cm.userid = ctx.instanceid
|
||||
LEFT JOIN {tool_cohortroles} tc ON tc.cohortid = cm.cohortid
|
||||
AND tc.userid = ra.userid
|
||||
AND tc.roleid = ra.roleid
|
||||
WHERE ra.component = :component
|
||||
AND tc.id is null';
|
||||
if ($candidatelegacyassignments = $DB->get_records_sql($sql, $paramsclean)) {
|
||||
$sql = 'SELECT DISTINCT(ra.id)
|
||||
FROM {role_assignments} ra
|
||||
JOIN {context} ctx ON ctx.id = ra.contextid AND ctx.contextlevel = :usercontext
|
||||
JOIN {cohort_members} cm ON cm.userid = ctx.instanceid
|
||||
JOIN {tool_cohortroles} tc ON tc.cohortid = cm.cohortid AND tc.userid = ra.userid
|
||||
WHERE ra.component = :component';
|
||||
if ($currentvalidroleassignments = $DB->get_records_sql($sql, $paramsclean)) {
|
||||
foreach ($candidatelegacyassignments as $candidate) {
|
||||
if (!array_key_exists($candidate->id, $currentvalidroleassignments)) {
|
||||
role_unassign($candidate->roleid, $candidate->userid, $candidate->contextid, 'tool_cohortroles');
|
||||
$rolesremoved[] = array(
|
||||
'useridassignedto' => $candidate->userid,
|
||||
'useridassignedover' => $candidate->instanceid,
|
||||
'roleid' => $candidate->roleid
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($candidatelegacyassignments as $candidate) {
|
||||
role_unassign($candidate->roleid, $candidate->userid, $candidate->contextid, 'tool_cohortroles');
|
||||
$rolesremoved[] = array(
|
||||
'useridassignedto' => $candidate->userid,
|
||||
'useridassignedover' => $candidate->instanceid,
|
||||
'roleid' => $candidate->roleid
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array('rolesadded' => $rolesadded, 'rolesremoved' => $rolesremoved);
|
||||
}
|
||||
|
||||
|
||||
@@ -103,5 +103,4 @@ class cohort_role_assignment extends persistent {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,8 +50,6 @@ class tool_cohortroles_api_testcase extends advanced_testcase {
|
||||
* Setup function- we will create a course and add an assign instance to it.
|
||||
*/
|
||||
protected function setUp() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Create some users.
|
||||
@@ -133,14 +131,91 @@ class tool_cohortroles_api_testcase extends advanced_testcase {
|
||||
|
||||
public function test_delete_cohort_role_assignment() {
|
||||
$this->setAdminUser();
|
||||
$params = (object) array(
|
||||
// Create a cohort role assigment.
|
||||
$params = (object) [
|
||||
'userid' => $this->userassignto->id,
|
||||
'roleid' => $this->roleid,
|
||||
'cohortid' => $this->cohort->id
|
||||
);
|
||||
$result = api::create_cohort_role_assignment($params);
|
||||
$worked = api::delete_cohort_role_assignment($result->get('id'));
|
||||
$this->assertTrue($worked);
|
||||
];
|
||||
$cohortroleassignment = api::create_cohort_role_assignment($params);
|
||||
$sync = api::sync_all_cohort_roles();
|
||||
$rolesadded = [
|
||||
[
|
||||
'useridassignedto' => $this->userassignto->id,
|
||||
'useridassignedover' => $this->userassignover->id,
|
||||
'roleid' => $this->roleid
|
||||
]
|
||||
];
|
||||
$expected = [
|
||||
'rolesadded' => $rolesadded,
|
||||
'rolesremoved' => []
|
||||
];
|
||||
$this->assertEquals($sync, $expected);
|
||||
|
||||
// Delete the cohort role assigment and confirm the roles are removed.
|
||||
$result = api::delete_cohort_role_assignment($cohortroleassignment->get('id'));
|
||||
$this->assertTrue($result);
|
||||
$sync = api::sync_all_cohort_roles();
|
||||
$expected = [
|
||||
'rolesadded' => [],
|
||||
'rolesremoved' => $rolesadded
|
||||
];
|
||||
$this->assertEquals($expected, $sync);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test case verifying that syncing won't remove role assignments if they are valid for another cohort role assignment.
|
||||
*/
|
||||
public function test_delete_cohort_role_assignment_cohorts_having_same_members() {
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create 2 cohorts, with a 1 user (user1) present in both,
|
||||
// and user2 and user3 members of 1 cohort each.
|
||||
$cohort1 = $this->getDataGenerator()->create_cohort();
|
||||
$cohort2 = $this->getDataGenerator()->create_cohort();
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$user3 = $this->getDataGenerator()->create_user();
|
||||
cohort_add_member($cohort1->id, $user1->id);
|
||||
cohort_add_member($cohort1->id, $user2->id);
|
||||
cohort_add_member($cohort2->id, $user1->id);
|
||||
cohort_add_member($cohort2->id, $user3->id);
|
||||
|
||||
// And a role and a user to assign that role to.
|
||||
$user4 = $this->getDataGenerator()->create_user(); // A cohort manager, for example.
|
||||
$roleid = create_role('Role 1', 'myrole', 'test');
|
||||
|
||||
// Assign the role for user4 in both cohorts.
|
||||
$params = (object) [
|
||||
'userid' => $user4->id,
|
||||
'roleid' => $roleid,
|
||||
'cohortid' => $cohort1->id
|
||||
];
|
||||
$cohort1roleassignment = api::create_cohort_role_assignment($params);
|
||||
$params->cohortid = $cohort2->id;
|
||||
$cohort2roleassignment = api::create_cohort_role_assignment($params);
|
||||
|
||||
$sync = api::sync_all_cohort_roles();
|
||||
|
||||
// There is no guarantee about the order of roles assigned.
|
||||
// so confirm we have 3 role assignments, and they are for the users 1, 2 and 3.
|
||||
$this->assertCount(3, $sync['rolesadded']);
|
||||
$addedusers = array_column($sync['rolesadded'], 'useridassignedover');
|
||||
$this->assertContains($user1->id, $addedusers);
|
||||
$this->assertContains($user2->id, $addedusers);
|
||||
$this->assertContains($user3->id, $addedusers);
|
||||
|
||||
// Remove the role assignment for user4/cohort1.
|
||||
// Verify only 1 role is unassigned as the others are still valid for the other cohort role assignment.
|
||||
$result = api::delete_cohort_role_assignment($cohort1roleassignment->get('id'));
|
||||
$this->assertTrue($result);
|
||||
|
||||
$sync = api::sync_all_cohort_roles();
|
||||
|
||||
$this->assertCount(0, $sync['rolesadded']);
|
||||
$this->assertCount(1, $sync['rolesremoved']);
|
||||
$removedusers = array_column($sync['rolesremoved'], 'useridassignedover');
|
||||
$this->assertContains($user2->id, $removedusers);
|
||||
}
|
||||
|
||||
public function test_list_cohort_role_assignments() {
|
||||
|
||||
@@ -1 +1 @@
|
||||
define(["jquery","core/ajax","core/notification","core/str","core/modal_factory","core/modal_events","core/templates"],function(a,b,c,d,e,f,g){function h(a){var e={methodname:"tool_dataprivacy_contact_dpo",args:{message:a}},f="success";b.call([e])[0].then(function(a){return a.result?d.get_string("requestsubmitted","tool_dataprivacy"):(f="error",a.warnings.join("<br>"))}).done(function(a){c.addNotification({message:a,type:f})}).fail(c.exception)}var i={CANCEL_REQUEST:'[data-action="cancel"]',CONTACT_DPO:'[data-action="contactdpo"]'},j=function(){this.registerEvents()};return j.prototype.registerEvents=function(){a(i.CANCEL_REQUEST).click(function(g){g.preventDefault();var h=a(this).data("requestid"),i=[{key:"cancelrequest",component:"tool_dataprivacy"},{key:"cancelrequestconfirmation",component:"tool_dataprivacy"}];d.get_strings(i).then(function(a){var d=a[0],g=a[1];return e.create({title:d,body:g,type:e.types.SAVE_CANCEL}).then(function(a){return a.setSaveButtonText(d),a.getRoot().on(f.save,function(){var a={requestid:h},d={methodname:"tool_dataprivacy_cancel_data_request",args:a};b.call([d])[0].done(function(a){a.result?window.location.reload():c.addNotification({message:a.warnings[0].message,type:"error"})}).fail(c.exception)}),a.getRoot().on(f.hidden,function(){a.destroy()}),a})}).done(function(a){a.show()}).fail(c.exception)}),a(i.CONTACT_DPO).click(function(b){b.preventDefault();var i=a(this).data("replytoemail"),j=[{key:"contactdataprotectionofficer",component:"tool_dataprivacy"},{key:"send",component:"tool_dataprivacy"}],k="";d.get_strings(j).then(function(a){var b=a[0];k=a[1];var c={replytoemail:i};return e.create({title:b,body:g.render("tool_dataprivacy/contact_dpo",c),type:e.types.SAVE_CANCEL,large:!0})}).done(function(b){b.setSaveButtonText(k),b.getRoot().on(f.save,function(b){var c=a("#message").val().trim();0===c.length?(b.preventDefault(),a('[data-region="messageinput"]').addClass("has-danger notifyproblem"),a("#id_error_message").removeAttr("hidden")):h(c)}),b.getRoot().on(f.hidden,function(){b.destroy()}),b.show()}).fail(c.exception)})},{init:function(){return new j}}});
|
||||
define(["jquery","core/ajax","core/notification","core/str","core/modal_factory","core/modal_events","core/templates","core/pending"],function(a,b,c,d,e,f,g,h){function i(a){var e={methodname:"tool_dataprivacy_contact_dpo",args:{message:a}},f="success";b.call([e])[0].then(function(a){return a.result?d.get_string("requestsubmitted","tool_dataprivacy"):(f="error",a.warnings.join("<br>"))}).done(function(a){c.addNotification({message:a,type:f})}).fail(c.exception)}var j={CANCEL_REQUEST:'[data-action="cancel"]',CONTACT_DPO:'[data-action="contactdpo"]'},k=function(){this.registerEvents()};return k.prototype.registerEvents=function(){a(j.CANCEL_REQUEST).click(function(g){g.preventDefault();var h=a(this).data("requestid"),i=[{key:"cancelrequest",component:"tool_dataprivacy"},{key:"cancelrequestconfirmation",component:"tool_dataprivacy"}];d.get_strings(i).then(function(a){var d=a[0],g=a[1];return e.create({title:d,body:g,type:e.types.SAVE_CANCEL}).then(function(a){return a.setSaveButtonText(d),a.getRoot().on(f.save,function(){var a={requestid:h},d={methodname:"tool_dataprivacy_cancel_data_request",args:a};b.call([d])[0].done(function(a){a.result?window.location.reload():c.addNotification({message:a.warnings[0].message,type:"error"})}).fail(c.exception)}),a.getRoot().on(f.hidden,function(){a.destroy()}),a})}).done(function(a){a.show()}).fail(c.exception)}),a(j.CONTACT_DPO).click(function(b){var j=new h("dataprivacy/crud:initModal:contactdpo");b.preventDefault();var k=a(this).data("replytoemail"),l=[{key:"contactdataprotectionofficer",component:"tool_dataprivacy"},{key:"send",component:"tool_dataprivacy"}],m="";d.get_strings(l).then(function(a){var b=a[0];m=a[1];var c={replytoemail:k};return e.create({title:b,body:g.render("tool_dataprivacy/contact_dpo",c),type:e.types.SAVE_CANCEL,large:!0})}).then(function(b){b.setSaveButtonText(m),b.show(),b.getRoot().on(f.save,function(b){var c=a("#message").val().trim();0===c.length?(b.preventDefault(),a('[data-region="messageinput"]').addClass("has-danger notifyproblem"),a("#id_error_message").removeAttr("hidden")):i(c)}),b.getRoot().on(f.hidden,function(){b.destroy()})}).then(j.resolve)["catch"](c.exception)})},{init:function(){return new k}}});
|
||||
@@ -28,8 +28,9 @@ define([
|
||||
'core/str',
|
||||
'core/modal_factory',
|
||||
'core/modal_events',
|
||||
'core/templates'],
|
||||
function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates) {
|
||||
'core/templates',
|
||||
'core/pending'],
|
||||
function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, Pending) {
|
||||
|
||||
/**
|
||||
* List of action selectors.
|
||||
@@ -118,6 +119,7 @@ function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates) {
|
||||
});
|
||||
|
||||
$(ACTIONS.CONTACT_DPO).click(function(e) {
|
||||
var pendingPromise = new Pending('dataprivacy/crud:initModal:contactdpo');
|
||||
e.preventDefault();
|
||||
|
||||
var replyToEmail = $(this).data('replytoemail');
|
||||
@@ -146,9 +148,12 @@ function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates) {
|
||||
type: ModalFactory.types.SAVE_CANCEL,
|
||||
large: true
|
||||
});
|
||||
}).done(function(modal) {
|
||||
}).then(function(modal) {
|
||||
modal.setSaveButtonText(sendButtonText);
|
||||
|
||||
// Show the modal!
|
||||
modal.show();
|
||||
|
||||
// Handle send event.
|
||||
modal.getRoot().on(ModalEvents.save, function(e) {
|
||||
var message = $('#message').val().trim();
|
||||
@@ -169,9 +174,9 @@ function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates) {
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
// Show the modal!
|
||||
modal.show();
|
||||
}).fail(Notification.exception);
|
||||
return;
|
||||
}).then(pendingPromise.resolve)
|
||||
.catch(Notification.exception);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -127,13 +127,17 @@ class process_data_request_task extends adhoc_task {
|
||||
$thing = $fs->create_file_from_pathname($filerecord, $exportedcontent);
|
||||
$completestatus = api::DATAREQUEST_STATUS_DOWNLOAD_READY;
|
||||
} else if ($request->type == api::DATAREQUEST_TYPE_DELETE) {
|
||||
// Delete the data.
|
||||
$manager = new \core_privacy\manager();
|
||||
$manager->set_observer(new \tool_dataprivacy\manager_observer());
|
||||
// Delete the data for users other than the primary admin, which is rejected.
|
||||
if (is_primary_admin($foruser->id)) {
|
||||
$completestatus = api::DATAREQUEST_STATUS_REJECTED;
|
||||
} else {
|
||||
$manager = new \core_privacy\manager();
|
||||
$manager->set_observer(new \tool_dataprivacy\manager_observer());
|
||||
|
||||
$manager->delete_data_for_user($approvedclcollection);
|
||||
$completestatus = api::DATAREQUEST_STATUS_DELETED;
|
||||
$deleteuser = !$foruser->deleted;
|
||||
$manager->delete_data_for_user($approvedclcollection);
|
||||
$completestatus = api::DATAREQUEST_STATUS_DELETED;
|
||||
$deleteuser = !$foruser->deleted;
|
||||
}
|
||||
}
|
||||
|
||||
// When the preparation of the metadata finishes, update the request status to awaiting approval.
|
||||
|
||||
@@ -134,6 +134,10 @@ class tool_dataprivacy_data_request_form extends moodleform {
|
||||
$errors['type'] = get_string('errorrequestalreadyexists', 'tool_dataprivacy');
|
||||
}
|
||||
|
||||
if ($data['type'] == api::DATAREQUEST_TYPE_DELETE && is_primary_admin($data['userid'])) {
|
||||
$errors['type'] = get_string('errorcannotdeleteadmin', 'tool_dataprivacy');
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +124,7 @@ $string['editpurposes'] = 'Edit purposes';
|
||||
$string['effectiveretentionperiodcourse'] = '{$a} (after the course end date)';
|
||||
$string['effectiveretentionperioduser'] = '{$a} (since the last time the user accessed the site)';
|
||||
$string['emailsalutation'] = 'Dear {$a},';
|
||||
$string['errorcannotdeleteadmin'] = 'Cannot create deletion request for the primary site admin';
|
||||
$string['errorinvalidrequestcreationmethod'] = 'Invalid request creation method!';
|
||||
$string['errorinvalidrequeststatus'] = 'Invalid request status!';
|
||||
$string['errorinvalidrequesttype'] = 'Invalid request type!';
|
||||
|
||||
@@ -78,7 +78,7 @@ function tool_dataprivacy_myprofile_navigation(tree $tree, $user, $iscurrentuser
|
||||
// Check if the user has an ongoing data deletion request.
|
||||
$hasdeleterequest = \tool_dataprivacy\api::has_ongoing_request($user->id, \tool_dataprivacy\api::DATAREQUEST_TYPE_DELETE);
|
||||
// Show data deletion link only if the user doesn't have an ongoing data deletion request.
|
||||
if (!$hasdeleterequest) {
|
||||
if (!$hasdeleterequest && !is_primary_admin($user->id)) {
|
||||
$deleteparams = ['type' => \tool_dataprivacy\api::DATAREQUEST_TYPE_DELETE];
|
||||
$deleteurl = new moodle_url('/admin/tool/dataprivacy/createdatarequest.php', $deleteparams);
|
||||
$deletenode = new core_user\output\myprofile\node('privacyandpolicies', 'requestdatadeletion',
|
||||
|
||||
@@ -301,6 +301,34 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
|
||||
$datarequest = api::create_data_request($student->id, api::DATAREQUEST_TYPE_EXPORT);
|
||||
|
||||
$requestid = $datarequest->get('id');
|
||||
|
||||
// Login as a user without DPO role.
|
||||
$this->setUser($teacher);
|
||||
$this->expectException(required_capability_exception::class);
|
||||
api::approve_data_request($requestid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that deletion requests for the primary admin are rejected
|
||||
*/
|
||||
public function test_reject_data_deletion_request_primary_admin() {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$datarequest = api::create_data_request(get_admin()->id, api::DATAREQUEST_TYPE_DELETE);
|
||||
|
||||
// Approve the request and execute the ad-hoc process task.
|
||||
ob_start();
|
||||
api::approve_data_request($datarequest->get('id'));
|
||||
$this->runAdhocTasks('\tool_dataprivacy\task\process_data_request_task');
|
||||
ob_end_clean();
|
||||
|
||||
$request = api::get_request($datarequest->get('id'));
|
||||
$this->assertEquals(api::DATAREQUEST_STATUS_REJECTED, $request->get('status'));
|
||||
|
||||
// Confirm they weren't deleted.
|
||||
$user = core_user::get_user($request->get('userid'));
|
||||
core_user::require_active_user($user);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -88,6 +88,12 @@ Feature: Data delete from the privacy API
|
||||
And I navigate to "Users > Privacy and policies > Data requests" in site administration
|
||||
And I should see "Deleted"
|
||||
|
||||
@javascript
|
||||
Scenario: As a primary admin, the link to create a data deletion request should not be shown.
|
||||
Given I log in as "admin"
|
||||
When I follow "Profile" in the user menu
|
||||
Then I should not see "Delete my account"
|
||||
|
||||
@javascript
|
||||
Scenario: As a parent, request account and data deletion for my child
|
||||
Given I log in as "parent"
|
||||
|
||||
@@ -75,7 +75,7 @@ class manage_competency_frameworks_page implements renderable, templatable {
|
||||
);
|
||||
$this->navigation[] = $addpage;
|
||||
$competenciesrepository = new single_button(
|
||||
new moodle_url('https://moodle.net/competencies'),
|
||||
new moodle_url('https://archive.moodle.net/competencies'),
|
||||
get_string('competencyframeworksrepository', 'tool_lp'),
|
||||
'get'
|
||||
);
|
||||
|
||||
@@ -206,6 +206,12 @@ class api {
|
||||
$identityprovidersdata = \auth_plugin_base::prepare_identity_providers_for_output($identityproviders, $OUTPUT);
|
||||
if (!empty($identityprovidersdata)) {
|
||||
$settings['identityproviders'] = $identityprovidersdata;
|
||||
// Clean URLs to avoid breaking Web Services.
|
||||
// We can't do it in prepare_identity_providers_for_output() because it may break the web output.
|
||||
foreach ($settings['identityproviders'] as &$ip) {
|
||||
$ip['url'] = (!empty($ip['url'])) ? clean_param($ip['url'], PARAM_URL) : '';
|
||||
$ip['iconurl'] = (!empty($ip['iconurl'])) ? clean_param($ip['iconurl'], PARAM_URL) : '';
|
||||
}
|
||||
}
|
||||
|
||||
// If age is verified, return also the admin contact details.
|
||||
|
||||
@@ -89,7 +89,7 @@ $string['pluginnotenabledorconfigured'] = 'Plugin not enabled or configured.';
|
||||
$string['remoteaddons'] = 'Remote add-ons';
|
||||
$string['selfsignedoruntrustedcertificatewarning'] = 'It seems that the HTTPS certificate is self-signed or not trusted. The mobile app will only work with trusted sites.';
|
||||
$string['setuplink'] = 'App download page';
|
||||
$string['setuplink_desc'] = 'URL of page with links to download the mobile app from the App Store and Google Play.';
|
||||
$string['setuplink_desc'] = 'URL of page with options to download the mobile app from the App Store and Google Play. The app download page link is displayed in the page footer and in a user\'s profile. Leave blank to not display a link.';
|
||||
$string['smartappbanners'] = 'App Banners';
|
||||
$string['typeoflogin'] = 'Type of login';
|
||||
$string['typeoflogin_desc'] = 'If the site uses a SSO authentication method, then select via a browser window or via an embedded browser. An embedded browser provides a better user experience, though it doesn\'t work with all SSO plugins.';
|
||||
|
||||
@@ -98,6 +98,7 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
|
||||
);
|
||||
$this->assertEquals($expected, $result);
|
||||
|
||||
$this->setAdminUser();
|
||||
// Change some values.
|
||||
set_config('registerauth', 'email');
|
||||
$authinstructions = 'Something with <b>html tags</b>';
|
||||
@@ -111,6 +112,18 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
|
||||
set_config('lang', 'a_b'); // Set invalid lang.
|
||||
set_config('disabledfeatures', 'myoverview', 'tool_mobile');
|
||||
|
||||
// Enable couple of issuers.
|
||||
$issuer = \core\oauth2\api::create_standard_issuer('google');
|
||||
$irecord = $issuer->to_record();
|
||||
$irecord->clientid = 'mock';
|
||||
$irecord->clientsecret = 'mock';
|
||||
core\oauth2\api::update_issuer($irecord);
|
||||
|
||||
set_config('user_attribute', 'test', 'auth_shibboleth');
|
||||
set_config('auth_logo', 'http://invalidurl.com//invalid/', 'auth_shibboleth');
|
||||
|
||||
set_config('auth', 'oauth2,shibboleth');
|
||||
|
||||
list($authinstructions, $notusedformat) = external_format_text($authinstructions, FORMAT_MOODLE, $context->id);
|
||||
$expected['registerauth'] = 'email';
|
||||
$expected['authinstructions'] = $authinstructions;
|
||||
@@ -132,7 +145,26 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
|
||||
|
||||
$result = external::get_public_config();
|
||||
$result = external_api::clean_returnvalue(external::get_public_config_returns(), $result);
|
||||
// First check providers.
|
||||
$identityproviders = $result['identityproviders'];
|
||||
unset($result['identityproviders']);
|
||||
|
||||
$this->assertEquals('Google', $identityproviders[0]['name']);
|
||||
$this->assertEquals($irecord->image, $identityproviders[0]['iconurl']);
|
||||
$this->assertContains($CFG->wwwroot, $identityproviders[0]['url']);
|
||||
|
||||
$this->assertEquals('Shibboleth Login', $identityproviders[1]['name']);
|
||||
$this->assertEmpty($identityproviders[1]['iconurl']);
|
||||
$this->assertContains($CFG->wwwroot, $identityproviders[1]['url']);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
|
||||
// Change providers img.
|
||||
$newurl = 'validimage.png';
|
||||
set_config('auth_logo', $newurl, 'auth_shibboleth');
|
||||
$result = external::get_public_config();
|
||||
$result = external_api::clean_returnvalue(external::get_public_config_returns(), $result);
|
||||
$this->assertContains($newurl, $result['identityproviders'][1]['iconurl']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
use \tool_monitor\privacy\provider;
|
||||
use \core_privacy\local\request\approved_contextlist;
|
||||
use \core_privacy\local\request\approved_userlist;
|
||||
use \core_privacy\tests\provider_testcase;
|
||||
|
||||
/**
|
||||
* Privacy test for the event monitor
|
||||
@@ -37,7 +38,7 @@ use \core_privacy\local\request\approved_userlist;
|
||||
* @copyright 2018 Adrian Greeve <adriangreeve.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_monitor_privacy_testcase extends advanced_testcase {
|
||||
class tool_monitor_privacy_testcase extends provider_testcase {
|
||||
|
||||
/**
|
||||
* Set up method.
|
||||
|
||||
@@ -46,6 +46,9 @@ class issuer extends persistent {
|
||||
/** @var string $type */
|
||||
protected $type;
|
||||
|
||||
/** @var boolean $showrequireconfirm Whether to show the require confirmation email checkbox or not. */
|
||||
protected $showrequireconfirm;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -71,6 +74,7 @@ class issuer extends persistent {
|
||||
if (array_key_exists('type', $customdata)) {
|
||||
$this->type = $customdata['type'];
|
||||
}
|
||||
$this->showrequireconfirm = !empty($customdata['showrequireconfirm']);
|
||||
parent::__construct($action, $customdata, $method, $target, $attributes, $editable, $ajaxformdata);
|
||||
}
|
||||
|
||||
@@ -157,9 +161,11 @@ class issuer extends persistent {
|
||||
$mform->addElement('checkbox', 'showonloginpage', get_string('issuershowonloginpage', 'tool_oauth2'));
|
||||
$mform->addHelpButton('showonloginpage', 'issuershowonloginpage', 'tool_oauth2');
|
||||
|
||||
// Require confirmation email for new accounts.
|
||||
$mform->addElement('advcheckbox', 'requireconfirmation', get_string('issuerrequireconfirmation', 'tool_oauth2'));
|
||||
$mform->addHelpButton('requireconfirmation', 'issuerrequireconfirmation', 'tool_oauth2');
|
||||
if ($this->showrequireconfirm) {
|
||||
// Require confirmation email for new accounts.
|
||||
$mform->addElement('advcheckbox', 'requireconfirmation', get_string('issuerrequireconfirmation', 'tool_oauth2'));
|
||||
$mform->addHelpButton('requireconfirmation', 'issuerrequireconfirmation', 'tool_oauth2');
|
||||
}
|
||||
|
||||
$mform->addElement('hidden', 'sortorder');
|
||||
$mform->setType('sortorder', PARAM_INT);
|
||||
|
||||
@@ -58,7 +58,24 @@ if ($action == 'edit') {
|
||||
$PAGE->navbar->add(get_string('createnewissuer', 'tool_oauth2'));
|
||||
}
|
||||
|
||||
$mform = new \tool_oauth2\form\issuer(null, ['persistent' => $issuer]);
|
||||
$showrequireconfirm = false;
|
||||
if (!empty($issuerid)) {
|
||||
// Show the "Require confirmation email" checkbox for trusted issuers like Google, Facebook and Microsoft.
|
||||
$likefacebook = $DB->sql_like('url', ':facebook');
|
||||
$likegoogle = $DB->sql_like('url', ':google');
|
||||
$likemicrosoft = $DB->sql_like('url', ':microsoft');
|
||||
$params = [
|
||||
'issuerid' => $issuerid,
|
||||
'facebook' => '%facebook%',
|
||||
'google' => '%google%',
|
||||
'microsoft' => '%microsoft%',
|
||||
];
|
||||
$select = "issuerid = :issuerid AND ($likefacebook OR $likegoogle OR $likemicrosoft)";
|
||||
// We're querying from the oauth2_endpoint table because the base URLs of FB and Microsoft can be empty in the issuer table.
|
||||
$showrequireconfirm = $DB->record_exists_select('oauth2_endpoint', $select, $params);
|
||||
}
|
||||
|
||||
$mform = new \tool_oauth2\form\issuer(null, ['persistent' => $issuer, 'showrequireconfirm' => $showrequireconfirm]);
|
||||
}
|
||||
|
||||
if ($mform && $mform->is_cancelled()) {
|
||||
@@ -108,9 +125,11 @@ if ($mform && $mform->is_cancelled()) {
|
||||
|
||||
$type = required_param('type', PARAM_ALPHA);
|
||||
$docs = required_param('docslink', PARAM_ALPHAEXT);
|
||||
$showrequireconfirm = optional_param('showrequireconfirm', false, PARAM_BOOL);
|
||||
require_sesskey();
|
||||
$issuer = core\oauth2\api::init_standard_issuer($type);
|
||||
$mform = new \tool_oauth2\form\issuer(null, ['persistent' => $issuer, 'type' => $type]);
|
||||
$mform = new \tool_oauth2\form\issuer(null, ['persistent' => $issuer, 'type' => $type,
|
||||
'showrequireconfirm' => $showrequireconfirm]);
|
||||
|
||||
echo $OUTPUT->header();
|
||||
$mform->display();
|
||||
@@ -178,26 +197,37 @@ if ($mform && $mform->is_cancelled()) {
|
||||
$issuers = core\oauth2\api::get_all_issuers();
|
||||
echo $renderer->issuers_table($issuers);
|
||||
|
||||
// Google template.
|
||||
$docs = 'admin/tool/oauth2/issuers/google';
|
||||
$params = ['action' => 'edittemplate', 'type' => 'google', 'sesskey' => sesskey(), 'docslink' => $docs];
|
||||
$params = ['action' => 'edittemplate', 'type' => 'google', 'sesskey' => sesskey(), 'docslink' => $docs,
|
||||
'showrequireconfirm' => true];
|
||||
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', $params);
|
||||
echo $renderer->single_button($addurl, get_string('createnewgoogleissuer', 'tool_oauth2'));
|
||||
|
||||
// Microsoft template.
|
||||
$docs = 'admin/tool/oauth2/issuers/microsoft';
|
||||
$params = ['action' => 'edittemplate', 'type' => 'microsoft', 'sesskey' => sesskey(), 'docslink' => $docs];
|
||||
$params = ['action' => 'edittemplate', 'type' => 'microsoft', 'sesskey' => sesskey(), 'docslink' => $docs,
|
||||
'showrequireconfirm' => true];
|
||||
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', $params);
|
||||
echo $renderer->single_button($addurl, get_string('createnewmicrosoftissuer', 'tool_oauth2'));
|
||||
|
||||
// Facebook template.
|
||||
$docs = 'admin/tool/oauth2/issuers/facebook';
|
||||
$params = ['action' => 'edittemplate', 'type' => 'microsoft', 'sesskey' => sesskey(), 'docslink' => $docs];
|
||||
$params = ['action' => 'edittemplate', 'type' => 'facebook', 'sesskey' => sesskey(), 'docslink' => $docs];
|
||||
$params = ['action' => 'edittemplate', 'type' => 'facebook', 'sesskey' => sesskey(), 'docslink' => $docs,
|
||||
'showrequireconfirm' => true];
|
||||
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', $params);
|
||||
echo $renderer->single_button($addurl, get_string('createnewfacebookissuer', 'tool_oauth2'));
|
||||
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', ['action' => 'edit']);
|
||||
|
||||
// Nextcloud template.
|
||||
$docs = 'admin/tool/oauth2/issuers/nextcloud';
|
||||
$params = ['action' => 'edittemplate', 'type' => 'nextcloud', 'sesskey' => sesskey(), 'docslink' => $docs];
|
||||
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', $params);
|
||||
echo $renderer->single_button($addurl, get_string('createnewnextcloudissuer', 'tool_oauth2'));
|
||||
|
||||
// Generic issuer.
|
||||
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', ['action' => 'edit']);
|
||||
echo $renderer->single_button($addurl, get_string('createnewissuer', 'tool_oauth2'));
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
}
|
||||
|
||||
@@ -131,11 +131,12 @@ if ($execute) {
|
||||
tool_phpunit_problem('Can not create configuration file');
|
||||
}
|
||||
}
|
||||
$configdir = escapeshellarg($configdir);
|
||||
// no cleanup of path - this is tricky because we can not use escapeshellarg and friends for escaping,
|
||||
// this is from admin user so PARAM_PATH must be enough
|
||||
chdir($CFG->dirroot);
|
||||
passthru("php $CFG->admin/tool/phpunit/cli/util.php --run -c $configdir $testclass $testpath", $code);
|
||||
$configdir = escapeshellarg($configdir);
|
||||
$cleanclass = escapeshellarg($testclass);
|
||||
$cleanpath = escapeshellarg($testpath);
|
||||
|
||||
passthru("php $CFG->admin/tool/phpunit/cli/util.php --run -c $configdir $cleanclass $cleanpath", $code);
|
||||
chdir($oldcwd);
|
||||
|
||||
echo '</pre>';
|
||||
@@ -146,7 +147,7 @@ if ($execute) {
|
||||
}
|
||||
|
||||
echo $OUTPUT->box_start('generalbox boxwidthwide boxaligncenter');
|
||||
echo '<form method="get" action="webrunner.php">';
|
||||
echo '<form method="post" action="webrunner.php">';
|
||||
echo '<fieldset class="invisiblefieldset">';
|
||||
echo '<label for="testpath">Test one file</label> ';
|
||||
echo '<input type="text" id="testpath" name="testpath" value="'.s($testpath).'" size="50" /> (all test cases from webrunner.xml if empty)';
|
||||
|
||||
@@ -257,7 +257,7 @@ class manager {
|
||||
'title' => get_string('importtour', 'tool_usertours'),
|
||||
],
|
||||
(object) [
|
||||
'link' => new \moodle_url('https://moodle.net/tours'),
|
||||
'link' => new \moodle_url('https://archive.moodle.net/tours'),
|
||||
'linkproperties' => [
|
||||
'target' => '_blank',
|
||||
],
|
||||
|
||||
@@ -198,7 +198,7 @@ abstract class base extends \core_analytics\calculable {
|
||||
$message->component = 'moodle';
|
||||
$message->name = 'insights';
|
||||
|
||||
$message->userfrom = \core_user::get_noreply_user();
|
||||
$message->userfrom = \core_user::get_support_user();
|
||||
$message->userto = $user;
|
||||
|
||||
$insighturl = new \moodle_url('/report/insights/insights.php?modelid=' . $modelid . '&contextid=' . $context->id);
|
||||
|
||||
@@ -547,45 +547,62 @@ class manager {
|
||||
public static function cleanup() {
|
||||
global $DB;
|
||||
|
||||
// Clean up stuff that depends on contexts that do not exist anymore.
|
||||
$sql = "SELECT DISTINCT ap.contextid FROM {analytics_predictions} ap
|
||||
LEFT JOIN {context} ctx ON ap.contextid = ctx.id
|
||||
WHERE ctx.id IS NULL";
|
||||
$apcontexts = $DB->get_records_sql($sql);
|
||||
$DB->execute("DELETE FROM {analytics_prediction_actions} WHERE predictionid IN
|
||||
(SELECT ap.id FROM {analytics_predictions} ap
|
||||
LEFT JOIN {context} ctx ON ap.contextid = ctx.id
|
||||
WHERE ctx.id IS NULL)");
|
||||
|
||||
$sql = "SELECT DISTINCT aic.contextid FROM {analytics_indicator_calc} aic
|
||||
LEFT JOIN {context} ctx ON aic.contextid = ctx.id
|
||||
WHERE ctx.id IS NULL";
|
||||
$indcalccontexts = $DB->get_records_sql($sql);
|
||||
|
||||
$contexts = $apcontexts + $indcalccontexts;
|
||||
if ($contexts) {
|
||||
list($sql, $params) = $DB->get_in_or_equal(array_keys($contexts));
|
||||
$DB->execute("DELETE FROM {analytics_prediction_actions} WHERE predictionid IN
|
||||
(SELECT ap.id FROM {analytics_predictions} ap WHERE ap.contextid $sql)", $params);
|
||||
|
||||
$DB->delete_records_select('analytics_predictions', "contextid $sql", $params);
|
||||
$DB->delete_records_select('analytics_indicator_calc', "contextid $sql", $params);
|
||||
}
|
||||
$contextsql = "SELECT id FROM {context} ctx";
|
||||
$DB->delete_records_select('analytics_predictions', "contextid NOT IN ($contextsql)");
|
||||
$DB->delete_records_select('analytics_indicator_calc', "contextid NOT IN ($contextsql)");
|
||||
|
||||
// Clean up stuff that depends on analysable ids that do not exist anymore.
|
||||
|
||||
$models = self::get_all_models();
|
||||
foreach ($models as $model) {
|
||||
|
||||
// We first dump into memory the list of analysables we have in the database (we could probably do this with 1 single
|
||||
// query for the 3 tables, but it may be safer to do it separately).
|
||||
$predictsamplesanalysableids = $DB->get_fieldset_select('analytics_predict_samples', 'DISTINCT analysableid',
|
||||
'modelid = :modelid', ['modelid' => $model->get_id()]);
|
||||
$predictsamplesanalysableids = array_flip($predictsamplesanalysableids);
|
||||
$trainsamplesanalysableids = $DB->get_fieldset_select('analytics_train_samples', 'DISTINCT analysableid',
|
||||
'modelid = :modelid', ['modelid' => $model->get_id()]);
|
||||
$trainsamplesanalysableids = array_flip($trainsamplesanalysableids);
|
||||
$usedanalysablesanalysableids = $DB->get_fieldset_select('analytics_used_analysables', 'DISTINCT analysableid',
|
||||
'modelid = :modelid', ['modelid' => $model->get_id()]);
|
||||
$usedanalysablesanalysableids = array_flip($usedanalysablesanalysableids);
|
||||
|
||||
$analyser = $model->get_analyser(array('notimesplitting' => true));
|
||||
|
||||
$analysables = $analyser->get_analysables();
|
||||
if (!$analysables) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$analysableids = array_map(function($analysable) {
|
||||
return $analysable->get_id();
|
||||
}, $analysables);
|
||||
foreach ($analysables as $analysable) {
|
||||
unset($predictsamplesanalysableids[$analysable->get_id()]);
|
||||
unset($trainsamplesanalysableids[$analysable->get_id()]);
|
||||
unset($usedanalysablesanalysableids[$analysable->get_id()]);
|
||||
}
|
||||
|
||||
list($notinsql, $params) = $DB->get_in_or_equal($analysableids, SQL_PARAMS_NAMED, 'param', false);
|
||||
$params['modelid'] = $model->get_id();
|
||||
$param = ['modelid' => $model->get_id()];
|
||||
|
||||
$DB->delete_records_select('analytics_predict_samples', "modelid = :modelid AND analysableid $notinsql", $params);
|
||||
$DB->delete_records_select('analytics_train_samples', "modelid = :modelid AND analysableid $notinsql", $params);
|
||||
if ($predictsamplesanalysableids) {
|
||||
list($idssql, $idsparams) = $DB->get_in_or_equal(array_flip($predictsamplesanalysableids), SQL_PARAMS_NAMED);
|
||||
$DB->delete_records_select('analytics_predict_samples', "modelid = :modelid AND analysableid $idssql",
|
||||
$param + $idsparams);
|
||||
}
|
||||
if ($trainsamplesanalysableids) {
|
||||
list($idssql, $idsparams) = $DB->get_in_or_equal(array_flip($trainsamplesanalysableids), SQL_PARAMS_NAMED);
|
||||
$DB->delete_records_select('analytics_train_samples', "modelid = :modelid AND analysableid $idssql",
|
||||
$param + $idsparams);
|
||||
}
|
||||
if ($usedanalysablesanalysableids) {
|
||||
list($idssql, $idsparams) = $DB->get_in_or_equal(array_flip($usedanalysablesanalysableids), SQL_PARAMS_NAMED);
|
||||
$DB->delete_records_select('analytics_used_analysables', "modelid = :modelid AND analysableid $idssql",
|
||||
$param + $idsparams);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -923,7 +923,7 @@ class model {
|
||||
}
|
||||
|
||||
// Get all samples data.
|
||||
list($sampleids, $samplesdata) = $this->get_analyser()->get_samples($sampleids);
|
||||
list($sampleids, $samplesdata) = $this->get_samples($sampleids);
|
||||
|
||||
// Calculate the targets.
|
||||
$predictions = array();
|
||||
@@ -1233,7 +1233,7 @@ class model {
|
||||
return $prediction->sampleid;
|
||||
}, $predictions);
|
||||
|
||||
list($unused, $samplesdata) = $this->get_analyser()->get_samples($sampleids);
|
||||
list($unused, $samplesdata) = $this->get_samples($sampleids);
|
||||
|
||||
$current = 0;
|
||||
|
||||
@@ -1275,7 +1275,7 @@ class model {
|
||||
*/
|
||||
public function prediction_sample_data($predictionobj) {
|
||||
|
||||
list($unused, $samplesdata) = $this->get_analyser()->get_samples(array($predictionobj->sampleid));
|
||||
list($unused, $samplesdata) = $this->get_samples(array($predictionobj->sampleid));
|
||||
|
||||
if (empty($samplesdata[$predictionobj->sampleid])) {
|
||||
throw new \moodle_exception('errorsamplenotavailable', 'analytics');
|
||||
@@ -1533,6 +1533,64 @@ class model {
|
||||
$DB->update_record('analytics_models', $this->model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around analyser's get_samples to skip DB's max-number-of-params exception.
|
||||
*
|
||||
* @param array $sampleids
|
||||
* @return array
|
||||
*/
|
||||
public function get_samples(array $sampleids): array {
|
||||
|
||||
if (empty($sampleids)) {
|
||||
throw new \coding_exception('No sample ids provided');
|
||||
}
|
||||
|
||||
$chunksize = count($sampleids);
|
||||
|
||||
// We start with just 1 chunk, if it is too large for the db we split the list of sampleids in 2 and we
|
||||
// try again. We repeat this process until the chunk is small enough for the db engine to process. The
|
||||
// >= has been added in case there are other \dml_read_exceptions unrelated to the max number of params.
|
||||
while (empty($done) && $chunksize >= 1) {
|
||||
|
||||
$chunks = array_chunk($sampleids, $chunksize);
|
||||
$allsampleids = [];
|
||||
$allsamplesdata = [];
|
||||
|
||||
foreach ($chunks as $index => $chunk) {
|
||||
|
||||
try {
|
||||
list($chunksampleids, $chunksamplesdata) = $this->get_analyser()->get_samples($chunk);
|
||||
} catch (\dml_read_exception $e) {
|
||||
|
||||
// Reduce the chunksize, we use floor() so the $chunksize is always less than the previous $chunksize value.
|
||||
$chunksize = floor($chunksize / 2);
|
||||
break;
|
||||
}
|
||||
|
||||
// We can sum as these two arrays are indexed by sampleid and there are no collisions.
|
||||
$allsampleids = $allsampleids + $chunksampleids;
|
||||
$allsamplesdata = $allsamplesdata + $chunksamplesdata;
|
||||
|
||||
if ($index === count($chunks) - 1) {
|
||||
// We successfully processed all the samples in all chunks, we are done.
|
||||
$done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($done)) {
|
||||
if (!empty($e)) {
|
||||
// Throw the last exception we caught, the \dml_read_exception we have been catching is unrelated to the max number
|
||||
// of param's exception.
|
||||
throw new \dml_read_exception($e);
|
||||
} else {
|
||||
throw new \coding_exception('We should never reach this point, there is a bug in ' .
|
||||
'core_analytics\\model::get_samples\'s code');
|
||||
}
|
||||
}
|
||||
return [$allsampleids, $allsamplesdata];
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges the insights cache.
|
||||
*/
|
||||
|
||||
@@ -306,13 +306,9 @@ class provider implements
|
||||
$idssql = "SELECT ap.id FROM {analytics_predictions} ap
|
||||
WHERE ap.contextid = :contextid AND ap.modelid = :modelid";
|
||||
$idsparams = ['contextid' => $context->id, 'modelid' => $modelid];
|
||||
$predictionids = $DB->get_fieldset_sql($idssql, $idsparams);
|
||||
if ($predictionids) {
|
||||
list($predictionidssql, $params) = $DB->get_in_or_equal($predictionids, SQL_PARAMS_NAMED);
|
||||
|
||||
$DB->delete_records_select('analytics_prediction_actions', "predictionid IN ($idssql)", $idsparams);
|
||||
$DB->delete_records_select('analytics_predictions', "id $predictionidssql", $params);
|
||||
}
|
||||
$DB->delete_records_select('analytics_prediction_actions', "predictionid IN ($idssql)", $idsparams);
|
||||
$DB->delete_records_select('analytics_predictions', "contextid = :contextid AND modelid = :modelid", $idsparams);
|
||||
}
|
||||
|
||||
// We delete them all this table is just a cache and we don't know which model filled it.
|
||||
|
||||
@@ -134,8 +134,9 @@ class analytics_manager_testcase extends advanced_testcase {
|
||||
$model->train();
|
||||
$model->predict();
|
||||
|
||||
$npredictsamples = $DB->count_records('analytics_predict_samples');
|
||||
$ntrainsamples = $DB->count_records('analytics_train_samples');
|
||||
$this->assertNotEmpty($DB->count_records('analytics_predict_samples'));
|
||||
$this->assertNotEmpty($DB->count_records('analytics_train_samples'));
|
||||
$this->assertNotEmpty($DB->count_records('analytics_used_analysables'));
|
||||
|
||||
// Now we delete an analysable, stored predict and training samples should be deleted.
|
||||
$deletedcontext = \context_course::instance($coursepredict1->id);
|
||||
@@ -145,6 +146,7 @@ class analytics_manager_testcase extends advanced_testcase {
|
||||
|
||||
$this->assertEmpty($DB->count_records('analytics_predict_samples', array('analysableid' => $coursepredict1->id)));
|
||||
$this->assertEmpty($DB->count_records('analytics_train_samples', array('analysableid' => $coursepredict1->id)));
|
||||
$this->assertEmpty($DB->count_records('analytics_used_analysables', array('analysableid' => $coursepredict1->id)));
|
||||
|
||||
set_config('enabled_stores', '', 'tool_log');
|
||||
get_log_manager(true);
|
||||
|
||||
@@ -341,6 +341,59 @@ class analytics_model_testcase extends advanced_testcase {
|
||||
$this->assertLessThanOrEqual(2, $DB->count_records('analytics_used_analysables', $params));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests model::get_samples()
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function test_get_samples() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
if (!PHPUNIT_LONGTEST) {
|
||||
$this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
|
||||
}
|
||||
|
||||
// 10000 should be enough to make oracle and mssql fail, if we want pgsql to fail we need around 70000
|
||||
// users, that is a few minutes just to create the users.
|
||||
$nusers = 10000;
|
||||
|
||||
$userids = [];
|
||||
for ($i = 0; $i < $nusers; $i++) {
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$userids[] = $user->id;
|
||||
}
|
||||
|
||||
$target = \core_analytics\manager::get_target('test_target_site_users');
|
||||
$indicator = \core_analytics\manager::get_indicator('test_indicator_max');
|
||||
$model = \core_analytics\model::create($target, ['test_indicator_max' => $indicator],
|
||||
'\core\analytics\time_splitting\single_range');
|
||||
|
||||
list($sampleids, $samplesdata) = $model->get_samples($userids);
|
||||
$this->assertCount($nusers, $sampleids);
|
||||
$this->assertCount($nusers, $samplesdata);
|
||||
|
||||
$subset = array_slice($userids, 0, 100);
|
||||
list($sampleids, $samplesdata) = $model->get_samples($subset);
|
||||
$this->assertCount(100, $sampleids);
|
||||
$this->assertCount(100, $samplesdata);
|
||||
|
||||
$subset = array_slice($userids, 0, 2);
|
||||
list($sampleids, $samplesdata) = $model->get_samples($subset);
|
||||
$this->assertCount(2, $sampleids);
|
||||
$this->assertCount(2, $samplesdata);
|
||||
|
||||
$subset = array_slice($userids, 0, 1);
|
||||
list($sampleids, $samplesdata) = $model->get_samples($subset);
|
||||
$this->assertCount(1, $sampleids);
|
||||
$this->assertCount(1, $samplesdata);
|
||||
|
||||
// Unexisting, so nothing returned, but still 2 arrays.
|
||||
list($sampleids, $samplesdata) = $model->get_samples([1231231231231231]);
|
||||
$this->assertEmpty($sampleids);
|
||||
$this->assertEmpty($samplesdata);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a model log record.
|
||||
*/
|
||||
|
||||
@@ -402,7 +402,6 @@ class api {
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_enabled() {
|
||||
$plugininfo = \core_plugin_manager::instance()->get_plugin_info('auth_oauth2');
|
||||
return $plugininfo->is_enabled();
|
||||
return is_enabled_auth('oauth2');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,5 +50,39 @@ function xmldb_auth_oauth2_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.6.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2018120301) {
|
||||
// Fetch Facebook, Google, and Microsoft issuers. We use the URL field to determine the issuer type as it's the only
|
||||
// field that contains the keyword that can somewhat let us reliably determine the issuer type.
|
||||
$likefacebook = $DB->sql_like('oe.url', ':facebook');
|
||||
$likegoogle = $DB->sql_like('oe.url', ':google');
|
||||
$likemicrosoft = $DB->sql_like('oe.url', ':microsoft');
|
||||
|
||||
$params = [
|
||||
'facebook' => '%facebook%',
|
||||
'google' => '%google%',
|
||||
'microsoft' => '%microsoft%',
|
||||
];
|
||||
|
||||
// We're querying from the oauth2_endpoint table because the base URLs of FB and Microsoft can be empty in the issuer table.
|
||||
$subsql = "
|
||||
SELECT DISTINCT oe.issuerid
|
||||
FROM {oauth2_endpoint} oe
|
||||
WHERE $likefacebook
|
||||
OR $likegoogle
|
||||
OR $likemicrosoft";
|
||||
|
||||
// Update non-Facebook/Google/Microsoft issuers and set requireconfirmation to 1.
|
||||
$updatesql = "
|
||||
UPDATE {oauth2_issuer}
|
||||
SET requireconfirmation = 1
|
||||
WHERE id NOT IN ({$subsql})";
|
||||
$DB->execute($updatesql, $params);
|
||||
|
||||
// Delete linked logins for non-Facebook/Google/Microsoft issuers. They can easily re-link their logins anyway.
|
||||
$DB->delete_records_select('auth_oauth2_linked_login', "issuerid NOT IN ($subsql)", $params);
|
||||
|
||||
upgrade_plugin_savepoint(true, 2018120301, 'auth', 'oauth2');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,10 @@ then cut and paste the address into the address
|
||||
line at the top of your web browser window.
|
||||
|
||||
If you need help, please contact the site administrator,
|
||||
{$a->admin}';
|
||||
{$a->admin}
|
||||
|
||||
If you did not do this, someone else could be trying to compromise your account.
|
||||
Please contact the site administrator immediately.';
|
||||
$string['confirmaccountemailsubject'] = '{$a}: account confirmation';
|
||||
$string['confirmationinvalid'] = 'The confirmation link is either invalid, or has expired. Please start the login process again to generate a new confirmation email.';
|
||||
$string['confirmationpending'] = 'This account is pending email confirmation.';
|
||||
@@ -60,7 +63,10 @@ then cut and paste the address into the address
|
||||
line at the top of your web browser window.
|
||||
|
||||
If you need help, please contact the site administrator,
|
||||
{$a->admin}';
|
||||
{$a->admin}
|
||||
|
||||
If you did not do this, someone else could be trying to compromise your account.
|
||||
Please contact the site administrator immediately.';
|
||||
$string['confirmlinkedloginemailsubject'] = '{$a}: linked login confirmation';
|
||||
$string['createaccountswarning'] = 'This authentication plugin allows users to create accounts on your site. You may want to enable the setting "authpreventaccountcreation" if you use this plugin.';
|
||||
$string['createnewlinkedlogin'] = 'Link a new account ({$a})';
|
||||
|
||||
@@ -140,4 +140,23 @@ class auth_oauth2_external_testcase extends advanced_testcase {
|
||||
$this->assertEquals($newuser->id, $match->get('userid'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that is_enabled correctly identifies when the plugin is enabled.
|
||||
*/
|
||||
public function test_is_enabled() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
set_config('auth', 'manual,oauth2');
|
||||
$this->assertTrue(\auth_oauth2\api::is_enabled());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that is_enabled correctly identifies when the plugin is disabled.
|
||||
*/
|
||||
public function test_is_enabled_disabled() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
set_config('auth', 'manual');
|
||||
$this->assertFalse(\auth_oauth2\api::is_enabled());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018120300; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2018120301; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2018112800; // Requires this Moodle version.
|
||||
$plugin->component = 'auth_oauth2'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -294,12 +294,19 @@ class auth_plugin_shibboleth extends auth_plugin_base {
|
||||
}
|
||||
|
||||
$url = new moodle_url('/auth/shibboleth/index.php');
|
||||
$iconurl = moodle_url::make_pluginfile_url(context_system::instance()->id,
|
||||
'auth_shibboleth',
|
||||
'logo',
|
||||
null,
|
||||
'/',
|
||||
$config->auth_logo);
|
||||
|
||||
if ($config->auth_logo) {
|
||||
$iconurl = moodle_url::make_pluginfile_url(
|
||||
context_system::instance()->id,
|
||||
'auth_shibboleth',
|
||||
'logo',
|
||||
null,
|
||||
null,
|
||||
$config->auth_logo);
|
||||
} else {
|
||||
$iconurl = null;
|
||||
}
|
||||
|
||||
$result[] = ['url' => $url, 'iconurl' => $iconurl, 'name' => $config->login_name];
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -42,10 +42,18 @@ class behat_auth extends behat_base {
|
||||
* Logs in the user. There should exist a user with the same value as username and password.
|
||||
*
|
||||
* @Given /^I log in as "(?P<username_string>(?:[^"]|\\")*)"$/
|
||||
* @param string $username the user to log in as.
|
||||
* @param moodle_url|null $wantsurl optional, URL to go to after logging in.
|
||||
*/
|
||||
public function i_log_in_as($username) {
|
||||
public function i_log_in_as(string $username, moodle_url $wantsurl = null) {
|
||||
|
||||
$loginurl = new moodle_url('/login/index.php');
|
||||
if ($wantsurl !== null) {
|
||||
$loginurl->param('wantsurl', $wantsurl->out_as_local_url());
|
||||
}
|
||||
|
||||
// Visit login page.
|
||||
$this->getSession()->visit($this->locate_path('login/index.php'));
|
||||
$this->getSession()->visit($this->locate_path($loginurl->out_as_local_url()));
|
||||
|
||||
// Enter username and password.
|
||||
$this->execute('behat_forms::i_set_the_field_to', array('Username', $this->escape($username)));
|
||||
|
||||
@@ -98,8 +98,14 @@ class core_badges_assertion {
|
||||
if ($this->_data) {
|
||||
$hash = $this->_data->uniquehash;
|
||||
$email = empty($this->_data->backpackemail) ? $this->_data->email : $this->_data->backpackemail;
|
||||
$assertionurl = new moodle_url('/badges/assertion.php', array('b' => $hash));
|
||||
$classurl = new moodle_url('/badges/assertion.php', array('b' => $hash, 'action' => 1));
|
||||
$assertionurl = new moodle_url('/badges/assertion.php', array('b' => $hash, 'obversion' => $this->_obversion));
|
||||
|
||||
if ($this->_obversion == OPEN_BADGES_V2) {
|
||||
$badgeid = $this->_data ? $this->_data->id : 0;
|
||||
$classurl = new moodle_url('/badges/badge_json.php', array('id' => $badgeid));
|
||||
} else {
|
||||
$classurl = new moodle_url('/badges/assertion.php', array('b' => $hash, 'action' => 1));
|
||||
}
|
||||
|
||||
// Required.
|
||||
$assertion['uid'] = $hash;
|
||||
@@ -136,13 +142,19 @@ class core_badges_assertion {
|
||||
} else {
|
||||
$context = context_course::instance($this->_data->courseid);
|
||||
}
|
||||
$issuerurl = new moodle_url('/badges/assertion.php', array('b' => $this->_data->uniquehash, 'action' => 0));
|
||||
|
||||
// Required.
|
||||
$class['name'] = $this->_data->name;
|
||||
$class['description'] = $this->_data->description;
|
||||
$class['image'] = moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $this->_data->id, '/', 'f1')->out(false);
|
||||
$class['criteria'] = $this->_url->out(false); // Currently issued badge URL.
|
||||
|
||||
if ($this->_obversion == OPEN_BADGES_V2) {
|
||||
$badgeid = $this->_data ? $this->_data->id : 0;
|
||||
$issuerurl = new moodle_url('/badges/badge_json.php', array('id' => $badgeid, 'action' => 0));
|
||||
} else {
|
||||
$issuerurl = new moodle_url('/badges/assertion.php', array('b' => $this->_data->uniquehash, 'action' => 0));
|
||||
}
|
||||
$class['issuer'] = $issuerurl->out(false);
|
||||
$this->embed_data_badge_version2($class, OPEN_BADGES_V2_TYPE_BADGE);
|
||||
}
|
||||
@@ -265,12 +277,13 @@ class core_badges_assertion {
|
||||
}
|
||||
|
||||
$hash = $this->_data->uniquehash;
|
||||
$badgeid = $this->_data ? $this->_data->id : 0;
|
||||
$assertionsurl = new moodle_url('/badges/assertion.php', array('b' => $hash, 'obversion' => $this->_obversion));
|
||||
$classurl = new moodle_url(
|
||||
'/badges/assertion.php',
|
||||
array('b' => $hash, 'action' => 1, 'obversion' => $this->_obversion)
|
||||
'/badges/badge_json.php',
|
||||
array('id' => $badgeid)
|
||||
);
|
||||
$issuerurl = new moodle_url('/badges/assertion.php', array('b' => $this->_data->uniquehash, 'action' => 0,
|
||||
$issuerurl = new moodle_url('/badges/badge_json.php', array('id' => $badgeid, 'action' => 0,
|
||||
'obversion' => $this->_obversion));
|
||||
// For assertion.
|
||||
if ($type == OPEN_BADGES_V2_TYPE_ASSERTION) {
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file keeps track of upgrades to the myoverview block
|
||||
* @since 3.6.7
|
||||
* @package block_myoverview
|
||||
* @copyright 2019 Jake Dallimore <jrhdallimore@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Upgrade code for the MyOverview block.
|
||||
*
|
||||
* @param int $oldversion
|
||||
*/
|
||||
function xmldb_block_myoverview_upgrade($oldversion) {
|
||||
global $DB;
|
||||
|
||||
if ($oldversion < 2018120301) {
|
||||
// Remove orphaned course favourites, which weren't being deleted when the course was deleted.
|
||||
$sql = 'SELECT f.id
|
||||
FROM {favourite} f
|
||||
LEFT JOIN {course} c
|
||||
ON (c.id = f.itemid)
|
||||
WHERE f.component = :component
|
||||
AND f.itemtype = :itemtype
|
||||
AND c.id IS NULL';
|
||||
$params = ['component' => 'core_course', 'itemtype' => 'courses'];
|
||||
|
||||
if ($records = $DB->get_fieldset_sql($sql, $params)) {
|
||||
$chunks = array_chunk($records, 1000);
|
||||
foreach ($chunks as $chunk) {
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($chunk);
|
||||
$DB->delete_records_select('favourite', "id $insql", $inparams);
|
||||
}
|
||||
}
|
||||
|
||||
upgrade_block_savepoint(true, 2018120301, 'myoverview', false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -35,7 +35,7 @@ $string['aria:displaydropdown'] = 'Display drop-down menu';
|
||||
$string['aria:favourites'] = 'Show starred courses';
|
||||
$string['aria:future'] = 'Show future courses';
|
||||
$string['aria:groupingdropdown'] = 'Grouping drop-down menu';
|
||||
$string['aria:inprogress'] = 'Show in courses in progress';
|
||||
$string['aria:inprogress'] = 'Show courses in progress';
|
||||
$string['aria:lastaccessed'] = 'Sort courses by last accessed date';
|
||||
$string['aria:list'] = 'Switch to list view';
|
||||
$string['aria:title'] = 'Sort courses by course name';
|
||||
|
||||
@@ -115,3 +115,14 @@ function block_myoverview_user_preferences() {
|
||||
|
||||
return $preferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-delete course hook to cleanup any records with references to the deleted course.
|
||||
*
|
||||
* @param stdClass $course The deleted course
|
||||
*/
|
||||
function block_myoverview_pre_course_delete(\stdClass $course) {
|
||||
// Removing any starred courses which have been created for users, for this course.
|
||||
global $DB;
|
||||
$DB->delete_records('favourite', ['component' => 'core_course', 'itemtype' => 'courses', 'itemid' => $course->id]);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
"courseimage": "https://moodlesite/pluginfile/123/course/overviewfiles/123.jpg",
|
||||
"fullname": "course 3",
|
||||
"hasprogress": true,
|
||||
"progress": 10
|
||||
"progress": 10,
|
||||
"visible": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
"courseimage": "https://moodlesite/pluginfile/123/course/overviewfiles/123.jpg",
|
||||
"fullname": "course 3",
|
||||
"hasprogress": true,
|
||||
"progress": 10
|
||||
"progress": 10,
|
||||
"visible": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -41,21 +42,28 @@
|
||||
data-course-id="{{{id}}}">
|
||||
<div class="row-fluid">
|
||||
<div class="{{#hasprogress}}col-md-6 span6{{/hasprogress}}{{^hasprogress}}col-md-11 span11{{/hasprogress}} d-flex align-items-center">
|
||||
<a href="{{viewurl}}" class="coursename">
|
||||
{{#showshortname}}
|
||||
<div>
|
||||
<a href="{{viewurl}}" class="coursename">
|
||||
{{#showshortname}}
|
||||
<span class="sr-only">
|
||||
{{#str}}aria:courseshortname, core_course{{/str}}
|
||||
</span>
|
||||
<div class="text-muted muted">
|
||||
{{{shortname}}}
|
||||
</div>
|
||||
{{/showshortname}}
|
||||
{{> core_course/favouriteicon }}
|
||||
<span class="sr-only">
|
||||
{{#str}}aria:courseshortname, core_course{{/str}}
|
||||
{{#str}}aria:coursename, core_course{{/str}}
|
||||
</span>
|
||||
<div class="text-muted muted">
|
||||
{{{shortname}}}
|
||||
{{{fullname}}}
|
||||
</a>
|
||||
{{^visible}}
|
||||
<div class="hiddenmessage">
|
||||
<span class="badge badge-info">{{#str}} hiddenfromstudents {{/str}}</span>
|
||||
</div>
|
||||
{{/showshortname}}
|
||||
{{> core_course/favouriteicon }}
|
||||
<span class="sr-only">
|
||||
{{#str}}aria:coursename, core_course{{/str}}
|
||||
</span>
|
||||
{{{fullname}}}
|
||||
</a>
|
||||
{{/visible}}
|
||||
</div>
|
||||
</div>
|
||||
{{#hasprogress}}
|
||||
<div class="col-md-5 span5 pt-1">
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
"fullname": "course 3",
|
||||
"summary": "This course is about assignments",
|
||||
"hasprogress": true,
|
||||
"progress": 10
|
||||
"progress": 10,
|
||||
"visible": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -48,21 +49,28 @@
|
||||
|
||||
<div class="align-self-stretch d-flex flex-column w-100">
|
||||
<div class="d-flex mb-1">
|
||||
<a href="{{viewurl}}" class="coursename">
|
||||
{{#showshortname}}
|
||||
<div>
|
||||
<a href="{{viewurl}}" class="coursename">
|
||||
{{#showshortname}}
|
||||
<span class="sr-only">
|
||||
{{#str}}aria:courseshortname, core_course{{/str}}
|
||||
</span>
|
||||
<div class="text-muted muted mb-1">
|
||||
{{{shortname}}}
|
||||
</div>
|
||||
{{/showshortname}}
|
||||
{{> core_course/favouriteicon }}
|
||||
<span class="sr-only">
|
||||
{{#str}}aria:courseshortname, core_course{{/str}}
|
||||
{{#str}}aria:coursename, core_course{{/str}}
|
||||
</span>
|
||||
<div class="text-muted muted mb-1">
|
||||
{{{shortname}}}
|
||||
<h6 class="d-inline h5">{{{fullname}}}</h6>
|
||||
</a>
|
||||
{{^visible}}
|
||||
<div class="hiddenmessage">
|
||||
<span class="badge badge-info">{{#str}} hiddenfromstudents {{/str}}</span>
|
||||
</div>
|
||||
{{/showshortname}}
|
||||
{{> core_course/favouriteicon }}
|
||||
<span class="sr-only">
|
||||
{{#str}}aria:coursename, core_course{{/str}}
|
||||
</span>
|
||||
<h6 class="d-inline h5">{{{fullname}}}</h6>
|
||||
</a>
|
||||
{{/visible}}
|
||||
</div>
|
||||
{{> block_myoverview/course-action-menu }}
|
||||
</div>
|
||||
<div class="summary">
|
||||
@@ -78,4 +86,4 @@
|
||||
</div>
|
||||
</div>
|
||||
{{/courses}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018120300; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2018120301; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2018112800; // Requires this Moodle version.
|
||||
$plugin->component = 'block_myoverview'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -114,12 +114,8 @@ class behat_block_site_main_menu extends behat_base {
|
||||
protected function get_site_menu_activity_element($element, $selectortype, $activityname) {
|
||||
$activitynode = $this->get_site_menu_activity_node($activityname);
|
||||
|
||||
// Transforming to Behat selector/locator.
|
||||
list($selector, $locator) = $this->transform_selector($selectortype, $element);
|
||||
$exception = new ElementNotFoundException($this->getSession(), '"' . $element . '" "' .
|
||||
$selectortype . '" in "' . $activityname . '" ');
|
||||
|
||||
return $this->find($selector, $locator, $exception, $activitynode);
|
||||
$exception = new ElementNotFoundException($this->getSession(), "'{$element}' '{$selectortype}' in '${activityname}'");
|
||||
return $this->find($selectortype, $element, $exception, $activitynode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,7 +56,11 @@ class behat_block_social_activities extends behat_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the specified activity's action menu contains an item.
|
||||
* Checks that the specified activity in the social activities block should have the specified editing icon.
|
||||
*
|
||||
* This includes items in the action menu for the item (does not require it to be open)
|
||||
*
|
||||
* You should be in the course page with editing mode turned on.
|
||||
*
|
||||
* @Then /^"(?P<activity_name_string>(?:[^"]|\\")*)" activity in social activities block should have "(?P<icon_name_string>(?:[^"]|\\")*)" editing icon$/
|
||||
* @param string $activityname
|
||||
@@ -71,7 +75,11 @@ class behat_block_social_activities extends behat_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the specified activity's action menu contains an item.
|
||||
* Checks that the specified activity in the social activities block should not have the specified editing icon.
|
||||
*
|
||||
* This includes items in the action menu for the item (does not require it to be open)
|
||||
*
|
||||
* You should be in the course page with editing mode turned on.
|
||||
*
|
||||
* @Then /^"(?P<activity_name_string>(?:[^"]|\\")*)" activity in social activities block should not have "(?P<icon_name_string>(?:[^"]|\\")*)" editing icon$/
|
||||
* @param string $activityname
|
||||
@@ -103,7 +111,7 @@ class behat_block_social_activities extends behat_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks on the specified element inside the activity container.
|
||||
* Finds the element containing a specific activity in the social activity block.
|
||||
*
|
||||
* @throws ElementNotFoundException
|
||||
* @param string $element
|
||||
@@ -114,16 +122,12 @@ class behat_block_social_activities extends behat_base {
|
||||
protected function get_social_block_activity_element($element, $selectortype, $activityname) {
|
||||
$activitynode = $this->get_social_block_activity_node($activityname);
|
||||
|
||||
// Transforming to Behat selector/locator.
|
||||
list($selector, $locator) = $this->transform_selector($selectortype, $element);
|
||||
$exception = new ElementNotFoundException($this->getSession(), '"' . $element . '" "' .
|
||||
$selectortype . '" in "' . $activityname . '" ');
|
||||
|
||||
return $this->find($selector, $locator, $exception, $activitynode);
|
||||
$exception = new ElementNotFoundException($this->getSession(), "'{$element}' '{$selectortype}' in '${activityname}'");
|
||||
return $this->find($selectortype, $element, $exception, $activitynode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the specified activity is hidden.
|
||||
* Checks that the specified activity is hidden in the social activities block.
|
||||
*
|
||||
* @Then /^"(?P<activity_name_string>(?:[^"]|\\")*)" activity in social activities block should be hidden$/
|
||||
* @param string $activityname
|
||||
@@ -133,7 +137,7 @@ class behat_block_social_activities extends behat_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the specified activity is hidden.
|
||||
* Checks that the specified activity is hidden in the social activities block.
|
||||
*
|
||||
* @Then /^"(?P<activity_name_string>(?:[^"]|\\")*)" activity in social activities block should be available but hidden from course page$/
|
||||
* @param string $activityname
|
||||
@@ -143,7 +147,7 @@ class behat_block_social_activities extends behat_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an activity actions menu if it is not already opened.
|
||||
* Opens an activity actions menu in the social activities block if it is not already opened.
|
||||
*
|
||||
* @Given /^I open "(?P<activity_name_string>(?:[^"]|\\")*)" actions menu in social activities block$/
|
||||
* @throws DriverException The step is not available when Javascript is disabled
|
||||
|
||||
@@ -88,13 +88,18 @@ class block_starredcourses_external extends core_course_external {
|
||||
return ($a->timemodified > $b->timemodified) ? -1 : 1;
|
||||
});
|
||||
|
||||
$formattedcourses = array_map(function($favourite) use ($renderer) {
|
||||
$formattedcourses = array();
|
||||
foreach ($favourites as $favourite) {
|
||||
$course = get_course($favourite->itemid);
|
||||
$context = \context_course::instance($favourite->itemid);
|
||||
$canviewhiddencourses = has_capability('moodle/course:viewhiddencourses', $context);
|
||||
|
||||
$exporter = new course_summary_exporter($course, ['context' => $context, 'isfavourite' => true]);
|
||||
return $exporter->export($renderer);
|
||||
}, $favourites);
|
||||
if ($course->visible || $canviewhiddencourses) {
|
||||
$exporter = new course_summary_exporter($course, ['context' => $context, 'isfavourite' => true]);
|
||||
$formattedcourse = $exporter->export($renderer);
|
||||
$formattedcourses[] = $formattedcourse;
|
||||
}
|
||||
}
|
||||
|
||||
return $formattedcourses;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/notification","core/templates","core/paged_content_factory","core/str","core/user_date","block_timeline/calendar_events_repository"],function(a,b,c,d,e,f,g){var h=86400,i={EMPTY_MESSAGE:'[data-region="empty-message"]',ROOT:'[data-region="event-list-container"]',EVENT_LIST_CONTENT:'[data-region="event-list-content"]',EVENT_LIST_LOADING_PLACEHOLDER:'[data-region="event-list-loading-placeholder"]'},j={EVENT_LIST_CONTENT:"block_timeline/event-list-content"},k={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,ariaLabels:{itemsperpagecomponents:"ariaeventlistpagelimit, block_timeline"}},l=function(a){a.find(i.EVENT_LIST_CONTENT).addClass("hidden"),a.find(i.EMPTY_MESSAGE).removeClass("hidden")},m=function(a){a.find(i.EVENT_LIST_CONTENT).removeClass("hidden"),a.find(i.EMPTY_MESSAGE).addClass("hidden")},n=function(a){a.find(i.EVENT_LIST_CONTENT).empty()},o=function(a,b){var c={},d={eventsbyday:[]};return a.forEach(function(a){var d=f.getUserMidnightForTimestamp(a.timesort,b);c[d]?c[d].push(a):c[d]=[a]}),Object.keys(c).forEach(function(a){var e=c[a];d.eventsbyday.push({past:a<b,dayTimestamp:a,events:e})}),d},p=function(a,b){var d=o(a,b),e=j.EVENT_LIST_CONTENT;return c.render(e,d)},q=function(a,b,c,d,e,f){var i=a+c*h,j=void 0!=d&&a+d*h,k={starttime:i,limit:b};return e&&(k.aftereventid=e),j&&(k.endtime=j),f?(k.courseid=f,g.queryByCourse(k)):g.queryByTime(k)},r=function(a,b,c,d,e,f,g,h){for(var i=a.pageNumber,j=a.limit,k=i;!d.hasOwnProperty(k);)k--;var l=d[k],m=null;return m=e&&e.hasOwnProperty(i)?e[i]:q(c,j+1,g,h,l,f),m.then(function(a){if(!a.events.length)return b.allItemsLoaded(i),[];var c=a.events,d=c.length<=j;return d?b.allItemsLoaded(i):c.pop(),c})},s=function(c,f,g,h,i,j,l,m,n){var o={1:0},q=!1,s=a.extend({},k,n);return e.get_string("ariaeventlistpagelimit","block_timeline",a.isArray(c)?c[0].value:c).then(function(a){return s.ariaLabels.itemsperpage=a,s.ariaLabels.paginationnav=m,a}).then(function(){return d.createWithLimit(c,function(c,d){var e=[];return c.forEach(function(a){var c=a.pageNumber,h=r(a,d,g,o,f,i,j,l).then(function(a){if(a.length){q=!0;var b=a[a.length-1].id;return o[c+1]=b,p(a,g)}return a})["catch"](b.exception);e.push(h)}),a.when.apply(a,e).then(function(){h.resolve(q)})["catch"](function(){h.resolve(q)}),e},s)})},t=function(d,e,f,g,h){d=a(d);var j=a.Deferred(),k=d.find(i.EVENT_LIST_CONTENT),o=d.find(i.EVENT_LIST_LOADING_PLACEHOLDER),p=d.attr("data-course-id"),q=parseInt(d.attr("data-days-offset"),10),r=d.attr("data-days-limit"),t=parseInt(d.attr("data-midnight"),10);return n(d),m(d),o.removeClass("hidden"),void 0!=r&&(r=parseInt(r,10)),s(e,f,t,j,p,q,r,g,h).then(function(b,e){return b=a(b),b.addClass("hidden"),c.replaceNodeContents(k,b,e),j.then(function(a){return b.removeClass("hidden"),o.addClass("hidden"),a||l(d),a})["catch"](function(){return!1}),b})["catch"](b.exception)};return{init:t,rootSelector:i.ROOT}});
|
||||
define(["jquery","core/notification","core/templates","core/paged_content_factory","core/str","core/user_date","block_timeline/calendar_events_repository"],function(a,b,c,d,e,f,g){var h=86400,i={EMPTY_MESSAGE:'[data-region="empty-message"]',ROOT:'[data-region="event-list-container"]',EVENT_LIST_CONTENT:'[data-region="event-list-content"]',EVENT_LIST_LOADING_PLACEHOLDER:'[data-region="event-list-loading-placeholder"]'},j={EVENT_LIST_CONTENT:"block_timeline/event-list-content"},k={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,ariaLabels:{itemsperpagecomponents:"ariaeventlistpagelimit, block_timeline"}},l=function(a){a.find(i.EVENT_LIST_CONTENT).addClass("hidden"),a.find(i.EMPTY_MESSAGE).removeClass("hidden")},m=function(a){a.find(i.EVENT_LIST_CONTENT).removeClass("hidden"),a.find(i.EMPTY_MESSAGE).addClass("hidden")},n=function(a){a.find(i.EVENT_LIST_CONTENT).empty()},o=function(a,b){var c={},d={eventsbyday:[]};return a.forEach(function(a){var d=f.getUserMidnightForTimestamp(a.timesort,b);c[d]?c[d].push(a):c[d]=[a]}),Object.keys(c).forEach(function(a){var e=c[a];d.eventsbyday.push({past:a<b,dayTimestamp:a,events:e})}),d},p=function(a,b){var d=o(a,b),e=j.EVENT_LIST_CONTENT;return c.render(e,d)},q=function(a,b,c,d,e,f){var i=a+c*h,j=void 0!=d&&a+d*h,k={starttime:i,limit:b};return e&&(k.aftereventid=e),j&&(k.endtime=j),f?(k.courseid=f,g.queryByCourse(k)):g.queryByTime(k)},r=function(a,b,c,d,e,f,g,h){for(var i=a.pageNumber,j=a.limit,k=i;!d.hasOwnProperty(k);)k--;var l=d[k],m=null;return m=e&&e.hasOwnProperty(i)?e[i]:q(c,j+1,g,h,l,f),m.then(function(a){if(!a.events.length)return b.allItemsLoaded(i),[];var c=a.events.filter(function(a){return"open"!=a.eventtype&&"opensubmission"!=a.eventtype}),d=c.length<=j;return d?b.allItemsLoaded(i):c.pop(),c})},s=function(c,f,g,h,i,j,l,m,n){var o={1:0},q=!1,s=a.extend({},k,n);return e.get_string("ariaeventlistpagelimit","block_timeline",a.isArray(c)?c[0].value:c).then(function(a){return s.ariaLabels.itemsperpage=a,s.ariaLabels.paginationnav=m,a}).then(function(){return d.createWithLimit(c,function(c,d){var e=[];return c.forEach(function(a){var c=a.pageNumber,h=r(a,d,g,o,f,i,j,l).then(function(a){if(a.length){q=!0;var b=a[a.length-1].id;return o[c+1]=b,p(a,g)}return a})["catch"](b.exception);e.push(h)}),a.when.apply(a,e).then(function(){h.resolve(q)})["catch"](function(){h.resolve(q)}),e},s)})},t=function(d,e,f,g,h){d=a(d);var j=a.Deferred(),k=d.find(i.EVENT_LIST_CONTENT),o=d.find(i.EVENT_LIST_LOADING_PLACEHOLDER),p=d.attr("data-course-id"),q=parseInt(d.attr("data-days-offset"),10),r=d.attr("data-days-limit"),t=parseInt(d.attr("data-midnight"),10);return n(d),m(d),o.removeClass("hidden"),void 0!=r&&(r=parseInt(r,10)),s(e,f,t,j,p,q,r,g,h).then(function(b,e){return b=a(b),b.addClass("hidden"),c.replaceNodeContents(k,b,e),j.then(function(a){return b.removeClass("hidden"),o.addClass("hidden"),a||l(d),a})["catch"](function(){return!1}),b})["catch"](b.exception)};return{init:t,rootSelector:i.ROOT}});
|
||||
@@ -265,7 +265,10 @@ function(
|
||||
return [];
|
||||
}
|
||||
|
||||
var calendarEvents = result.events;
|
||||
var calendarEvents = result.events.filter(function(event) {
|
||||
// Do not include events that does not have a due date.
|
||||
return event.eventtype != "open" && event.eventtype != "opensubmission";
|
||||
});
|
||||
// We expect to receive limit + 1 events back from the server.
|
||||
// Any less means there are no more events to load.
|
||||
var loadedAll = calendarEvents.length <= limit;
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
}}
|
||||
<div class="border-bottom pb-2">
|
||||
{{#eventsbyday}}
|
||||
<h5 class="h6 mt-3 mb-0 {{#past}}text-danger{{/past}}">{{#userdate}} {{dayTimestamp}}, {{#str}} strftimedayshort, core_langconfig {{/str}} {{/userdate}}</h5>
|
||||
<h5 class="h6 mt-3 mb-0 {{#past}}text-danger{{/past}}">{{#userdate}} {{dayTimestamp}}, {{#str}} strftimedaydate, core_langconfig {{/str}} {{/userdate}}</h5>
|
||||
{{> block_timeline/event-list-items }}
|
||||
{{/eventsbyday}}
|
||||
</div>
|
||||
@@ -460,8 +460,7 @@ class provider implements
|
||||
$params = array_merge($inparams, ['userid' => $userid]);
|
||||
$associds = $DB->get_fieldset_sql($sql, $params);
|
||||
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($associds, SQL_PARAMS_NAMED, 'param', true);
|
||||
$DB->delete_records_select('blog_association', "id $insql", $inparams);
|
||||
$DB->delete_records_list('blog_association', 'id', $associds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -370,6 +370,37 @@ class core_blog_privacy_testcase extends provider_testcase {
|
||||
$this->assertTrue($DB->record_exists('post', ['courseid' => $c1->id, 'userid' => $u1->id, 'module' => 'notes']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test provider delete_data_for_user with a context that contains no entries
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_delete_data_for_user_empty_context() {
|
||||
global $DB;
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$context = context_course::instance($course->id);
|
||||
|
||||
// Create a blog entry for user, associated with course.
|
||||
$entry = new blog_entry($this->create_post(['userid' => $user->id, 'courseid' => $course->id])->id);
|
||||
$entry->add_association($context->id);
|
||||
|
||||
// Generate list of contexts for user.
|
||||
$contexts = provider::get_contexts_for_userid($user->id);
|
||||
$this->assertContains($context->id, $contexts->get_contextids());
|
||||
|
||||
// Now delete the blog entry.
|
||||
$entry->delete();
|
||||
|
||||
// Try to delete user data using contexts obtained prior to entry deletion.
|
||||
$contextlist = new approved_contextlist($user, 'core_blog', $contexts->get_contextids());
|
||||
provider::delete_data_for_user($contextlist);
|
||||
|
||||
// Sanity check to ensure blog_associations is really empty.
|
||||
$this->assertEmpty($DB->get_records('blog_association', ['contextid' => $context->id]));
|
||||
}
|
||||
|
||||
public function test_delete_data_for_all_users_in_context() {
|
||||
global $DB;
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core_calendar/selectors","core_calendar/events","core/str","core/templates"],function(a,b,c,d,e){var f=function(d){d.on("click",b.eventFilterItem,function(b){var c=a(b.currentTarget);g(c),b.preventDefault()}),a("body").on(c.viewUpdated,function(){var c=d.find(b.eventFilterItem);c.each(function(b,c){if(c=a(c),c.data("eventtype-hidden")){var d=i(c);h(d)}})})},g=function(a){var b=i(a);return b.hidden=!b.hidden,d.get_string("eventtype"+b.eventtype,"calendar").then(function(a){return b.name=a,b}).then(function(a){return e.render("core_calendar/event_filter_key",a)}).then(function(b,c){return e.replaceNode(a,b,c)}).then(function(){h(b)})},h=function(b){M.util.js_pending("month-mini-filterChanged"),a("body").trigger(c.filterChanged,{type:b.eventtype,hidden:b.hidden}),M.util.js_complete("month-mini-filterChanged")},i=function(a){return{eventtype:a.data("eventtype"),hidden:a.data("eventtype-hidden")}};return{init:function(b){b=a(b),f(b)}}});
|
||||
define(["jquery","core_calendar/selectors","core_calendar/events","core/str","core/templates"],function(a,b,c,d,e){var f=function(d){d.on("click",b.eventFilterItem,function(b){var c=a(b.currentTarget);g(c),b.preventDefault()}),a("body").on(c.viewUpdated,function(){var c=d.find(b.eventFilterItem);c.each(function(b,c){if(c=a(c),c.data("eventtype-hidden")){var d=i(c);h(d)}})})},g=function(a){var b=i(a);return b.hidden=!b.hidden,d.get_string("eventtype"+b.eventtype,"calendar").then(function(a){return b.name=a,b.icon=!0,b.key="i/"+b.eventtype+"event",b.component="core",b}).then(function(a){return e.render("core_calendar/event_filter_key",a)}).then(function(b,c){return e.replaceNode(a,b,c)}).then(function(){h(b)})},h=function(b){M.util.js_pending("month-mini-filterChanged"),a("body").trigger(c.filterChanged,{type:b.eventtype,hidden:b.hidden}),M.util.js_complete("month-mini-filterChanged")},i=function(a){return{eventtype:a.data("eventtype"),hidden:a.data("eventtype-hidden")}};return{init:function(b){b=a(b),f(b)}}});
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/modal_event_form","core_calendar/repository","core_calendar/events","core_calendar/modal_delete","core_calendar/selectors"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){function n(d,e,f){var i=[{key:"deleteevent",component:"calendar"}];f=parseInt(f,10);var m,n=f>1;n?(i.push({key:"confirmeventseriesdelete",component:"calendar",param:{name:e,count:f}}),m=g.create({type:l.TYPE})):(i.push({key:"confirmeventdelete",component:"calendar",param:e}),m=g.create({type:g.types.SAVE_CANCEL})),m.then(function(a){a.show()}).fail(c.exception);var o=b.get_strings(i),p=a.when(o,m).then(function(b,e){return e.setTitle(b[0]),e.setBody(b[1]),n||e.setSaveButtonText(b[0]),e.getRoot().on(h.save,function(){j.deleteEvent(d,!1).then(function(){a("body").trigger(k.deleted,[d,!1])})["catch"](c.exception)}),e.getRoot().on(k.deleteAll,function(){j.deleteEvent(d,!0).then(function(){a("body").trigger(k.deleted,[d,!0])})["catch"](c.exception)}),e}).fail(c.exception);return p}function o(b){b.on("click",m.actions.remove,function(b){var c=a(this).closest(m.eventItem),d=c.data("eventId"),e=c.data("eventTitle"),f=c.data("eventCount");n(d,e,f),b.preventDefault()})}function p(b,d){return d.then(function(c){a("body").on(k.editEvent,function(a,d){var e=b.find(m.wrapper);c.setEventId(d),c.setContextId(e.data("contextId")),c.show(),a.stopImmediatePropagation()})}).fail(c.exception),d}var q=function(b){var d=g.create({type:i.TYPE,large:!0});return b.on("click",m.actions.create,function(a){d.then(function(a){var c=b.find(m.wrapper),d=c.data("categoryid");"undefined"!=typeof d&&a.setCategoryId(d);var e=b.find(m.today),f=b.find(m.day);!e.length&&f.length&&a.setStartTime(f.data("newEventTimestamp")),a.setContextId(c.data("contextId")),a.setCourseId(c.data("courseid")),a.show()}).fail(c.exception),a.preventDefault()}),b.on("click",m.actions.edit,function(b){b.preventDefault();var e=a(b.currentTarget),f=e.closest(m.wrapper),g=e.closest(m.eventItem);d.then(function(a){a.setEventId(g.data("eventId")),a.setContextId(f.data("contextId")),a.show(),b.stopImmediatePropagation()}).fail(c.exception)}),d};return{registerRemove:o,registerEditListeners:p,registerEventFormModal:q}});
|
||||
define(["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/modal_event_form","core_calendar/repository","core_calendar/events","core_calendar/modal_delete","core_calendar/selectors","core/pending"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){function o(d,e,f){var i=[{key:"deleteevent",component:"calendar"}];f=parseInt(f,10);var m,o=f>1;o?(i.push({key:"confirmeventseriesdelete",component:"calendar",param:{name:e,count:f}}),m=g.create({type:l.TYPE})):(i.push({key:"confirmeventdelete",component:"calendar",param:e}),m=g.create({type:g.types.SAVE_CANCEL}));var p=b.get_strings(i),q=a.when(p,m).then(function(b,e){return e.setTitle(b[0]),e.setBody(b[1]),o||e.setSaveButtonText(b[0]),e.show(),e.getRoot().on(h.save,function(){var b=new n("calendar/crud:initModal:deletedevent");j.deleteEvent(d,!1).then(function(){a("body").trigger(k.deleted,[d,!1])}).then(b.resolve)["catch"](c.exception)}),e.getRoot().on(k.deleteAll,function(){var b=new n("calendar/crud:initModal:deletedallevent");j.deleteEvent(d,!0).then(function(){a("body").trigger(k.deleted,[d,!0])}).then(b.resolve)["catch"](c.exception)}),e})["catch"](c.exception);return q}function p(b){b.on("click",m.actions.remove,function(b){var c=a(this).closest(m.eventItem),d=c.data("eventId"),e=c.data("eventTitle"),f=c.data("eventCount");o(d,e,f),b.preventDefault()})}function q(b,d){return d.then(function(c){a("body").on(k.editEvent,function(a,d){var e=b.find(m.wrapper);c.setEventId(d),c.setContextId(e.data("contextId")),c.show(),a.stopImmediatePropagation()})}).fail(c.exception),d}var r=function(b){var d=g.create({type:i.TYPE,large:!0});return b.on("click",m.actions.create,function(a){d.then(function(a){var c=b.find(m.wrapper),d=c.data("categoryid");"undefined"!=typeof d&&a.setCategoryId(d);var e=b.find(m.today),f=b.find(m.day);!e.length&&f.length&&a.setStartTime(f.data("newEventTimestamp")),a.setContextId(c.data("contextId")),a.setCourseId(c.data("courseid")),a.show()}).fail(c.exception),a.preventDefault()}),b.on("click",m.actions.edit,function(b){b.preventDefault();var e=a(b.currentTarget),f=e.closest(m.wrapper),g=e.closest(m.eventItem);d.then(function(a){a.setEventId(g.data("eventId")),a.setContextId(f.data("contextId")),a.show(),b.stopImmediatePropagation()}).fail(c.exception)}),d};return{registerRemove:p,registerEditListeners:q,registerEventFormModal:r}});
|
||||
@@ -67,6 +67,9 @@ function(
|
||||
return Str.get_string('eventtype' + data.eventtype, 'calendar')
|
||||
.then(function(nameStr) {
|
||||
data.name = nameStr;
|
||||
data.icon = true;
|
||||
data.key = 'i/' + data.eventtype + 'event';
|
||||
data.component = 'core';
|
||||
|
||||
return data;
|
||||
})
|
||||
|
||||
@@ -35,6 +35,7 @@ define([
|
||||
'core_calendar/events',
|
||||
'core_calendar/modal_delete',
|
||||
'core_calendar/selectors',
|
||||
'core/pending',
|
||||
],
|
||||
function(
|
||||
$,
|
||||
@@ -49,7 +50,8 @@ function(
|
||||
CalendarRepository,
|
||||
CalendarEvents,
|
||||
ModalDelete,
|
||||
CalendarSelectors
|
||||
CalendarSelectors,
|
||||
Pending
|
||||
) {
|
||||
|
||||
/**
|
||||
@@ -101,13 +103,6 @@ function(
|
||||
);
|
||||
}
|
||||
|
||||
deletePromise.then(function(deleteModal) {
|
||||
deleteModal.show();
|
||||
|
||||
return;
|
||||
})
|
||||
.fail(Notification.exception);
|
||||
|
||||
var stringsPromise = Str.get_strings(deleteStrings);
|
||||
|
||||
var finalPromise = $.when(stringsPromise, deletePromise)
|
||||
@@ -118,27 +113,33 @@ function(
|
||||
deleteModal.setSaveButtonText(strings[0]);
|
||||
}
|
||||
|
||||
deleteModal.show();
|
||||
|
||||
deleteModal.getRoot().on(ModalEvents.save, function() {
|
||||
var pendingPromise = new Pending('calendar/crud:initModal:deletedevent');
|
||||
CalendarRepository.deleteEvent(eventId, false)
|
||||
.then(function() {
|
||||
$('body').trigger(CalendarEvents.deleted, [eventId, false]);
|
||||
return;
|
||||
})
|
||||
.then(pendingPromise.resolve)
|
||||
.catch(Notification.exception);
|
||||
});
|
||||
|
||||
deleteModal.getRoot().on(CalendarEvents.deleteAll, function() {
|
||||
var pendingPromise = new Pending('calendar/crud:initModal:deletedallevent');
|
||||
CalendarRepository.deleteEvent(eventId, true)
|
||||
.then(function() {
|
||||
$('body').trigger(CalendarEvents.deleted, [eventId, true]);
|
||||
return;
|
||||
})
|
||||
.then(pendingPromise.resolve)
|
||||
.catch(Notification.exception);
|
||||
});
|
||||
|
||||
return deleteModal;
|
||||
})
|
||||
.fail(Notification.exception);
|
||||
.catch(Notification.exception);
|
||||
|
||||
return finalPromise;
|
||||
}
|
||||
|
||||
@@ -70,11 +70,13 @@ class create_update_form_mapper implements create_update_form_mapper_interface {
|
||||
'format' => $data->format
|
||||
];
|
||||
|
||||
// We don't want to return the context because it's not a
|
||||
// form value and breaks the validation.
|
||||
// Don't return context or subscription because they're not form values and break validation.
|
||||
if (isset($data->context)) {
|
||||
unset($data->context);
|
||||
}
|
||||
if (isset($data->subscription)) {
|
||||
unset($data->subscription);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
+5
-2
@@ -2203,8 +2203,8 @@ function calendar_view_event_allowed(calendar_event $event) {
|
||||
if (has_capability('moodle/calendar:manageentries', $event->context)) {
|
||||
return true;
|
||||
}
|
||||
$mycourses = enrol_get_my_courses('id');
|
||||
return isset($mycourses[$event->courseid]);
|
||||
|
||||
return can_access_course(get_course($event->courseid));
|
||||
} else if ($event->userid) {
|
||||
if ($event->userid != $USER->id) {
|
||||
// No-one can ever see another users events.
|
||||
@@ -3661,6 +3661,9 @@ function calendar_get_filter_types() {
|
||||
return [
|
||||
'eventtype' => $type,
|
||||
'name' => get_string("eventtype{$type}", "calendar"),
|
||||
'icon' => true,
|
||||
'key' => 'i/'.$type.'event',
|
||||
'component' => 'core'
|
||||
];
|
||||
}, $types);
|
||||
}
|
||||
|
||||
@@ -127,7 +127,8 @@ $searches = [];
|
||||
$params = [];
|
||||
|
||||
$usedefaultfilters = true;
|
||||
if (!empty($courseid) && $courseid == SITEID && !empty($types['site'])) {
|
||||
|
||||
if (!empty($types['site'])) {
|
||||
$searches[] = "(eventtype = 'site')";
|
||||
$usedefaultfilters = false;
|
||||
}
|
||||
@@ -144,9 +145,14 @@ if (!empty($courseid) && !empty($types['course'])) {
|
||||
$usedefaultfilters = false;
|
||||
}
|
||||
|
||||
if (!empty($categoryid) && !empty($types['category'])) {
|
||||
$searches[] = "(eventtype = 'category' AND categoryid = :categoryid)";
|
||||
$params += ['categoryid' => $categoryid];
|
||||
if (!empty($types['category'])) {
|
||||
if (!empty($categoryid)) {
|
||||
$searches[] = "(eventtype = 'category' AND categoryid = :categoryid)";
|
||||
$params += ['categoryid' => $categoryid];
|
||||
} else {
|
||||
$searches[] = "(eventtype = 'category')";
|
||||
}
|
||||
|
||||
$usedefaultfilters = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,5 +37,5 @@
|
||||
{{#pix}} icon, {{modulename}} {{/pix}}
|
||||
{{/modulename}}
|
||||
{{^modulename}}
|
||||
{{#pix}} i/{{eventtype}}event, core {{/pix}}
|
||||
{{#icon}}{{#pix}} {{key}}, {{component}}, {{alttext}} {{/pix}}{{/icon}}
|
||||
{{/modulename}}
|
||||
|
||||
@@ -136,12 +136,7 @@
|
||||
{{$content}}
|
||||
{{#events}}
|
||||
<div data-popover-eventtype-{{calendareventtype}}="1">
|
||||
{{#modulename}}
|
||||
{{#pix}} icon, {{modulename}} {{/pix}}
|
||||
{{/modulename}}
|
||||
{{^modulename}}
|
||||
{{#pix}} i/{{eventtype}}event, core {{/pix}}
|
||||
{{/modulename}}
|
||||
{{> core_calendar/event_icon}}
|
||||
{{{popupname}}}
|
||||
</div>
|
||||
{{/events}}
|
||||
|
||||
@@ -47,3 +47,30 @@ Feature: Import and edit calendar events
|
||||
And I view the calendar for "2" "2017"
|
||||
And I should not see "Event on 2-25-2017"
|
||||
And I should not see "Event on 2-20-2017"
|
||||
|
||||
Scenario: Import events using different event types.
|
||||
Given I log in as "admin"
|
||||
And I view the calendar for "1" "2016"
|
||||
And I press "Manage subscriptions"
|
||||
And I set the following fields to these values:
|
||||
| Calendar name | Test Import |
|
||||
| Import from | Calendar file (.ics) |
|
||||
| Type of event | User |
|
||||
And I upload "calendar/tests/fixtures/import.ics" file to "Calendar file (.ics)" filemanager
|
||||
And I press "Add"
|
||||
And I should see "User events"
|
||||
And I set the following fields to these values:
|
||||
| Calendar name | Test Import |
|
||||
| Import from | Calendar file (.ics) |
|
||||
| Type of event | Category |
|
||||
| Category | Miscellaneous |
|
||||
And I upload "calendar/tests/fixtures/import.ics" file to "Calendar file (.ics)" filemanager
|
||||
And I press "Add"
|
||||
And I should see "Category events"
|
||||
And I set the following fields to these values:
|
||||
| Calendar name | Test Import |
|
||||
| Import from | Calendar file (.ics) |
|
||||
| Type of event | Site |
|
||||
And I upload "calendar/tests/fixtures/import.ics" file to "Calendar file (.ics)" filemanager
|
||||
And I press "Add"
|
||||
And I should see "Site events"
|
||||
|
||||
@@ -772,4 +772,72 @@ class core_calendar_lib_testcase extends advanced_testcase {
|
||||
$this->assertEquals(array($coursegroups[$courses[0]->id][1]->id), $groupids);
|
||||
$this->assertEquals($users[1]->id, $userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for calendar_view_event_allowed for course event types.
|
||||
*/
|
||||
public function test_calendar_view_event_allowed_course_event() {
|
||||
global $USER;
|
||||
|
||||
$this->setAdminUser();
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
// A student in a course.
|
||||
$student = $generator->create_user();
|
||||
// Some user not enrolled in any course.
|
||||
$someuser = $generator->create_user();
|
||||
|
||||
// A course with manual enrolments.
|
||||
$manualcourse = $generator->create_course();
|
||||
|
||||
// Enrol the student to the manual enrolment course.
|
||||
$generator->enrol_user($student->id, $manualcourse->id);
|
||||
|
||||
// A course that allows guest access.
|
||||
$guestcourse = $generator->create_course(
|
||||
(object)[
|
||||
'shortname' => 'guestcourse',
|
||||
'enrol_guest_status_0' => ENROL_INSTANCE_ENABLED,
|
||||
'enrol_guest_password_0' => ''
|
||||
]);
|
||||
|
||||
$manualevent = (object)[
|
||||
'name' => 'Manual course event',
|
||||
'description' => '',
|
||||
'format' => 1,
|
||||
'categoryid' => 0,
|
||||
'courseid' => $manualcourse->id,
|
||||
'groupid' => 0,
|
||||
'userid' => $USER->id,
|
||||
'modulename' => 0,
|
||||
'instance' => 0,
|
||||
'eventtype' => 'course',
|
||||
'timestart' => time(),
|
||||
'timeduration' => 86400,
|
||||
'visible' => 1
|
||||
];
|
||||
$caleventmanual = calendar_event::create($manualevent, false);
|
||||
|
||||
// Create a course event for the course with guest access.
|
||||
$guestevent = clone $manualevent;
|
||||
$guestevent->name = 'Guest course event';
|
||||
$guestevent->courseid = $guestcourse->id;
|
||||
$caleventguest = calendar_event::create($guestevent, false);
|
||||
|
||||
// Viewing as admin.
|
||||
$this->assertTrue(calendar_view_event_allowed($caleventmanual));
|
||||
$this->assertTrue(calendar_view_event_allowed($caleventguest));
|
||||
|
||||
// Viewing as someone enrolled in a course.
|
||||
$this->setUser($student);
|
||||
$this->assertTrue(calendar_view_event_allowed($caleventmanual));
|
||||
|
||||
// Viewing as someone not enrolled in any course.
|
||||
$this->setUser($someuser);
|
||||
// Viewing as someone not enrolled in a course without guest access on.
|
||||
$this->assertFalse(calendar_view_event_allowed($caleventmanual));
|
||||
// Viewing as someone not enrolled in a course with guest access on.
|
||||
$this->assertTrue(calendar_view_event_allowed($caleventguest));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "6.5.*",
|
||||
"phpunit/dbunit": "3.0.*",
|
||||
"moodlehq/behat-extension": "3.36.1",
|
||||
"moodlehq/behat-extension": "3.36.3",
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"instaclick/php-webdriver": "dev-local as 1.x-dev"
|
||||
}
|
||||
|
||||
Generated
+111
-112
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "c5f05bdc99eb03364db93ed9b7f6d17e",
|
||||
"content-hash": "6333f2093f75686fe706fe129cc5d8c5",
|
||||
"packages": [],
|
||||
"packages-dev": [
|
||||
{
|
||||
@@ -619,27 +619,28 @@
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "6.3.3",
|
||||
"version": "6.4.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba"
|
||||
"reference": "0895c932405407fd3a7368b6910c09a24d26db11"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba",
|
||||
"reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/0895c932405407fd3a7368b6910c09a24d26db11",
|
||||
"reference": "0895c932405407fd3a7368b6910c09a24d26db11",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/promises": "^1.0",
|
||||
"guzzlehttp/psr7": "^1.4",
|
||||
"guzzlehttp/psr7": "^1.6.1",
|
||||
"php": ">=5.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-curl": "*",
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
|
||||
"psr/log": "^1.0"
|
||||
"psr/log": "^1.1"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "Required for using the Log middleware"
|
||||
@@ -651,12 +652,12 @@
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\": "src/"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -680,7 +681,7 @@
|
||||
"rest",
|
||||
"web service"
|
||||
],
|
||||
"time": "2018-04-22T15:46:56+00:00"
|
||||
"time": "2019-10-23T15:58:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
@@ -903,8 +904,8 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frank Kleine",
|
||||
"role": "Developer",
|
||||
"homepage": "http://frankkleine.de/"
|
||||
"homepage": "http://frankkleine.de/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Virtual file system to mock the real file system in unit tests.",
|
||||
@@ -913,16 +914,16 @@
|
||||
},
|
||||
{
|
||||
"name": "moodlehq/behat-extension",
|
||||
"version": "v3.36.1",
|
||||
"version": "v3.36.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/moodlehq/moodle-behat-extension.git",
|
||||
"reference": "a3c38c2864e7259b1de834218abfe49eecc03417"
|
||||
"reference": "3bee9012d8818dd24c72cc7a6614dea7ccf9c1fb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/moodlehq/moodle-behat-extension/zipball/a3c38c2864e7259b1de834218abfe49eecc03417",
|
||||
"reference": "a3c38c2864e7259b1de834218abfe49eecc03417",
|
||||
"url": "https://api.github.com/repos/moodlehq/moodle-behat-extension/zipball/3bee9012d8818dd24c72cc7a6614dea7ccf9c1fb",
|
||||
"reference": "3bee9012d8818dd24c72cc7a6614dea7ccf9c1fb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -931,7 +932,7 @@
|
||||
"behat/mink-extension": "~2.2",
|
||||
"behat/mink-goutte-driver": "~1.2",
|
||||
"behat/mink-selenium2-driver": "~1.3",
|
||||
"php": ">=5.4.4",
|
||||
"php": ">=7.0.0",
|
||||
"symfony/process": "2.8.*"
|
||||
},
|
||||
"type": "library",
|
||||
@@ -947,9 +948,9 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Monllaó",
|
||||
"role": "Developer",
|
||||
"email": "david.monllao@gmail.com",
|
||||
"homepage": "http://moodle.com"
|
||||
"homepage": "http://moodle.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Moodle behat extension",
|
||||
@@ -958,7 +959,7 @@
|
||||
"Behat",
|
||||
"moodle"
|
||||
],
|
||||
"time": "2019-07-18T08:33:39+00:00"
|
||||
"time": "2019-10-28T11:42:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
@@ -1043,18 +1044,18 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Arne Blankerts",
|
||||
"role": "Developer",
|
||||
"email": "arne@blankerts.de"
|
||||
"email": "arne@blankerts.de",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Sebastian Heuer",
|
||||
"role": "Developer",
|
||||
"email": "sebastian@phpeople.de"
|
||||
"email": "sebastian@phpeople.de",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"role": "Developer",
|
||||
"email": "sebastian@phpunit.de"
|
||||
"email": "sebastian@phpunit.de",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
|
||||
@@ -1090,18 +1091,18 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Arne Blankerts",
|
||||
"role": "Developer",
|
||||
"email": "arne@blankerts.de"
|
||||
"email": "arne@blankerts.de",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Sebastian Heuer",
|
||||
"role": "Developer",
|
||||
"email": "sebastian@phpeople.de"
|
||||
"email": "sebastian@phpeople.de",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"role": "Developer",
|
||||
"email": "sebastian@phpunit.de"
|
||||
"email": "sebastian@phpunit.de",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Library for handling version information and constraints",
|
||||
@@ -1163,26 +1164,26 @@
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-docblock",
|
||||
"version": "4.3.1",
|
||||
"version": "4.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
|
||||
"reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c"
|
||||
"reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c",
|
||||
"reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
|
||||
"reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0",
|
||||
"phpdocumentor/reflection-common": "^1.0.0",
|
||||
"phpdocumentor/type-resolver": "^0.4.0",
|
||||
"phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0",
|
||||
"phpdocumentor/type-resolver": "~0.4 || ^1.0.0",
|
||||
"webmozart/assert": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/instantiator": "~1.0.5",
|
||||
"doctrine/instantiator": "^1.0.5",
|
||||
"mockery/mockery": "^1.0",
|
||||
"phpunit/phpunit": "^6.4"
|
||||
},
|
||||
@@ -1210,29 +1211,29 @@
|
||||
}
|
||||
],
|
||||
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
|
||||
"time": "2019-04-30T17:48:53+00:00"
|
||||
"time": "2019-09-12T14:27:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
"version": "0.4.0",
|
||||
"version": "0.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||
"reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
|
||||
"reference": "cf842904952e64e703800d094cdf34e715a8a3ae"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
|
||||
"reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/cf842904952e64e703800d094cdf34e715a8a3ae",
|
||||
"reference": "cf842904952e64e703800d094cdf34e715a8a3ae",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.5 || ^7.0",
|
||||
"php": "^7.0",
|
||||
"phpdocumentor/reflection-common": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^0.9.4",
|
||||
"phpunit/phpunit": "^5.2||^4.8.24"
|
||||
"mockery/mockery": "^1.0",
|
||||
"phpunit/phpunit": "^6.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@@ -1242,9 +1243,7 @@
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"phpDocumentor\\Reflection\\": [
|
||||
"src/"
|
||||
]
|
||||
"phpDocumentor\\Reflection\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -1257,26 +1256,26 @@
|
||||
"email": "me@mikevanriel.com"
|
||||
}
|
||||
],
|
||||
"time": "2017-07-14T14:27:02+00:00"
|
||||
"time": "2017-12-30T13:23:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
"version": "1.8.1",
|
||||
"version": "1.9.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpspec/prophecy.git",
|
||||
"reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76"
|
||||
"reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
|
||||
"reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203",
|
||||
"reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.0.2",
|
||||
"php": "^5.3|^7.0",
|
||||
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
|
||||
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
|
||||
"sebastian/comparator": "^1.1|^2.0|^3.0",
|
||||
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
|
||||
},
|
||||
@@ -1320,7 +1319,7 @@
|
||||
"spy",
|
||||
"stub"
|
||||
],
|
||||
"time": "2019-06-13T12:50:23+00:00"
|
||||
"time": "2019-10-03T11:07:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/dbunit",
|
||||
@@ -1425,8 +1424,8 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"role": "lead",
|
||||
"email": "sebastian@phpunit.de"
|
||||
"email": "sebastian@phpunit.de",
|
||||
"role": "lead"
|
||||
}
|
||||
],
|
||||
"description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
|
||||
@@ -1473,8 +1472,8 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"role": "lead",
|
||||
"email": "sb@sebastian-bergmann.de"
|
||||
"email": "sb@sebastian-bergmann.de",
|
||||
"role": "lead"
|
||||
}
|
||||
],
|
||||
"description": "FilterIterator implementation that filters files based on a list of suffixes.",
|
||||
@@ -1515,8 +1514,8 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"role": "lead",
|
||||
"email": "sebastian@phpunit.de"
|
||||
"email": "sebastian@phpunit.de",
|
||||
"role": "lead"
|
||||
}
|
||||
],
|
||||
"description": "Simple template engine.",
|
||||
@@ -1564,8 +1563,8 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"role": "lead",
|
||||
"email": "sb@sebastian-bergmann.de"
|
||||
"email": "sb@sebastian-bergmann.de",
|
||||
"role": "lead"
|
||||
}
|
||||
],
|
||||
"description": "Utility class for timing",
|
||||
@@ -1695,8 +1694,8 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"role": "lead",
|
||||
"email": "sebastian@phpunit.de"
|
||||
"email": "sebastian@phpunit.de",
|
||||
"role": "lead"
|
||||
}
|
||||
],
|
||||
"description": "The PHP Unit Testing framework.",
|
||||
@@ -1869,16 +1868,16 @@
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/log.git",
|
||||
"reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
|
||||
"reference": "bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
|
||||
"reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2",
|
||||
"reference": "bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1887,7 +1886,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1912,7 +1911,7 @@
|
||||
"psr",
|
||||
"psr-3"
|
||||
],
|
||||
"time": "2018-11-20T15:27:04+00:00"
|
||||
"time": "2019-10-25T08:06:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ralouphie/getallheaders",
|
||||
@@ -2167,16 +2166,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/exporter",
|
||||
"version": "3.1.1",
|
||||
"version": "3.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/exporter.git",
|
||||
"reference": "06a9a5947f47b3029d76118eb5c22802e5869687"
|
||||
"reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/06a9a5947f47b3029d76118eb5c22802e5869687",
|
||||
"reference": "06a9a5947f47b3029d76118eb5c22802e5869687",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e",
|
||||
"reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2230,7 +2229,7 @@
|
||||
"export",
|
||||
"exporter"
|
||||
],
|
||||
"time": "2019-08-11T12:43:14+00:00"
|
||||
"time": "2019-09-14T09:02:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/global-state",
|
||||
@@ -2505,8 +2504,8 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"role": "lead",
|
||||
"email": "sebastian@phpunit.de"
|
||||
"email": "sebastian@phpunit.de",
|
||||
"role": "lead"
|
||||
}
|
||||
],
|
||||
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
|
||||
@@ -2515,16 +2514,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/browser-kit",
|
||||
"version": "v3.4.31",
|
||||
"version": "v3.4.32",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/browser-kit.git",
|
||||
"reference": "1a3406a6b9b61b492592a816ca056afcc9e976c0"
|
||||
"reference": "abe4bf2c934ddd1fd490a7d9147df7827b5fff0f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/1a3406a6b9b61b492592a816ca056afcc9e976c0",
|
||||
"reference": "1a3406a6b9b61b492592a816ca056afcc9e976c0",
|
||||
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/abe4bf2c934ddd1fd490a7d9147df7827b5fff0f",
|
||||
"reference": "abe4bf2c934ddd1fd490a7d9147df7827b5fff0f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2568,11 +2567,11 @@
|
||||
],
|
||||
"description": "Symfony BrowserKit Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-08-26T07:52:58+00:00"
|
||||
"time": "2019-09-10T10:13:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/class-loader",
|
||||
"version": "v3.4.31",
|
||||
"version": "v3.4.32",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/class-loader.git",
|
||||
@@ -2628,16 +2627,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/config",
|
||||
"version": "v3.4.31",
|
||||
"version": "v3.4.32",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/config.git",
|
||||
"reference": "24a60c0d7ad98a0fa5d1f892e9286095a389404f"
|
||||
"reference": "717ad66b5257e9752ae3c5722b5810bb4c40b236"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/config/zipball/24a60c0d7ad98a0fa5d1f892e9286095a389404f",
|
||||
"reference": "24a60c0d7ad98a0fa5d1f892e9286095a389404f",
|
||||
"url": "https://api.github.com/repos/symfony/config/zipball/717ad66b5257e9752ae3c5722b5810bb4c40b236",
|
||||
"reference": "717ad66b5257e9752ae3c5722b5810bb4c40b236",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2688,7 +2687,7 @@
|
||||
],
|
||||
"description": "Symfony Config Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-08-26T07:52:57+00:00"
|
||||
"time": "2019-09-19T15:32:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
@@ -2760,16 +2759,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
"version": "v3.4.31",
|
||||
"version": "v3.4.32",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/css-selector.git",
|
||||
"reference": "e18c5c4b35e7f17513448a25d02f7af34a4bdb41"
|
||||
"reference": "f819f71ae3ba6f396b4c015bd5895de7d2f1f85f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/e18c5c4b35e7f17513448a25d02f7af34a4bdb41",
|
||||
"reference": "e18c5c4b35e7f17513448a25d02f7af34a4bdb41",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/f819f71ae3ba6f396b4c015bd5895de7d2f1f85f",
|
||||
"reference": "f819f71ae3ba6f396b4c015bd5895de7d2f1f85f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2809,20 +2808,20 @@
|
||||
],
|
||||
"description": "Symfony CssSelector Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-08-20T13:31:17+00:00"
|
||||
"time": "2019-10-01T11:57:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/debug",
|
||||
"version": "v3.4.31",
|
||||
"version": "v3.4.32",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/debug.git",
|
||||
"reference": "0b600300918780001e2821db77bc28b677794486"
|
||||
"reference": "b3e7ce815d82196435d16dc458023f8fb6b36ceb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/0b600300918780001e2821db77bc28b677794486",
|
||||
"reference": "0b600300918780001e2821db77bc28b677794486",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/b3e7ce815d82196435d16dc458023f8fb6b36ceb",
|
||||
"reference": "b3e7ce815d82196435d16dc458023f8fb6b36ceb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2865,7 +2864,7 @@
|
||||
],
|
||||
"description": "Symfony Debug Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-08-20T13:31:17+00:00"
|
||||
"time": "2019-09-19T15:32:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/dependency-injection",
|
||||
@@ -2939,16 +2938,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/dom-crawler",
|
||||
"version": "v3.4.31",
|
||||
"version": "v3.4.32",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/dom-crawler.git",
|
||||
"reference": "8558d1bc4554f5cb0b66e50377457967a8969263"
|
||||
"reference": "29cffc38a38f2a8ed7e494c9cea2f890a40c2359"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/8558d1bc4554f5cb0b66e50377457967a8969263",
|
||||
"reference": "8558d1bc4554f5cb0b66e50377457967a8969263",
|
||||
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/29cffc38a38f2a8ed7e494c9cea2f890a40c2359",
|
||||
"reference": "29cffc38a38f2a8ed7e494c9cea2f890a40c2359",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2992,11 +2991,11 @@
|
||||
],
|
||||
"description": "Symfony DomCrawler Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-08-26T07:52:58+00:00"
|
||||
"time": "2019-08-30T17:42:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v3.4.31",
|
||||
"version": "v3.4.32",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
@@ -3059,7 +3058,7 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v3.4.31",
|
||||
"version": "v3.4.32",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
@@ -3426,8 +3425,8 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Arne Blankerts",
|
||||
"role": "Developer",
|
||||
"email": "arne@blankerts.de"
|
||||
"email": "arne@blankerts.de",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
|
||||
|
||||
@@ -815,6 +815,10 @@ $CFG->admin = 'admin';
|
||||
// Example:
|
||||
// $CFG->behat_faildump_path = '/my/path/to/save/failure/dumps';
|
||||
//
|
||||
// You can make behat pause upon failure to help you diagnose and debug problems with your tests.
|
||||
//
|
||||
// $CFG->behat_pause_on_fail = true;
|
||||
//
|
||||
// You can specify db, selenium wd_host etc. for behat parallel run by setting following variable.
|
||||
// Example:
|
||||
// $CFG->behat_parallel_run = array (
|
||||
|
||||
@@ -103,6 +103,9 @@ class course_summary_exporter extends \core\external\exporter {
|
||||
),
|
||||
'enddate' => array(
|
||||
'type' => PARAM_INT,
|
||||
),
|
||||
'visible' => array(
|
||||
'type' => PARAM_BOOL,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
+3
-5
@@ -166,11 +166,9 @@ if ($editform->is_cancelled()) {
|
||||
|
||||
if (!empty($CFG->creatornewroleid) and !is_viewing($context, NULL, 'moodle/role:assign') and !is_enrolled($context, NULL, 'moodle/role:assign')) {
|
||||
// Deal with course creators - enrol them internally with default role.
|
||||
if (user_can_assign($context, $CFG->creatornewroleid)) {
|
||||
enrol_try_internal_enrol($course->id, $USER->id, $CFG->creatornewroleid);
|
||||
} else {
|
||||
enrol_try_internal_enrol($course->id, $USER->id);
|
||||
}
|
||||
// Note: This does not respect capabilities, the creator will be assigned the default role.
|
||||
// This is an expected behaviour. See MDL-66683 for further details.
|
||||
enrol_try_internal_enrol($course->id, $USER->id, $CFG->creatornewroleid);
|
||||
}
|
||||
|
||||
// The URL to take them to if they chose save and display.
|
||||
|
||||
@@ -84,7 +84,14 @@ if ($deletesection) {
|
||||
}
|
||||
}
|
||||
|
||||
$editoroptions = array('context'=>$context ,'maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes'=>$CFG->maxbytes, 'trusttext'=>false, 'noclean'=>true);
|
||||
$editoroptions = array(
|
||||
'context' => $context,
|
||||
'maxfiles' => EDITOR_UNLIMITED_FILES,
|
||||
'maxbytes' => $CFG->maxbytes,
|
||||
'trusttext' => false,
|
||||
'noclean' => true,
|
||||
'subdirs' => true
|
||||
);
|
||||
|
||||
$courseformat = course_get_format($course);
|
||||
$defaultsectionname = $courseformat->get_default_section_name($section);
|
||||
|
||||
@@ -3586,7 +3586,7 @@ class core_course_external extends external_api {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return structure for edit_module()
|
||||
* Return structure for get_module()
|
||||
*
|
||||
* @since Moodle 3.3
|
||||
* @return external_description
|
||||
|
||||
+1
-1
@@ -4577,7 +4577,7 @@ function course_get_recent_courses(int $userid = null, int $limit = 0, int $offs
|
||||
}
|
||||
|
||||
$basefields = array('id', 'idnumber', 'summary', 'summaryformat', 'startdate', 'enddate', 'category',
|
||||
'shortname', 'fullname', 'timeaccess', 'component');
|
||||
'shortname', 'fullname', 'timeaccess', 'component', 'visible');
|
||||
|
||||
$sort = trim($sort);
|
||||
if (empty($sort)) {
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
"courseimage": "https://moodlesite/pluginfile/123/course/overviewfiles/123.jpg",
|
||||
"fullname": "course 3",
|
||||
"hasprogress": true,
|
||||
"progress": 10
|
||||
"progress": 10,
|
||||
"visible": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -63,6 +64,11 @@
|
||||
</a>
|
||||
{{$menu}}{{/menu}}
|
||||
</div>
|
||||
{{^visible}}
|
||||
<div class="hiddenmessage">
|
||||
<span class="badge badge-info">{{#str}} hiddenfromstudents {{/str}}</span>
|
||||
</div>
|
||||
{{/visible}}
|
||||
</div>
|
||||
{{$progress}}{{/progress}}
|
||||
</div>
|
||||
|
||||
@@ -1123,11 +1123,8 @@ class behat_course extends behat_base {
|
||||
protected function get_activity_element($element, $selectortype, $activityname) {
|
||||
$activitynode = $this->get_activity_node($activityname);
|
||||
|
||||
// Transforming to Behat selector/locator.
|
||||
list($selector, $locator) = $this->transform_selector($selectortype, $element);
|
||||
$exception = new ElementNotFoundException($this->getSession(), '"' . $element . '" "' . $selectortype . '" in "' . $activityname . '" ');
|
||||
|
||||
return $this->find($selector, $locator, $exception, $activitynode);
|
||||
$exception = new ElementNotFoundException($this->getSession(), "'{$element}' '{$selectortype}' in '${activityname}'");
|
||||
return $this->find($selectortype, $element, $exception, $activitynode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -85,7 +85,6 @@ Feature: Managers can create courses
|
||||
| moodle/course:create | Allow |
|
||||
| moodle/course:manageactivities | Allow |
|
||||
| moodle/course:viewparticipants | Allow |
|
||||
| moodle/role:assign | Allow |
|
||||
And I log out
|
||||
And I log in as "kevin"
|
||||
And I am on site homepage
|
||||
@@ -96,20 +95,4 @@ Feature: Managers can create courses
|
||||
And I press "Save and display"
|
||||
And I follow "Participants"
|
||||
Then I should see "Kevin the"
|
||||
And I should not see "Teacher"
|
||||
And I log out
|
||||
Given I log in as "admin"
|
||||
And I define the allowed role assignments for the "Creator" role as:
|
||||
| Teacher | Assignable |
|
||||
And I log out
|
||||
And I log in as "kevin"
|
||||
And I am on site homepage
|
||||
And I turn editing mode on
|
||||
When I press "Add a new course"
|
||||
And I set the following fields to these values:
|
||||
| Course full name | My second course |
|
||||
| Course short name | mysecondcourse |
|
||||
And I press "Save and display"
|
||||
And I follow "Participants"
|
||||
Then I should see "Kevin the"
|
||||
And I should see "Teacher"
|
||||
|
||||
@@ -993,6 +993,70 @@ class core_enrol_external extends external_api {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of submit_user_enrolment_form parameters.
|
||||
*
|
||||
* @return external_function_parameters.
|
||||
*/
|
||||
public static function submit_user_enrolment_form_parameters() {
|
||||
return new external_function_parameters([
|
||||
'formdata' => new external_value(PARAM_RAW, 'The data from the event form'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* External function that handles the user enrolment form submission.
|
||||
*
|
||||
* @param string $formdata The user enrolment form data in s URI encoded param string
|
||||
* @return array An array consisting of the processing result and error flag, if available
|
||||
*/
|
||||
public static function submit_user_enrolment_form($formdata) {
|
||||
global $CFG, $DB, $PAGE;
|
||||
|
||||
// Parameter validation.
|
||||
$params = self::validate_parameters(self::submit_user_enrolment_form_parameters(), ['formdata' => $formdata]);
|
||||
|
||||
$data = [];
|
||||
parse_str($params['formdata'], $data);
|
||||
|
||||
$userenrolment = $DB->get_record('user_enrolments', ['id' => $data['ue']], '*', MUST_EXIST);
|
||||
$instance = $DB->get_record('enrol', ['id' => $userenrolment->enrolid], '*', MUST_EXIST);
|
||||
$plugin = enrol_get_plugin($instance->enrol);
|
||||
$course = get_course($instance->courseid);
|
||||
$context = context_course::instance($course->id);
|
||||
self::validate_context($context);
|
||||
|
||||
require_once("$CFG->dirroot/enrol/editenrolment_form.php");
|
||||
$customformdata = [
|
||||
'ue' => $userenrolment,
|
||||
'modal' => true,
|
||||
'enrolinstancename' => $plugin->get_instance_name($instance)
|
||||
];
|
||||
$mform = new enrol_user_enrolment_form(null, $customformdata, 'post', '', null, true, $data);
|
||||
|
||||
if ($validateddata = $mform->get_data()) {
|
||||
require_once($CFG->dirroot . '/enrol/locallib.php');
|
||||
$manager = new course_enrolment_manager($PAGE, $course);
|
||||
$result = $manager->edit_enrolment($userenrolment, $validateddata);
|
||||
|
||||
return ['result' => $result];
|
||||
} else {
|
||||
return ['result' => false, 'validationerror' => true];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of submit_user_enrolment_form() result value
|
||||
*
|
||||
* @return external_description
|
||||
*/
|
||||
public static function submit_user_enrolment_form_returns() {
|
||||
return new external_single_structure([
|
||||
'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
|
||||
'validationerror' => new external_value(PARAM_BOOL, 'Indicates invalid form data', VALUE_DEFAULT, false),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of unenrol_user_enrolment() parameters
|
||||
*
|
||||
|
||||
@@ -1 +1 @@
|
||||
define(["jquery","core/ajax","core/templates","core/str"],function(a,b,c,d){var e=100;return{processResults:function(b,c){var d=[];return a.isArray(c)?(a.each(c,function(a,b){d.push({value:b.id,label:b._label})}),d):c},transport:function(f,g,h,i){var j,k=a(f).attr("courseid"),l=a(f).attr("userfields").split(",");"undefined"==typeof k&&(k="1");var m=a(f).attr("enrolid");"undefined"==typeof m&&(m=""),j=b.call([{methodname:"core_enrol_get_potential_users",args:{courseid:k,enrolid:m,search:g,searchanywhere:!0,page:0,perpage:e+1}}]),j[0].then(function(b){var f=[],g=0;return b.length<=e?(a.each(b,function(b,d){var e=d,g=[];a.each(l,function(a,b){"undefined"!=typeof d[b]&&""!==d[b]&&(e.hasidentity=!0,g.push(d[b]))}),e.identity=g.join(", "),f.push(c.render("enrol_manual/form-user-selector-suggestion",e))}),a.when.apply(a.when,f).then(function(){var c=arguments;a.each(b,function(a,b){b._label=c[g],g++}),h(b)})):d.get_string("toomanyuserstoshow","core",">"+e).then(function(a){h(a)})}).fail(i)}}});
|
||||
define(["jquery","core/ajax","core/templates","core/str"],function(a,b,c,d){return{processResults:function(b,c){var d=[];return a.isArray(c)?(a.each(c,function(a,b){d.push({value:b.id,label:b._label})}),d):c},transport:function(e,f,g,h){var i,j=a(e).attr("courseid"),k=a(e).attr("userfields").split(",");"undefined"==typeof j&&(j="1");var l=a(e).attr("enrolid");"undefined"==typeof l&&(l="");var m=a(e).attr("perpage");"undefined"==typeof m&&(m=100),i=b.call([{methodname:"core_enrol_get_potential_users",args:{courseid:j,enrolid:l,search:f,searchanywhere:!0,page:0,perpage:m+1}}]),i[0].then(function(b){var e=[],f=0;return b.length<=m?(a.each(b,function(b,d){var f=d,g=[];a.each(k,function(a,b){"undefined"!=typeof d[b]&&""!==d[b]&&(f.hasidentity=!0,g.push(d[b]))}),f.identity=g.join(", "),e.push(c.render("enrol_manual/form-user-selector-suggestion",f))}),a.when.apply(a.when,e).then(function(){var c=arguments;a.each(b,function(a,b){b._label=c[f],f++}),g(b)})):d.get_string("toomanyuserstoshow","core",">"+m).then(function(a){g(a)})}).fail(h)}}});
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["core/templates","jquery","core/str","core/config","core/notification","core/modal_factory","core/modal_events","core/fragment"],function(a,b,c,d,e,f,g,h){var i={COHORTSELECT:"#id_cohortlist",TRIGGERBUTTONS:".enrolusersbutton.enrol_manual_plugin [type='submit']",UNWANTEDHIDDENFIELDS:":input[value='_qf__force_multiselect_submission']"},j=function(a){this.contextid=a.contextid,this.initModal()};return j.prototype.courseid=0,j.prototype.modal=null,j.prototype.initModal=function(){var a=b(i.TRIGGERBUTTONS);b.when(c.get_strings([{key:"enroluserscohorts",component:"enrol_manual"},{key:"enrolusers",component:"enrol_manual"}]),f.create({type:f.types.SAVE_CANCEL,large:!0},a)).then(function(a,c){this.modal=c,c.setTitle(a[1]),c.setSaveButtonText(a[1]),c.getRoot().on(g.save,this.submitForm.bind(this)),c.getRoot().on("submit","form",this.submitFormAjax.bind(this)),c.getRoot().on(g.hidden,function(){c.setBody("")}),c.getRoot().on(g.shown,function(){var d=this.getBody();d.then(function(d){var e=b(d).find(i.COHORTSELECT).length?0:1;c.setSaveButtonText(a[e])}).fail(e.exception),c.setBody(d)}.bind(this))}.bind(this)).fail(e.exception)},j.prototype.submitForm=function(a){a.preventDefault(),this.modal.getRoot().find("form").submit()},j.prototype.submitFormAjax=function(a){a.preventDefault();var c=this.modal.getRoot().find("form"),f=c.find(i.UNWANTEDHIDDENFIELDS);f.each(function(){b(this).remove()});var g=c.serialize();this.modal.hide();var h={type:"GET",processData:!1,contentType:"application/json"},j=d.wwwroot+"/enrol/manual/ajax.php?"+g;b.ajax(j,h).then(function(a){a.error?e.addNotification({message:a.error,type:"error"}):("undefined"!=typeof window.M.core_formchangechecker&&window.M.core_formchangechecker.reset_form_dirty_state(),window.location.reload())}).fail(e.exception)},j.prototype.getBody=function(){return h.loadFragment("enrol_manual","enrol_users_form",this.contextid,{}).fail(e.exception)},j.prototype.getFooter=function(){return a.render("enrol_manual/enrol_modal_footer",{})},{init:function(a){new j(a)}}});
|
||||
define(["core/templates","jquery","core/str","core/config","core/notification","core/modal_factory","core/modal_events","core/fragment","core/pending"],function(a,b,c,d,e,f,g,h,i){var j={COHORTSELECT:"#id_cohortlist",TRIGGERBUTTONS:".enrolusersbutton.enrol_manual_plugin [type='submit']",UNWANTEDHIDDENFIELDS:":input[value='_qf__force_multiselect_submission']"},k=function(a){this.contextid=a.contextid,this.initModal()};return k.prototype.courseid=0,k.prototype.modal=null,k.prototype.initModal=function(){var a=b(j.TRIGGERBUTTONS);b.when(c.get_strings([{key:"enroluserscohorts",component:"enrol_manual"},{key:"enrolusers",component:"enrol_manual"}]),f.create({type:f.types.SAVE_CANCEL,large:!0},a)).then(function(a,c){this.modal=c,c.setTitle(a[1]),c.setSaveButtonText(a[1]),c.getRoot().on(g.save,this.submitForm.bind(this)),c.getRoot().on("submit","form",this.submitFormAjax.bind(this)),c.getRoot().on(g.hidden,function(){c.setBody("")}),c.getRoot().on(g.shown,function(){var d=new i("enrol_manual/quickenrolment:initModal:shown"),f=this.getBody();f.then(function(d){var e=b(d).find(j.COHORTSELECT).length?0:1;c.setSaveButtonText(a[e])}).then(d.resolve)["catch"](e.exception),c.setBody(f)}.bind(this))}.bind(this)).fail(e.exception)},k.prototype.submitForm=function(a){a.preventDefault(),this.modal.getRoot().find("form").submit()},k.prototype.submitFormAjax=function(a){a.preventDefault();var c=this.modal.getRoot().find("form"),f=c.find(j.UNWANTEDHIDDENFIELDS);f.each(function(){b(this).remove()});var g=c.serialize();this.modal.hide();var h={type:"GET",processData:!1,contentType:"application/json"},i=d.wwwroot+"/enrol/manual/ajax.php?"+g;b.ajax(i,h).then(function(a){a.error?e.addNotification({message:a.error,type:"error"}):("undefined"!=typeof window.M.core_formchangechecker&&window.M.core_formchangechecker.reset_form_dirty_state(),window.location.reload())}).fail(e.exception)},k.prototype.getBody=function(){return h.loadFragment("enrol_manual","enrol_users_form",this.contextid,{}).fail(e.exception)},k.prototype.getFooter=function(){return a.render("enrol_manual/enrol_modal_footer",{})},{init:function(a){new k(a)}}});
|
||||
@@ -25,9 +25,6 @@
|
||||
|
||||
define(['jquery', 'core/ajax', 'core/templates', 'core/str'], function($, Ajax, Templates, Str) {
|
||||
|
||||
/** @var {Number} Maximum number of users to show. */
|
||||
var MAXUSERS = 100;
|
||||
|
||||
return /** @alias module:enrol_manual/form-potential-user-selector */ {
|
||||
|
||||
processResults: function(selector, results) {
|
||||
@@ -57,6 +54,10 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'], function($, Ajax,
|
||||
if (typeof enrolid === "undefined") {
|
||||
enrolid = '';
|
||||
}
|
||||
var perpage = $(selector).attr('perpage');
|
||||
if (typeof perpage === "undefined") {
|
||||
perpage = 100;
|
||||
}
|
||||
|
||||
promise = Ajax.call([{
|
||||
methodname: 'core_enrol_get_potential_users',
|
||||
@@ -66,7 +67,7 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'], function($, Ajax,
|
||||
search: query,
|
||||
searchanywhere: true,
|
||||
page: 0,
|
||||
perpage: MAXUSERS + 1
|
||||
perpage: perpage + 1
|
||||
}
|
||||
}]);
|
||||
|
||||
@@ -74,7 +75,7 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'], function($, Ajax,
|
||||
var promises = [],
|
||||
i = 0;
|
||||
|
||||
if (results.length <= MAXUSERS) {
|
||||
if (results.length <= perpage) {
|
||||
// Render the label.
|
||||
$.each(results, function(index, user) {
|
||||
var ctx = user,
|
||||
@@ -101,7 +102,7 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'], function($, Ajax,
|
||||
});
|
||||
|
||||
} else {
|
||||
return Str.get_string('toomanyuserstoshow', 'core', '>' + MAXUSERS).then(function(toomanyuserstoshow) {
|
||||
return Str.get_string('toomanyuserstoshow', 'core', '>' + perpage).then(function(toomanyuserstoshow) {
|
||||
success(toomanyuserstoshow);
|
||||
return;
|
||||
});
|
||||
|
||||
@@ -28,8 +28,9 @@ define(['core/templates',
|
||||
'core/modal_factory',
|
||||
'core/modal_events',
|
||||
'core/fragment',
|
||||
'core/pending',
|
||||
],
|
||||
function(Template, $, Str, Config, Notification, ModalFactory, ModalEvents, Fragment) {
|
||||
function(Template, $, Str, Config, Notification, ModalFactory, ModalEvents, Fragment, Pending) {
|
||||
|
||||
/** @type {Object} The list of selectors for the quick enrolment modal. */
|
||||
var SELECTORS = {
|
||||
@@ -91,6 +92,7 @@ define(['core/templates',
|
||||
});
|
||||
|
||||
modal.getRoot().on(ModalEvents.shown, function() {
|
||||
var pendingPromise = new Pending('enrol_manual/quickenrolment:initModal:shown');
|
||||
var bodyPromise = this.getBody();
|
||||
bodyPromise.then(function(html) {
|
||||
var stringIndex = $(html).find(SELECTORS.COHORTSELECT).length ? 0 : 1;
|
||||
@@ -98,7 +100,8 @@ define(['core/templates',
|
||||
|
||||
return;
|
||||
})
|
||||
.fail(Notification.exception);
|
||||
.then(pendingPromise.resolve)
|
||||
.catch(Notification.exception);
|
||||
|
||||
modal.setBody(bodyPromise);
|
||||
}.bind(this));
|
||||
|
||||
@@ -99,6 +99,7 @@ class enrol_manual_enrol_users_form extends moodleform {
|
||||
'multiple' => true,
|
||||
'courseid' => $course->id,
|
||||
'enrolid' => $instance->id,
|
||||
'perpage' => $CFG->maxusersperpage,
|
||||
'userfields' => implode(',', get_extra_user_fields($context))
|
||||
);
|
||||
$mform->addElement('autocomplete', 'userlist', get_string('selectusers', 'enrol_manual'), array(), $options);
|
||||
|
||||
@@ -154,6 +154,16 @@ Feature: Teacher can search and enrol users one by one into the course
|
||||
And I click on ".form-autocomplete-downarrow" "css_element" in the "Select users" "form_row"
|
||||
Then I should see "Too many users (>100) to show"
|
||||
|
||||
@javascript
|
||||
Scenario: Changing the Maximum users per page setting affects the enrolment pop-up.
|
||||
Given the following config values are set as admin:
|
||||
| maxusersperpage | 5 |
|
||||
And I navigate to course participants
|
||||
And I press "Enrol users"
|
||||
When I set the field "Select users" to "student00"
|
||||
And I click on ".form-autocomplete-downarrow" "css_element" in the "Select users" "form_row"
|
||||
Then I should see "Too many users (>5) to show"
|
||||
|
||||
@javascript
|
||||
Scenario: Change the Show user identity setting affects the enrolment pop-up.
|
||||
Given I log out
|
||||
|
||||
@@ -855,6 +855,154 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
|
||||
$this->assertEquals(ENROL_USER_SUSPENDED, $ue->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* dataProvider for test_submit_user_enrolment_form().
|
||||
*/
|
||||
public function submit_user_enrolment_form_provider() {
|
||||
$now = new DateTime();
|
||||
|
||||
$nextmonth = clone($now);
|
||||
$nextmonth->add(new DateInterval('P1M'));
|
||||
|
||||
return [
|
||||
'Invalid data' => [
|
||||
'customdata' => [
|
||||
'status' => ENROL_USER_ACTIVE,
|
||||
'timestart' => [
|
||||
'day' => $now->format('j'),
|
||||
'month' => $now->format('n'),
|
||||
'year' => $now->format('Y'),
|
||||
'hour' => $now->format('G'),
|
||||
'minute' => 0,
|
||||
'enabled' => 1,
|
||||
],
|
||||
'timeend' => [
|
||||
'day' => $now->format('j'),
|
||||
'month' => $now->format('n'),
|
||||
'year' => $now->format('Y'),
|
||||
'hour' => $now->format('G'),
|
||||
'minute' => 0,
|
||||
'enabled' => 1,
|
||||
],
|
||||
],
|
||||
'expectedresult' => false,
|
||||
'validationerror' => true
|
||||
],
|
||||
'Valid data' => [
|
||||
'customdata' => [
|
||||
'status' => ENROL_USER_ACTIVE,
|
||||
'timestart' => [
|
||||
'day' => $now->format('j'),
|
||||
'month' => $now->format('n'),
|
||||
'year' => $now->format('Y'),
|
||||
'hour' => $now->format('G'),
|
||||
'minute' => 0,
|
||||
'enabled' => 1,
|
||||
],
|
||||
'timeend' => [
|
||||
'day' => $nextmonth->format('j'),
|
||||
'month' => $nextmonth->format('n'),
|
||||
'year' => $nextmonth->format('Y'),
|
||||
'hour' => $nextmonth->format('G'),
|
||||
'minute' => 0,
|
||||
'enabled' => 1,
|
||||
],
|
||||
],
|
||||
'expectedresult' => true,
|
||||
'validationerror' => false
|
||||
],
|
||||
'Suspend user' => [
|
||||
'customdata' => [
|
||||
'status' => ENROL_USER_SUSPENDED,
|
||||
],
|
||||
'expectedresult' => true,
|
||||
'validationerror' => false
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $customdata The data we are providing to the webservice.
|
||||
* @param bool $expectedresult The result we are expecting to receive from the webservice.
|
||||
* @param bool $validationerror The validationerror we are expecting to receive from the webservice.
|
||||
* @dataProvider submit_user_enrolment_form_provider
|
||||
*/
|
||||
public function test_submit_user_enrolment_form($customdata, $expectedresult, $validationerror) {
|
||||
global $CFG, $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
$datagen = $this->getDataGenerator();
|
||||
|
||||
/** @var enrol_manual_plugin $manualplugin */
|
||||
$manualplugin = enrol_get_plugin('manual');
|
||||
|
||||
$studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST);
|
||||
$teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST);
|
||||
$course = $datagen->create_course();
|
||||
$user = $datagen->create_user();
|
||||
$teacher = $datagen->create_user();
|
||||
|
||||
$instanceid = null;
|
||||
$instances = enrol_get_instances($course->id, true);
|
||||
foreach ($instances as $inst) {
|
||||
if ($inst->enrol == 'manual') {
|
||||
$instanceid = (int)$inst->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (empty($instanceid)) {
|
||||
$instanceid = $manualplugin->add_default_instance($course);
|
||||
if (empty($instanceid)) {
|
||||
$instanceid = $manualplugin->add_instance($course);
|
||||
}
|
||||
}
|
||||
$this->assertNotNull($instanceid);
|
||||
|
||||
$instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST);
|
||||
$manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
|
||||
$manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE);
|
||||
$ueid = (int) $DB->get_field(
|
||||
'user_enrolments',
|
||||
'id',
|
||||
['enrolid' => $instance->id, 'userid' => $user->id],
|
||||
MUST_EXIST
|
||||
);
|
||||
|
||||
// Login as teacher.
|
||||
$teacher->ignoresesskey = true;
|
||||
$this->setUser($teacher);
|
||||
|
||||
$formdata = [
|
||||
'ue' => $ueid,
|
||||
'ifilter' => 0,
|
||||
'status' => null,
|
||||
'timestart' => null,
|
||||
'timeend' => null,
|
||||
];
|
||||
|
||||
$formdata = array_merge($formdata, $customdata);
|
||||
|
||||
require_once("$CFG->dirroot/enrol/editenrolment_form.php");
|
||||
$formdata = enrol_user_enrolment_form::mock_generate_submit_keys($formdata);
|
||||
|
||||
$querystring = http_build_query($formdata, '', '&');
|
||||
|
||||
$result = external_api::clean_returnvalue(
|
||||
core_enrol_external::submit_user_enrolment_form_returns(),
|
||||
core_enrol_external::submit_user_enrolment_form($querystring)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
['result' => $expectedresult, 'validationerror' => $validationerror],
|
||||
$result,
|
||||
'', 0.0, 10, true);
|
||||
|
||||
if (!empty($result['result'])) {
|
||||
$ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST);
|
||||
$this->assertEquals($formdata['status'], $ue->status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for core_enrol_external::unenrol_user_enrolment().
|
||||
*/
|
||||
|
||||
+3
-3
@@ -949,9 +949,9 @@ class core_files_renderer extends plugin_renderer_base {
|
||||
<div class="file-picker fp-dlg">
|
||||
<p class="fp-dlg-text"></p>
|
||||
<div class="fp-dlg-buttons">
|
||||
<button class="fp-dlg-butoverwrite btn">'.get_string('overwrite', 'repository').'</button>
|
||||
<button class="fp-dlg-butrename btn"></button>
|
||||
<button class="fp-dlg-butcancel btn btn-cancel">'.get_string('cancel').'</button>
|
||||
<button class="fp-dlg-butoverwrite btn mb-1">'.get_string('overwrite', 'repository').'</button>
|
||||
<button class="fp-dlg-butrename btn mb-1"></button>
|
||||
<button class="fp-dlg-butcancel btn btn-cancel mb-1">'.get_string('cancel').'</button>
|
||||
</div>
|
||||
</div>';
|
||||
return $rv;
|
||||
|
||||
@@ -85,7 +85,7 @@ class external extends external_api {
|
||||
} catch (Exception $e) {
|
||||
$warnings[] = array(
|
||||
'item' => 'context',
|
||||
'itemid' => $context['instanceid'],
|
||||
'itemid' => $contextinfo['instanceid'],
|
||||
'warningcode' => $e->getCode(),
|
||||
'message' => $e->getMessage(),
|
||||
);
|
||||
|
||||
@@ -104,14 +104,14 @@ class filter_emoticon extends moodle_text_filter {
|
||||
}
|
||||
|
||||
// Detect all zones that we should not handle (including the nested tags).
|
||||
$processing = preg_split('/(<\/?(?:span|script)[^>]*>)/is', $text, 0, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
||||
$processing = preg_split('/(<\/?(?:span|script|pre)[^>]*>)/is', $text, 0, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
// Initialize the results.
|
||||
$resulthtml = "";
|
||||
$exclude = 0;
|
||||
|
||||
// Define the patterns that mark the start of the forbidden zones.
|
||||
$excludepattern = array('/^<script/is', '/^<span[^>]+class="nolink[^"]*"/is');
|
||||
$excludepattern = array('/^<script/is', '/^<span[^>]+class="nolink[^"]*"/is', '/^<pre/is');
|
||||
|
||||
// Loop through the fragments.
|
||||
foreach ($processing as $fragment) {
|
||||
@@ -126,13 +126,15 @@ class filter_emoticon extends moodle_text_filter {
|
||||
}
|
||||
if ($exclude > 0) {
|
||||
// If we are ignoring the fragment, then we must check if we may have reached the end of the zone.
|
||||
if (strpos($fragment, '</span') !== false || strpos($fragment, '</script') !== false) {
|
||||
if (strpos($fragment, '</span') !== false || strpos($fragment, '</script') !== false
|
||||
|| strpos($fragment, '</pre') !== false) {
|
||||
$exclude -= 1;
|
||||
// This is needed because of a double increment at the first element.
|
||||
if ($exclude == 1) {
|
||||
$exclude -= 1;
|
||||
}
|
||||
} else if (strpos($fragment, '<span') !== false || strpos($fragment, '<script') !== false) {
|
||||
} else if (strpos($fragment, '<span') !== false || strpos($fragment, '<script') !== false
|
||||
|| strpos($fragment, '<pre') !== false) {
|
||||
// If we find a nested tag we increase the exclusion level.
|
||||
$exclude = $exclude + 1;
|
||||
}
|
||||
|
||||
@@ -95,6 +95,21 @@ class filter_emoticon_testcase extends advanced_testcase {
|
||||
'format' => FORMAT_HTML,
|
||||
'expected' => '<span class="nolink"><span>(n)</span>(n)</span>' . $this->get_converted_content_for_emoticon('(n)'),
|
||||
],
|
||||
'Basic pre should not be processed' => [
|
||||
'input' => '<pre>(n)</pre>',
|
||||
'format' => FORMAT_HTML,
|
||||
'expected' => '<pre>(n)</pre>',
|
||||
],
|
||||
'Nested pre should not be processed' => [
|
||||
'input' => '<pre><pre>(n)</pre>(n)</pre>',
|
||||
'format' => FORMAT_HTML,
|
||||
'expected' => '<pre><pre>(n)</pre>(n)</pre>',
|
||||
],
|
||||
'Nested pre should not be processed but following emoticon' => [
|
||||
'input' => '<pre><pre>(n)</pre>(n)</pre>(n)',
|
||||
'format' => FORMAT_HTML,
|
||||
'expected' => '<pre><pre>(n)</pre>(n)</pre>' . $this->get_converted_content_for_emoticon('(n)'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -189,5 +189,17 @@ MathJax.Hub.Config({
|
||||
// Automatically generated Moodle v3.6.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2018120301) {
|
||||
// Update CDN url.
|
||||
$originalurl = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js';
|
||||
$newurl = 'https://cdn.jsdelivr.net/npm/mathjax@2.7.8/MathJax.js';
|
||||
$currenturl = get_config('filter_mathjaxloader', 'httpsurl');
|
||||
if ($currenturl == $originalurl) {
|
||||
set_config('httpsurl', $newurl, 'filter_mathjaxloader');
|
||||
}
|
||||
|
||||
upgrade_plugin_savepoint(true, 2018120301, 'filter', 'mathjaxloader');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Description of MathJAX library integration in Moodle
|
||||
====================================================
|
||||
|
||||
* Default MathJax version: 2.7.2
|
||||
* Default MathJax version: 2.7.8
|
||||
* License: Apache 2.0
|
||||
* Source: https://www.mathjax.org/
|
||||
|
||||
@@ -18,3 +18,8 @@ Upgrading the default MathJax version
|
||||
3. Check and eventually update the list of language mappings in filter.php.
|
||||
Also see the unit test for the language mappings.
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
* The MathJax 2.7.2 seems to have a possible security issue, the CDN default value have been
|
||||
updated to point to the recommended 2.7.8 version. See MDL-68430 for details.
|
||||
|
||||
@@ -33,7 +33,7 @@ if ($ADMIN->fulltree) {
|
||||
$item = new admin_setting_configtext('filter_mathjaxloader/httpsurl',
|
||||
new lang_string('httpsurl', 'filter_mathjaxloader'),
|
||||
new lang_string('httpsurl_help', 'filter_mathjaxloader'),
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js',
|
||||
'https://cdn.jsdelivr.net/npm/mathjax@2.7.8/MathJax.js',
|
||||
PARAM_RAW);
|
||||
$settings->add($item);
|
||||
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018120300;
|
||||
$plugin->version = 2018120301;
|
||||
$plugin->requires = 2018112800; // Requires this Moodle version.
|
||||
$plugin->component= 'filter_mathjaxloader';
|
||||
|
||||
@@ -19,6 +19,14 @@
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
/* This is needed to display videos in an adequate size and without too much space
|
||||
withing the specific mod assign table views. */
|
||||
.path-mod-assign .gradingtable .mediaplugin,
|
||||
.path-mod-assign .submissionsummarytable .mediaplugin {
|
||||
width: 400px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Make media plugin behave properly in mod summaries and labels */
|
||||
.mod-indent-outer .mediaplugin {
|
||||
display: table-cell;
|
||||
|
||||
@@ -187,5 +187,14 @@ class core_filter_external_testcase extends externallib_advanced_testcase {
|
||||
$this->assertEquals($firstfilter, $result['filters'][0]['filter']); // OK, the filter is enabled globally.
|
||||
$this->assertEquals(TEXTFILTER_OFF, $result['filters'][0]['localstate']); // It is not available in this context.
|
||||
$this->assertEquals(TEXTFILTER_ON, $result['filters'][0]['inheritedstate']); // In the parent context is available.
|
||||
|
||||
// Try user without permission, warning expected.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
$result = external::get_available_in_context(array(array('contextlevel' => 'module', 'instanceid' => $forum->cmid)));
|
||||
$result = external_api::clean_returnvalue(external::get_available_in_context_returns(), $result);
|
||||
$this->assertNotEmpty($result['warnings']);
|
||||
$this->assertEquals('context', $result['warnings'][0]['item']);
|
||||
$this->assertEquals($forum->cmid, $result['warnings'][0]['itemid']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,8 +322,10 @@ class grading_manager {
|
||||
}
|
||||
|
||||
} else if ($this->get_context()->contextlevel == CONTEXT_MODULE) {
|
||||
list($context, $course, $cm) = get_context_info_array($this->get_context()->id);
|
||||
return self::available_areas('mod_'.$cm->modname);
|
||||
$modulecontext = $this->get_context();
|
||||
$coursecontext = $modulecontext->get_course_context();
|
||||
$cm = get_fast_modinfo($coursecontext->instanceid)->get_cm($modulecontext->instanceid);
|
||||
return self::available_areas("mod_{$cm->modname}");
|
||||
|
||||
} else {
|
||||
throw new coding_exception('Unsupported gradable area context level');
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user