Compare commits
539 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2288e60615 | |||
| f951911682 | |||
| ad262b981f | |||
| da4ffac0c9 | |||
| 702c4b4781 | |||
| fedba1fcc4 | |||
| de96ddee74 | |||
| 7b6a902ccd | |||
| a0a03baad8 | |||
| e37073a24b | |||
| 9f59264afa | |||
| 7f4be41ed2 | |||
| 3dec6b78ce | |||
| e3e41170fe | |||
| 7c6be5b319 | |||
| 0613e56e69 | |||
| a3065e8220 | |||
| f496b55189 | |||
| 135461b4a9 | |||
| 8253f41405 | |||
| cf0b79a80f | |||
| 1a8dc02878 | |||
| 5b3ec84495 | |||
| 0608b35a7b | |||
| abb5f229b1 | |||
| e2fd22fadd | |||
| 18c824af1f | |||
| 37ce725987 | |||
| 8ac6793716 | |||
| 6022706b47 | |||
| 250ea6a145 | |||
| 039096d1b1 | |||
| 0ca3442925 | |||
| 1a4626d9fa | |||
| fa0b5768cc | |||
| 7b33c5e9ec | |||
| d394a22ee6 | |||
| 83fe3c44c0 | |||
| 282c4224da | |||
| 1312197a82 | |||
| 5dc6fd3b23 | |||
| b49b76f397 | |||
| a4f4656110 | |||
| 83bdb872dc | |||
| 39a83aa250 | |||
| e70853386d | |||
| 451d514d84 | |||
| 72f5a6e119 | |||
| 7f624852e4 | |||
| 8abe8980d6 | |||
| 431c7ba150 | |||
| 9b632e17d8 | |||
| b731ea9798 | |||
| 472b1b095e | |||
| 2cddf32f7e | |||
| 9b898bf8b4 | |||
| a87d3a64bb | |||
| 55cd43d397 | |||
| 9c1462ecfe | |||
| e22aba78ca | |||
| d952f227d0 | |||
| d80cec9c2b | |||
| 77fe56108a | |||
| 03550b7b8f | |||
| 274ccc948b | |||
| bc8d5da483 | |||
| 94bf3e9c78 | |||
| e92bd648db | |||
| e4bb1cc603 | |||
| c2f12aa742 | |||
| 454af7cf52 | |||
| f73e64f166 | |||
| 18a738f88f | |||
| 493a30cdc2 | |||
| 65edc59a51 | |||
| 28b9209af5 | |||
| 8b2c91e627 | |||
| 8256d18453 | |||
| ab11164395 | |||
| 29933a945f | |||
| c7b384ce66 | |||
| 665e256961 | |||
| 879532b41f | |||
| 4e83a79cd5 | |||
| 3ecba892e3 | |||
| b92f4ba9f1 | |||
| 6a3a83af93 | |||
| 6e3a070030 | |||
| 5d701a706c | |||
| 612e4d9d02 | |||
| df6f010a86 | |||
| bf4904c32b | |||
| 2821f78e6c | |||
| f904215fa7 | |||
| 635b605eab | |||
| 08dd41b304 | |||
| 04cd0cb2ca | |||
| 5cacda33bb | |||
| 864ef22534 | |||
| f008a03c60 | |||
| 8831cc14c7 | |||
| ebb87a1e63 | |||
| 93d9b19607 | |||
| 92d04a7913 | |||
| a5cdde3c79 | |||
| d073f5daf0 | |||
| 3972ef0a32 | |||
| 01b63287ef | |||
| b3259b51bb | |||
| b683a48f69 | |||
| 944888a509 | |||
| 21ac04e12e | |||
| ab9b65b6a7 | |||
| 4a852e8467 | |||
| 9c211250ed | |||
| a4eb84f12f | |||
| 1e3da12a5f | |||
| d5ac2c69cf | |||
| 3cf2cce110 | |||
| 1df418416b | |||
| fdee2f256c | |||
| 15974729df | |||
| 99f8dea24b | |||
| 27829f24e1 | |||
| 6fd3764d99 | |||
| 8563f23446 | |||
| 033afe1028 | |||
| c21f3b540b | |||
| 9fa3c6756e | |||
| d543802d90 | |||
| 374d0b4702 | |||
| 043da28910 | |||
| b35de2fdf0 | |||
| 2b1b27dfbd | |||
| bd370cda69 | |||
| 86cbf2a643 | |||
| d33ae6c0cc | |||
| 61b51f93ef | |||
| eb2f9593a7 | |||
| e8b6cb418f | |||
| f8798a6b07 | |||
| 75259ad00f | |||
| e149838c2a | |||
| 7e0c5d1c38 | |||
| b78a289579 | |||
| de168198e6 | |||
| d4cd76a5c8 | |||
| 7430be37c5 | |||
| 327c830b2a | |||
| f2c38390c9 | |||
| 55bbdac9d9 | |||
| 7ed867fa20 | |||
| fc6f41655b | |||
| 15ddbf3907 | |||
| 27b547db5a | |||
| 0d13442b29 | |||
| d1bc79e02a | |||
| 3abca9b368 | |||
| 3a000a8def | |||
| 1c3c38fb00 | |||
| 86cbd0840b | |||
| 2ea211e78a | |||
| b60ec99fec | |||
| 953b1b85da | |||
| 45e2df1ce4 | |||
| 9f1149ac13 | |||
| a157baa65a | |||
| 15d2dcf037 | |||
| 9eab90a827 | |||
| 2bee1f66b9 | |||
| c3731344e5 | |||
| c46b2eaf18 | |||
| d6af305bb9 | |||
| fa319a92b8 | |||
| e11d0e35de | |||
| 313fd64469 | |||
| 9f51263a37 | |||
| b375088f23 | |||
| 6cdfbdc119 | |||
| c3e7a570f3 | |||
| b12ee51f8b | |||
| 9192613a6a | |||
| 74fc0a9620 | |||
| 264ea8c7fc | |||
| 06079db957 | |||
| 59c9dd780b | |||
| 5ec6a4e462 | |||
| 4ce08a765d | |||
| 0498684ae2 | |||
| 9a0078f100 | |||
| d9126f03dc | |||
| 382fc36eba | |||
| defe05cbcf | |||
| 71548acf7e | |||
| de75e7471d | |||
| 8635cc3871 | |||
| a8d72a836c | |||
| 140b417065 | |||
| 0094a77ccc | |||
| c7f46956d4 | |||
| 648e72316d | |||
| 6b8c4ddacc | |||
| 3bf1e69a3e | |||
| df4041b745 | |||
| ac9e0f9ad5 | |||
| c0cfb0ee09 | |||
| 5725188035 | |||
| acb34a20c2 | |||
| e8c5c935a5 | |||
| 5db144950d | |||
| 904399daaf | |||
| 498fa3c1ae | |||
| a8f2981158 | |||
| ccca3fc6e4 | |||
| 5d16523422 | |||
| 1953f78e3e | |||
| 6504388f3e | |||
| 902d300967 | |||
| 9399070f9f | |||
| 96b2794f29 | |||
| d551e89fad | |||
| 3bb633edc4 | |||
| 862d20bac0 | |||
| cf2beaf8c5 | |||
| d3eeabcf04 | |||
| 8c678d9aa9 | |||
| 9d86101204 | |||
| 2f535a7c43 | |||
| 776141f4d3 | |||
| f33dbc4223 | |||
| 11685ae642 | |||
| 4cf939c638 | |||
| 7a5e8583d9 | |||
| 63b2abcc34 | |||
| 16a14f010d | |||
| da6a75fcc7 | |||
| 522a84d484 | |||
| 09325c5a80 | |||
| db72e5b94e | |||
| 2a7d1c59e8 | |||
| ed13b5bff3 | |||
| d4345429ec | |||
| dccc4f0338 | |||
| 300afd6ab4 | |||
| 91998db5f5 | |||
| e76b0e1014 | |||
| 5e5789ab09 | |||
| 50d3ef02b9 | |||
| cea931e57e | |||
| ee4ea23869 | |||
| 473d664b4a | |||
| f4c3a9e129 | |||
| d29528c4db | |||
| 88f85ede8e | |||
| b2a600563e | |||
| 7f5049c7bc | |||
| 321125cadb | |||
| 9c2796097f | |||
| cc6bd24c30 | |||
| 9b37061aba | |||
| 8ac0bb82df | |||
| 7ff9602c7a | |||
| 2918831394 | |||
| bce54527f7 | |||
| 39cd996f62 | |||
| 2edb40d973 | |||
| 1fa428ffcf | |||
| 40f3c1a50a | |||
| c1aea9fd14 | |||
| 4e4d4e37b8 | |||
| 16eeee959b | |||
| 3d44463106 | |||
| 8fd0ca20b9 | |||
| eafc231dd8 | |||
| e8b72dabe6 | |||
| 3dff98b036 | |||
| 5e82098480 | |||
| eff7030f7e | |||
| 1dd2ee3f49 | |||
| 263b3cb643 | |||
| 55ca32ba74 | |||
| 7a548abf01 | |||
| 73d401b462 | |||
| 7ea8581868 | |||
| 3f923b4570 | |||
| 8ef6c25a98 | |||
| 007b946948 | |||
| d1e419d86f | |||
| 2e190e4739 | |||
| e81ac514ca | |||
| 563f08ff54 | |||
| 8e70179f4d | |||
| 30ac3bcfeb | |||
| b287771272 | |||
| 4b00d8392c | |||
| abe4ff5534 | |||
| 5b641c4187 | |||
| 02dc7147c7 | |||
| 8ab8dee8c9 | |||
| b9569a887e | |||
| efee2e4c8b | |||
| 33f22e1e83 | |||
| 500fc1b8f0 | |||
| e36f1c9896 | |||
| cd6fb4322b | |||
| 1398145998 | |||
| ec6f1419dc | |||
| 473145c2c7 | |||
| 469ac6dd08 | |||
| 5ddac4b5b9 | |||
| 0c8113bf8c | |||
| f4110673d4 | |||
| fdecf60bab | |||
| bef3d096fc | |||
| bb1cfe25cf | |||
| 1150862f9d | |||
| b94ea41bbf | |||
| 6946a25e9f | |||
| 2a75ea0ec9 | |||
| dafd6f136c | |||
| b6147f7e80 | |||
| 531036848c | |||
| c0e1742985 | |||
| bedb1ffc49 | |||
| 4a6b23937a | |||
| 83d8624eec | |||
| 6e311854b7 | |||
| 12531500d3 | |||
| 73c4aa313b | |||
| b22947d3b7 | |||
| e208a8d307 | |||
| a4c878c734 | |||
| 54d672958d | |||
| 13e1ef0300 | |||
| b1899fcc4a | |||
| 31c96d5e7f | |||
| 3bb9a613c6 | |||
| 29fcc59f88 | |||
| 3cf521a427 | |||
| a27c089b40 | |||
| 4d1fbba086 | |||
| c8c229e08b | |||
| 7d78aeece1 | |||
| 4d0fa8abdc | |||
| 4c775e4618 | |||
| 9462d6f1c8 | |||
| 2aeff9cfd7 | |||
| 5b01f0099f | |||
| 6e3f19324a | |||
| 34a46486ef | |||
| 2a373db105 | |||
| 08135b0689 | |||
| 1bd6794d80 | |||
| 3e854512c5 | |||
| 8326233128 | |||
| eb29ebefb4 | |||
| 638ba35597 | |||
| 94cbb103ef | |||
| efaad61031 | |||
| 05e4efbcee | |||
| 3451667bc9 | |||
| 9efdbf36d7 | |||
| 23bb771193 | |||
| f47cc5ce3f | |||
| 9f504db2f3 | |||
| 9e5cda1f6c | |||
| 3751ec0e68 | |||
| 5a93c6c9ab | |||
| 596aa22c4e | |||
| cc72ae7f29 | |||
| 7aa7b392c0 | |||
| 11a4683cfd | |||
| b6af039890 | |||
| 69e9f234e1 | |||
| b7f0a3fd9c | |||
| 4f701a0e67 | |||
| 45b9bae80f | |||
| 5d3bf61c27 | |||
| c344124599 | |||
| 22baf5c0aa | |||
| f1125775fe | |||
| ca0f244e09 | |||
| 216ea27814 | |||
| e0ea44ae29 | |||
| 8ffac7d8e3 | |||
| 8d0c6ac9dd | |||
| 5b7563dbb2 | |||
| 8bf1ffc9ed | |||
| b3d49c46eb | |||
| 98af4c736d | |||
| 2653ea76be | |||
| 367a45a60b | |||
| 1da8aa30e0 | |||
| 2e9bc58475 | |||
| dd74898f63 | |||
| 12476a35a6 | |||
| bf35831359 | |||
| cc145e54fd | |||
| 48f96614ff | |||
| 4066dda725 | |||
| 0a91d215c6 | |||
| 6d0e1fe82d | |||
| b7218d8a10 | |||
| de2f0d85e7 | |||
| 077825f8c9 | |||
| 06d2ee0fde | |||
| 96607cf5b3 | |||
| b290b3eded | |||
| f059d1176c | |||
| 0f389d44b6 | |||
| 29dba99cdf | |||
| 7e7b71fbeb | |||
| 4f2ee1dec1 | |||
| f7a4ef3866 | |||
| 6181040347 | |||
| 8082ff4805 | |||
| 5e2338c868 | |||
| 6909b8aada | |||
| 80c1b6bcd0 | |||
| d6455734d8 | |||
| c91c235fb6 | |||
| f9ea236c4f | |||
| 61adf3b25b | |||
| e9c0b71aee | |||
| 3304b0728c | |||
| d831bfbf04 | |||
| 6bfcfd9a1f | |||
| 9cbdd41129 | |||
| c56206cebf | |||
| 71c0f9c055 | |||
| 5e3ce4ef7d | |||
| 1e21aeb97c | |||
| 403f2d450c | |||
| c8ce874199 | |||
| 13cdeaf6a4 | |||
| 0503fc7a35 | |||
| 6abbe519d6 | |||
| bdca601d03 | |||
| 4477b21322 | |||
| 1625acf3eb | |||
| 8326e3d1a9 | |||
| c4658fd9e2 | |||
| d28abb632d | |||
| 819ff2f3d7 | |||
| 2787bb5593 | |||
| 2ca6891e1f | |||
| 5381aad94a | |||
| 618b27a8ae | |||
| 1ed39a4991 | |||
| 2427689f4c | |||
| 89b9e4f805 | |||
| 4418ec9411 | |||
| 5fbbcddd0c | |||
| 25570f80dc | |||
| 0f038af8de | |||
| 5e97ea1423 | |||
| 2e790a9741 | |||
| 68b61978e8 | |||
| 0eff5b7765 | |||
| fd0b312303 | |||
| cfa3409712 | |||
| 15d1157600 | |||
| a1026db0fe | |||
| 959c70e87b | |||
| 8e2360f9c8 | |||
| 55d5362ab6 | |||
| e1c280f951 | |||
| 238585c911 | |||
| d7903d34f0 | |||
| 710b4e0a4b | |||
| 0c74fb3f82 | |||
| ab3236199d | |||
| 67d62946ff | |||
| 9415d26ea4 | |||
| c2a956a63d | |||
| d9af5335c2 | |||
| 4c0ea3f45e | |||
| d707892d0c | |||
| d70651f787 | |||
| c55d75120a | |||
| 59f0c4b48a | |||
| f4ae633fa5 | |||
| 89eded8477 | |||
| 57b5bf77b7 | |||
| dc5d047953 | |||
| 8f9f22aeb3 | |||
| 500ea66aba | |||
| 5c12024435 | |||
| 51feaeafa2 | |||
| bccd34fa0a | |||
| 77e067dc2e | |||
| 0ec20a18a4 | |||
| d402e44ab9 | |||
| a8ad45b9e8 | |||
| b60590a2fa | |||
| 25160a2a05 | |||
| 849f7f40c8 | |||
| 36ec2d7503 | |||
| e3a7647ec3 | |||
| 70c84f07d7 | |||
| 485ac4aeba | |||
| ca39432c7a | |||
| e1de53be9f | |||
| 7c05382b75 | |||
| 8d371c2174 | |||
| 457f84654e | |||
| 87648029c4 | |||
| 5fd2f276f1 | |||
| ba8f84021f | |||
| 7f84843045 | |||
| 41bb7608e8 | |||
| b9ceefadf0 | |||
| 2ff2274ffc | |||
| 82a46e6f07 | |||
| e34384ed18 | |||
| 43121fbe8b | |||
| fe033417a6 | |||
| 73c24fa02c | |||
| b1376641d4 | |||
| 546ea12179 | |||
| 55c0301d33 | |||
| cca1025271 | |||
| dda86847b1 | |||
| 12abff6943 | |||
| e247dabc3a | |||
| 22f5807e22 | |||
| 3be7a5ce18 | |||
| 34667451cf | |||
| b02583be81 | |||
| 4d53d44f2e | |||
| deb6d89f94 | |||
| 9820d27a23 | |||
| 78615db17f | |||
| 8d34e84bb9 | |||
| f20c9009fa | |||
| 3be433b5d2 | |||
| 3086259abb | |||
| 4480118b5d | |||
| ed73a073a3 |
@@ -203,7 +203,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ["**/amd/src/*.js"],
|
||||
files: ["**/amd/src/*.js", "Gruntfile.js"],
|
||||
// Check AMD with some slightly stricter rules.
|
||||
rules: {
|
||||
'no-unused-vars': 'error',
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
**/amd/**
|
||||
/*.js
|
||||
@@ -11,6 +11,8 @@ notifications:
|
||||
|
||||
language: php
|
||||
|
||||
dist: trusty
|
||||
|
||||
php:
|
||||
# We only run the highest and lowest supported versions to reduce the load on travis-ci.org.
|
||||
- 7.2
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
* Grunt configuration
|
||||
*/
|
||||
|
||||
/* eslint-env node */
|
||||
module.exports = function(grunt) {
|
||||
var path = require('path'),
|
||||
tasks = {},
|
||||
|
||||
@@ -710,7 +710,7 @@ if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('cliadminemail', 'install'));
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['adminemail']);
|
||||
$options['adminemail'] = cli_input($prompt);
|
||||
$options['adminemail'] = cli_input($prompt, $options['adminemail']);
|
||||
}
|
||||
|
||||
// Validate that the address provided was an e-mail address.
|
||||
|
||||
@@ -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>
|
||||
@@ -2799,4 +2801,366 @@
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.8" requires="3.2">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="5.5.31" />
|
||||
<VENDOR name="mysql" version="5.6" />
|
||||
<VENDOR name="postgres" version="9.4" />
|
||||
<VENDOR name="mssql" version="11.0" />
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.1.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="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="mbstringrecommended" />
|
||||
</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>
|
||||
<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>
|
||||
|
||||
+1
-2
@@ -823,8 +823,7 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
*/
|
||||
protected function registration_warning($registered) {
|
||||
|
||||
if (!$registered) {
|
||||
|
||||
if (!$registered && site_is_public()) {
|
||||
if (has_capability('moodle/site:config', context_system::instance())) {
|
||||
$registerbutton = $this->single_button(new moodle_url('/admin/registration/index.php'),
|
||||
get_string('register', 'admin'));
|
||||
|
||||
@@ -69,8 +69,11 @@ class core_role_check_users_selector extends user_selector_base {
|
||||
|
||||
if ($coursecontext and $coursecontext != SITEID) {
|
||||
$sql1 = " FROM {user} u
|
||||
JOIN {user_enrolments} ue ON (ue.userid = u.id)
|
||||
JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid1)
|
||||
JOIN (SELECT DISTINCT subu.id
|
||||
FROM {user} subu
|
||||
JOIN {user_enrolments} ue ON (ue.userid = subu.id)
|
||||
JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid1)
|
||||
) subq ON subq.id = u.id
|
||||
WHERE $wherecondition";
|
||||
$params['courseid1'] = $coursecontext->instanceid;
|
||||
|
||||
|
||||
@@ -325,7 +325,7 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
|
||||
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_skip_modif_prev', new lang_string('skipmodifprev', 'backup'), new lang_string('skipmodifprevhelp', 'backup'), 0));
|
||||
|
||||
// Automated defaults section.
|
||||
$temp->add(new admin_setting_heading('automatedsettings', new lang_string('automatedsettings','backup'), ''));
|
||||
$temp->add(new admin_setting_heading('automatedsettings', new lang_string('automatedsettings','backup'), new lang_string('recyclebin_desc', 'backup')));
|
||||
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_users', new lang_string('generalusers', 'backup'), new lang_string('configgeneralusers', 'backup'), 1));
|
||||
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_role_assignments', new lang_string('generalroleassignments','backup'), new lang_string('configgeneralroleassignments','backup'), 1));
|
||||
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_activities', new lang_string('generalactivities','backup'), new lang_string('configgeneralactivities','backup'), 1));
|
||||
|
||||
@@ -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::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::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']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,57 +1,74 @@
|
||||
@core @core_admin
|
||||
Feature: Enable multiple accounts to have the same email address
|
||||
In order to have multiple accounts registerd on the system with the same email address
|
||||
@core @core_admin @core_user
|
||||
Feature: Allowing multiple accounts to have the same email address
|
||||
In order to manage user accounts
|
||||
As an admin
|
||||
I need to enable multiple accounts to be registered with the same email address and verify it is applied
|
||||
I need to be able to set whether to allow multiple accounts with the same email or not
|
||||
|
||||
Background:
|
||||
Given I log in as "admin"
|
||||
|
||||
Scenario: Enable registration of multiple accounts with the same email address
|
||||
Scenario Outline: Create a user with the same email as an existing user
|
||||
Given the following config values are set as admin:
|
||||
| allowaccountssameemail | 1 |
|
||||
When I navigate to "Add a new user" node in "Site administration>Users>Accounts"
|
||||
| allowaccountssameemail | <allowsameemail> |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| s1 | John | Doe | s1@example.com |
|
||||
When I log in as "admin"
|
||||
And I navigate to "Users > Accounts > Add a new user" in site administration
|
||||
And I set the following fields to these values:
|
||||
| Username | testmultiemailuser1 |
|
||||
| Choose an authentication method | Manual accounts |
|
||||
| New password | test@User1 |
|
||||
| First name | Test |
|
||||
| Surname | Multi1 |
|
||||
| Email address | testmultiemailuser@example.com |
|
||||
| Username | s2 |
|
||||
| First name | Jane |
|
||||
| Surname | Doe |
|
||||
| Email address | <email> |
|
||||
| New password | test |
|
||||
And I press "Create user"
|
||||
And I should see "Test Multi1"
|
||||
And I press "Add a new user"
|
||||
And I set the following fields to these values:
|
||||
| Username | testmultiemailuser2 |
|
||||
| Choose an authentication method | Manual accounts |
|
||||
| New password | test@User2 |
|
||||
| First name | Test |
|
||||
| Surname | Multi2 |
|
||||
| Email address | testmultiemailuser@example.com |
|
||||
And I press "Create user"
|
||||
Then I should see "Test Multi2"
|
||||
And I should not see "This email address is already registered"
|
||||
Then I should <expect> "This email address is already registered."
|
||||
|
||||
Scenario: Disable registration of multiple accounts with the same email address
|
||||
Examples:
|
||||
| allowsameemail | email | expect |
|
||||
| 0 | s1@example.com | see |
|
||||
| 0 | S1@EXAMPLE.COM | see |
|
||||
| 1 | s1@example.com | not see |
|
||||
| 1 | S1@EXAMPLE.COM | not see |
|
||||
|
||||
Scenario Outline: Update a user with the same email as an existing user
|
||||
Given the following config values are set as admin:
|
||||
| allowaccountssameemail | 0 |
|
||||
When I navigate to "Add a new user" node in "Site administration>Users>Accounts"
|
||||
And I set the following fields to these values:
|
||||
| Username | testmultiemailuser1 |
|
||||
| Choose an authentication method | Manual accounts |
|
||||
| New password | test@User1 |
|
||||
| First name | Test |
|
||||
| Surname | Multi1 |
|
||||
| Email address | testmultiemailuser@example.com |
|
||||
And I press "Create user"
|
||||
And I should see "Test Multi1"
|
||||
And I press "Add a new user"
|
||||
And I set the following fields to these values:
|
||||
| Username | testmultiemailuser2 |
|
||||
| Choose an authentication method | Manual accounts |
|
||||
| New password | test@User2 |
|
||||
| First name | Test |
|
||||
| Surname | Multi2 |
|
||||
| Email address | testmultiemailuser@example.com |
|
||||
And I press "Create user"
|
||||
Then I should see "This email address is already registered"
|
||||
| allowaccountssameemail | <allowsameemail> |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| s1 | John | Doe | s1@example.com |
|
||||
| s2 | Jane | Doe | s2@example.com |
|
||||
When I log in as "admin"
|
||||
And I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
And I click on "Edit" "link" in the "Jane Doe" "table_row"
|
||||
And I set the field "Email address" to "<email>"
|
||||
And I press "Update profile"
|
||||
Then I should <expect> "This email address is already registered."
|
||||
|
||||
Examples:
|
||||
| allowsameemail | email | expect |
|
||||
| 0 | s1@example.com | see |
|
||||
| 0 | S1@EXAMPLE.COM | see |
|
||||
| 1 | s1@example.com | not see |
|
||||
| 1 | S1@EXAMPLE.COM | not see |
|
||||
| 0 | S2@EXAMPLE.COM | not see |
|
||||
| 1 | S2@EXAMPLE.COM | not see |
|
||||
|
||||
Scenario Outline: Update own user profile with the same email as an existing user
|
||||
Given the following config values are set as admin:
|
||||
| allowaccountssameemail | <allowsameemail> |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| s1 | John | Doe | s1@example.com |
|
||||
| s2 | Jane | Doe | s2@example.com |
|
||||
When I log in as "s2"
|
||||
And I open my profile in edit mode
|
||||
And I set the field "Email address" to "<email>"
|
||||
And I press "Update profile"
|
||||
Then I should <expect> "This email address is already registered."
|
||||
|
||||
Examples:
|
||||
| allowsameemail | email | expect |
|
||||
| 0 | s1@example.com | see |
|
||||
| 0 | S1@EXAMPLE.COM | see |
|
||||
| 1 | s1@example.com | not see |
|
||||
| 1 | S1@EXAMPLE.COM | not see |
|
||||
| 0 | S2@EXAMPLE.COM | not see |
|
||||
| 1 | S2@EXAMPLE.COM | not see |
|
||||
|
||||
@@ -9,6 +9,7 @@ Feature: Manage tokens
|
||||
| username | password | firstname | lastname |
|
||||
| testuser | testuser | Joe | Bloggs |
|
||||
| testuser2 | testuser2 | TestFirstname | TestLastname |
|
||||
And I change window size to "small"
|
||||
And I log in as "admin"
|
||||
And I am on site homepage
|
||||
|
||||
|
||||
@@ -162,11 +162,11 @@ class tool_behat_renderer extends plugin_renderer_base {
|
||||
$html .= $this->output->heading($title);
|
||||
|
||||
// Info.
|
||||
$installurl = behat_command::DOCS_URL . '#Installation';
|
||||
$installurl = behat_command::DOCS_URL;
|
||||
$installlink = html_writer::tag('a', $installurl, array('href' => $installurl, 'target' => '_blank'));
|
||||
$writetestsurl = behat_command::DOCS_URL . '#Writing_features';
|
||||
$writetestsurl = 'https://docs.moodle.org/dev/Writing acceptance tests';
|
||||
$writetestslink = html_writer::tag('a', $writetestsurl, array('href' => $writetestsurl, 'target' => '_blank'));
|
||||
$writestepsurl = behat_command::DOCS_URL . '#Adding_steps_definitions';
|
||||
$writestepsurl = 'https://docs.moodle.org/dev/Writing_new_acceptance_test_step_definitions';
|
||||
$writestepslink = html_writer::tag('a', $writestepsurl, array('href' => $writestepsurl, 'target' => '_blank'));
|
||||
$infos = array(
|
||||
get_string('installinfo', 'tool_behat', $installlink),
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -440,6 +440,9 @@ class purpose extends persistent {
|
||||
}
|
||||
if (!empty($data->sensitivedatareasons) && is_array($data->sensitivedatareasons)) {
|
||||
$data->sensitivedatareasons = implode(',', $data->sensitivedatareasons);
|
||||
} else {
|
||||
// Nothing selected. Set default value of null.
|
||||
$data->sensitivedatareasons = null;
|
||||
}
|
||||
|
||||
// A single value.
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
}} data-status="{{status}}"{{!
|
||||
}}>
|
||||
<td>{{typename}}</td>
|
||||
<td>{{#userdate}} {{timecreated}}, {{#str}} strftimedatetime {{/str}} {{/userdate}}</td>
|
||||
<td>{{#userdate}} {{timecreated}}, {{#str}} strftimedatetime, core_langconfig {{/str}} {{/userdate}}</td>
|
||||
<td><a href="{{requestedbyuser.profileurl}}" title="{{#str}}viewprofile{{/str}}">{{requestedbyuser.fullname}}</a></td>
|
||||
<td>
|
||||
<span class="label {{statuslabelclass}}" title="{{statuslabeltitle}}">{{statuslabel}}</span>
|
||||
|
||||
@@ -67,7 +67,8 @@
|
||||
<a href="mailto:{{foruser.email}}">{{foruser.email}}</a>
|
||||
<div class="clearfix m-t-1 m-b-1">
|
||||
<span class="pull-left m-r-1">
|
||||
<strong>{{#str}}daterequesteddetail, tool_dataprivacy{{/str}}</strong> {{#userdate}} {{timecreated}}, {{#str}} strftimedatetime {{/str}} {{/userdate}}
|
||||
<strong>{{#str}}daterequesteddetail, tool_dataprivacy{{/str}}</strong>
|
||||
{{#userdate}} {{timecreated}}, {{#str}} strftimedatetime, core_langconfig {{/str}} {{/userdate}}
|
||||
</span>
|
||||
<span class="pull-left m-r-1">
|
||||
<strong>{{#str}}statusdetail, tool_dataprivacy{{/str}}</strong>
|
||||
|
||||
@@ -337,6 +337,11 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -137,14 +137,14 @@ abstract class restore_tool_log_logstore_subplugin extends restore_subplugin {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now we want to serialize it so we can store it in the DB.
|
||||
$data->other = serialize($data->other);
|
||||
} else {
|
||||
$message = "Event class not found: \"$eventclass\". Skipping log record.";
|
||||
$this->log($message, backup::LOG_DEBUG);
|
||||
return; // No such class, can not restore.
|
||||
}
|
||||
}
|
||||
// Now we want to serialize it so we can store it in the DB.
|
||||
$data->other = serialize($data->other);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
define(["jquery","core/notification","tool_lp/dialogue","core/str","core/ajax","core/templates"],function(a,b,c,d,e,f){var g=function(b){a(b).on("click",this.configureSettings.bind(this))};return g.prototype._dialogue=null,g.prototype.configureSettings=function(e){var g=a(e.target).closest("a").data("courseid"),h=a(e.target).closest("a").data("pushratingstouserplans"),i={courseid:g,settings:{pushratingstouserplans:h}};e.preventDefault(),f.render("tool_lp/course_competency_settings",i).done(function(a){d.get_string("configurecoursecompetencysettings","tool_lp").done(function(b){this._dialogue=new c(b,a,this.addListeners.bind(this))}.bind(this)).fail(b.exception)}.bind(this)).fail(b.exception)},g.prototype.addListeners=function(){var a=this._find('[data-action="save"]');a.on("click",this.saveSettings.bind(this));var b=this._find('[data-action="cancel"]');b.on("click",this.cancelChanges.bind(this))},g.prototype.cancelChanges=function(a){a.preventDefault(),this._dialogue.close()},g.prototype._find=function(b){return a('[data-region="coursecompetencysettings"]').find(b)},g.prototype.saveSettings=function(a){a.preventDefault();var c=this._find('input[name="pushratingstouserplans"]:checked').val(),d=this._find('input[name="courseid"]').val(),f={pushratingstouserplans:c};e.call([{methodname:"core_competency_update_course_competency_settings",args:{courseid:d,settings:f}}])[0].done(function(){this.refreshCourseCompetenciesPage()}.bind(this)).fail(b.exception)},g.prototype.refreshCourseCompetenciesPage=function(){var c=this._find('input[name="courseid"]').val();e.call([{methodname:"tool_lp_data_for_course_competencies_page",args:{courseid:c}}])[0].done(function(c){f.render("tool_lp/course_competencies_page",c).done(function(b,c){a('[data-region="coursecompetenciespage"]').replaceWith(b),f.runTemplateJS(c),this._dialogue.close()}.bind(this)).fail(b.exception)}.bind(this)).fail(b.exception)},g});
|
||||
define(["jquery","core/notification","tool_lp/dialogue","core/str","core/ajax","core/templates","core/pending"],function(a,b,c,d,e,f,g){var h=function(b){a(b).on("click",this.configureSettings.bind(this))};return h.prototype._dialogue=null,h.prototype.configureSettings=function(e){var h=new g,i=a(e.target).closest("a").data("courseid"),j=a(e.target).closest("a").data("pushratingstouserplans"),k={courseid:i,settings:{pushratingstouserplans:j}};e.preventDefault(),a.when(d.get_string("configurecoursecompetencysettings","tool_lp"),f.render("tool_lp/course_competency_settings",k)).then(function(a,b){return this._dialogue=new c(a,b[0],this.addListeners.bind(this)),this._dialogue}.bind(this)).then(h.resolve)["catch"](b.exception)},h.prototype.addListeners=function(){var a=this._find('[data-action="save"]');a.on("click",this.saveSettings.bind(this));var b=this._find('[data-action="cancel"]');b.on("click",this.cancelChanges.bind(this))},h.prototype.cancelChanges=function(a){a.preventDefault(),this._dialogue.close()},h.prototype._find=function(b){return a('[data-region="coursecompetencysettings"]').find(b)},h.prototype.saveSettings=function(a){var c=new g;a.preventDefault();var d=this._find('input[name="pushratingstouserplans"]:checked').val(),f=this._find('input[name="courseid"]').val(),h={pushratingstouserplans:d};e.call([{methodname:"core_competency_update_course_competency_settings",args:{courseid:f,settings:h}}])[0].then(function(){return this.refreshCourseCompetenciesPage()}.bind(this)).then(c.resolve)["catch"](b.exception)},h.prototype.refreshCourseCompetenciesPage=function(){var c=this._find('input[name="courseid"]').val(),d=new g;e.call([{methodname:"tool_lp_data_for_course_competencies_page",args:{courseid:c}}])[0].then(function(a){return f.render("tool_lp/course_competencies_page",a)}).then(function(b,c){f.replaceNode(a('[data-region="coursecompetenciespage"]'),b,c),this._dialogue.close()}.bind(this)).then(d.resolve)["catch"](b.exception)},h});
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/notification","core/templates","tool_lp/dialogue","tool_lp/event_base","core/str"],function(a,b,c,d,e,f){var g=function(a){e.prototype.constructor.apply(this,[]),this._ratingOptions=a};return g.prototype=Object.create(e.prototype),g.prototype._popup=null,g.prototype._ratingOptions=null,g.prototype._afterRender=function(){var b=this._find('[data-action="rate"]'),c=this._find('[name="rating"]'),d=this._find('[name="comment"]');this._find('[data-action="cancel"]').click(function(a){a.preventDefault(),this._trigger("cancelled"),this.close()}.bind(this)),c.change(function(){var c=a(this);c.val()?b.prop("disabled",!1):b.prop("disabled",!0)}).change(),b.click(function(a){a.preventDefault();var b=c.val();b&&(this._trigger("rated",{rating:b,note:d.val()}),this.close())}.bind(this))},g.prototype.close=function(){this._popup.close(),this._popup=null},g.prototype.display=function(){return this._render().then(function(a){return f.get_string("rate","tool_lp").then(function(b){this._popup=new d(b,a,this._afterRender.bind(this))}.bind(this))}.bind(this)).fail(b.exception)},g.prototype._find=function(b){return a(this._popup.getContent()).find(b)},g.prototype._render=function(){var a={cangrade:this._canGrade,ratings:this._ratingOptions};return c.render("tool_lp/competency_grader",a)},g});
|
||||
define(["jquery","core/notification","core/templates","tool_lp/dialogue","tool_lp/event_base","core/str"],function(a,b,c,d,e,f){var g=function(a){e.prototype.constructor.apply(this,[]),this._ratingOptions=a};return g.prototype=Object.create(e.prototype),g.prototype._popup=null,g.prototype._ratingOptions=null,g.prototype._afterRender=function(){var b=this._find('[data-action="rate"]'),c=this._find('[name="rating"]'),d=this._find('[name="comment"]');this._find('[data-action="cancel"]').click(function(a){a.preventDefault(),this._trigger("cancelled"),this.close()}.bind(this)),c.change(function(){var c=a(this);c.val()?b.prop("disabled",!1):b.prop("disabled",!0)}).change(),b.click(function(a){a.preventDefault();var b=c.val();b&&(this._trigger("rated",{rating:b,note:d.val()}),this.close())}.bind(this))},g.prototype.close=function(){this._popup.close(),this._popup=null},g.prototype.display=function(){return a.when(f.get_string("rate","tool_lp"),this._render()).then(function(a,b){return this._popup=new d(a,b[0],this._afterRender.bind(this)),this._popup}.bind(this))["catch"](b.exception)},g.prototype._find=function(b){return a(this._popup.getContent()).find(b)},g.prototype._render=function(){var a={cangrade:this._canGrade,ratings:this._ratingOptions};return c.render("tool_lp/competency_grader",a)},g});
|
||||
@@ -1 +1 @@
|
||||
define(["jquery","core/notification","core/ajax","core/log","tool_lp/grade_dialogue","tool_lp/event_base","tool_lp/scalevalues"],function(a,b,c,d,e,f,g){var h=function(b,c,d,e,g,h,i){f.prototype.constructor.apply(this,[]);var j=a(b);if(!j.length)throw new Error("Could not find the trigger");this._scaleId=c,this._competencyId=d,this._userId=e,this._planId=g,this._courseId=h,this._chooseStr=i,this._setUp(),j.click(function(a){a.preventDefault(),this._dialogue.display()}.bind(this)),this._planId?(this._methodName="core_competency_grade_competency_in_plan",this._args={competencyid:this._competencyId,planid:this._planId}):this._courseId?(this._methodName="core_competency_grade_competency_in_course",this._args={competencyid:this._competencyId,courseid:this._courseId,userid:this._userId}):(this._methodName="core_competency_grade_competency",this._args={userid:this._userId,competencyid:this._competencyId})};return h.prototype=Object.create(f.prototype),h.prototype._setUp=function(){var a=[],d=this,f=g.get_values(d._scaleId);f.done(function(f){a.push({value:"",name:d._chooseStr});for(var g=0;g<f.length;g++){var h=f[g];a.push({value:h.id,name:h.name})}d._dialogue=new e(a),d._dialogue.on("rated",function(a,e){var f=d._args;f.grade=e.rating,f.note=e.note,c.call([{methodname:d._methodName,args:f,done:function(a){d._trigger("competencyupdated",{args:f,evidence:a})},fail:b.exception}])})}).fail(b.exception)},h.prototype._scaleId=null,h.prototype._competencyId=null,h.prototype._userId=null,h.prototype._planId=null,h.prototype._courseId=null,h.prototype._chooseStr=null,h.prototype._dialogue=null,h});
|
||||
define(["jquery","core/notification","core/ajax","core/log","tool_lp/grade_dialogue","tool_lp/event_base","tool_lp/scalevalues"],function(a,b,c,d,e,f,g){var h=function(b,c,d,e,g,h,i){f.prototype.constructor.apply(this,[]);var j=a(b);if(!j.length)throw new Error("Could not find the trigger");this._scaleId=c,this._competencyId=d,this._userId=e,this._planId=g,this._courseId=h,this._chooseStr=i,this._setUp(),j.click(function(a){a.preventDefault(),this._dialogue.display()}.bind(this)),this._planId?(this._methodName="core_competency_grade_competency_in_plan",this._args={competencyid:this._competencyId,planid:this._planId}):this._courseId?(this._methodName="core_competency_grade_competency_in_course",this._args={competencyid:this._competencyId,courseid:this._courseId,userid:this._userId}):(this._methodName="core_competency_grade_competency",this._args={userid:this._userId,competencyid:this._competencyId})};return h.prototype=Object.create(f.prototype),h.prototype._setUp=function(){var a=[],d=this,f=g.get_values(d._scaleId);f.then(function(b){a.push({value:"",name:d._chooseStr});for(var c=0;c<b.length;c++){var e=b[c];a.push({value:e.id,name:e.name})}return a}).then(function(a){return new e(a)}).then(function(a){return a.on("rated",function(a,e){var f=d._args;f.grade=e.rating,f.note=e.note,c.call([{methodname:d._methodName,args:f,done:function(a){d._trigger("competencyupdated",{args:f,evidence:a})},fail:b.exception}])}),a}).then(function(a){d._dialogue=a}).fail(b.exception)},h.prototype._scaleId=null,h.prototype._competencyId=null,h.prototype._userId=null,h.prototype._planId=null,h.prototype._courseId=null,h.prototype._chooseStr=null,h.prototype._dialogue=null,h});
|
||||
@@ -27,8 +27,9 @@ define(['jquery',
|
||||
'core/templates',
|
||||
'core/str',
|
||||
'tool_lp/competencypicker',
|
||||
'tool_lp/dragdrop-reorder'],
|
||||
function($, notification, ajax, templates, str, Picker, dragdrop) {
|
||||
'tool_lp/dragdrop-reorder',
|
||||
'core/pending'],
|
||||
function($, notification, ajax, templates, str, Picker, dragdrop, Pending) {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@@ -117,6 +118,7 @@ define(['jquery',
|
||||
* Pick a competency
|
||||
*
|
||||
* @method pickCompetency
|
||||
* @return {Promise}
|
||||
*/
|
||||
competencies.prototype.pickCompetency = function() {
|
||||
var self = this;
|
||||
@@ -132,6 +134,7 @@ define(['jquery',
|
||||
self.pickerInstance = new Picker(self.pageContextId, false, pageContextIncludes);
|
||||
self.pickerInstance.on('save', function(e, data) {
|
||||
var compIds = data.competencyIds;
|
||||
var pendingPromise = new Pending();
|
||||
|
||||
if (self.itemtype === "course") {
|
||||
requests = [];
|
||||
@@ -181,17 +184,20 @@ define(['jquery',
|
||||
pagerender = 'tool_lp/plan_page';
|
||||
pageregion = 'plan-page';
|
||||
}
|
||||
ajax.call(requests)[requests.length - 1].then(function(context) {
|
||||
ajax.call(requests)[requests.length - 1]
|
||||
.then(function(context) {
|
||||
return templates.render(pagerender, context);
|
||||
}).then(function(html, js) {
|
||||
$('[data-region="' + pageregion + '"]').replaceWith(html);
|
||||
templates.runTemplateJS(js);
|
||||
})
|
||||
.then(function(html, js) {
|
||||
templates.replaceNode($('[data-region="' + pageregion + '"]'), html, js);
|
||||
return;
|
||||
}).catch(notification.exception);
|
||||
})
|
||||
.then(pendingPromise.resolve)
|
||||
.catch(notification.exception);
|
||||
});
|
||||
}
|
||||
|
||||
self.pickerInstance.display();
|
||||
return self.pickerInstance.display();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -302,6 +308,7 @@ define(['jquery',
|
||||
if (localthis.itemtype == 'course') {
|
||||
// Course completion rule handling.
|
||||
$('[data-region="coursecompetenciespage"]').on('change', 'select[data-field="ruleoutcome"]', function(e) {
|
||||
var pendingPromise = new Pending();
|
||||
var requests = [];
|
||||
var pagerender = 'tool_lp/course_competencies_page';
|
||||
var pageregion = 'coursecompetenciespage';
|
||||
@@ -314,18 +321,24 @@ define(['jquery',
|
||||
args: {courseid: localthis.itemid}}
|
||||
]);
|
||||
|
||||
requests[1].done(function(context) {
|
||||
templates.render(pagerender, context).done(function(html, js) {
|
||||
$('[data-region="' + pageregion + '"]').replaceWith(html);
|
||||
templates.runTemplateJS(js);
|
||||
}).fail(notification.exception);
|
||||
}).fail(notification.exception);
|
||||
requests[1].then(function(context) {
|
||||
return templates.render(pagerender, context);
|
||||
})
|
||||
.then(function(html, js) {
|
||||
return templates.replaceNode($('[data-region="' + pageregion + '"]'), html, js);
|
||||
})
|
||||
.then(pendingPromise.resolve)
|
||||
.catch(notification.exception);
|
||||
});
|
||||
}
|
||||
|
||||
$('[data-region="actions"] button').click(function(e) {
|
||||
var pendingPromise = new Pending();
|
||||
e.preventDefault();
|
||||
localthis.pickCompetency();
|
||||
|
||||
localthis.pickCompetency()
|
||||
.then(pendingPromise.resolve)
|
||||
.catch();
|
||||
});
|
||||
$('[data-action="delete-competency-link"]').click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -33,8 +33,12 @@ define(['jquery',
|
||||
'tool_lp/menubar',
|
||||
'tool_lp/competencypicker',
|
||||
'tool_lp/competency_outcomes',
|
||||
'tool_lp/competencyruleconfig'],
|
||||
function($, url, templates, notification, str, ajax, dragdrop, Ariatree, Dialogue, menubar, Picker, Outcomes, RuleConfig) {
|
||||
'tool_lp/competencyruleconfig',
|
||||
'core/pending',
|
||||
],
|
||||
function(
|
||||
$, url, templates, notification, str, ajax, dragdrop, Ariatree, Dialogue, menubar, Picker, Outcomes, RuleConfig, Pending
|
||||
) {
|
||||
|
||||
// Private variables and functions.
|
||||
/** @var {Object} treeModel - This is an object representing the nodes in the tree. */
|
||||
@@ -412,6 +416,7 @@ define(['jquery',
|
||||
if (!pickerInstance) {
|
||||
pickerInstance = new Picker(pageContextId, relatedTarget.competencyframeworkid);
|
||||
pickerInstance.on('save', function(e, data) {
|
||||
var pendingPromise = new Pending();
|
||||
var compIds = data.competencyIds;
|
||||
|
||||
var calls = [];
|
||||
@@ -436,7 +441,9 @@ define(['jquery',
|
||||
templates.runTemplateJS(js);
|
||||
updatedRelatedCompetencies();
|
||||
return;
|
||||
}).catch(notification.exception);
|
||||
})
|
||||
.then(pendingPromise.resolve)
|
||||
.catch(notification.exception);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,10 @@ define(['jquery',
|
||||
'core/templates',
|
||||
'tool_lp/dialogue',
|
||||
'core/str',
|
||||
'tool_lp/tree'],
|
||||
function($, Notification, Ajax, Templates, Dialogue, Str, Tree) {
|
||||
'tool_lp/tree',
|
||||
'core/pending'
|
||||
],
|
||||
function($, Notification, Ajax, Templates, Dialogue, Str, Tree, Pending) {
|
||||
|
||||
/**
|
||||
* Competency picker class.
|
||||
@@ -157,6 +159,7 @@ define(['jquery',
|
||||
// Add listener for add.
|
||||
self._find('[data-region="competencylinktree"] [data-action="add"]').click(function(e) {
|
||||
e.preventDefault();
|
||||
var pendingPromise = new Pending();
|
||||
if (!self._selectedCompetencies.length) {
|
||||
return;
|
||||
}
|
||||
@@ -168,7 +171,10 @@ define(['jquery',
|
||||
self._trigger('save', {competencyId: self._selectedCompetencies[0]});
|
||||
}
|
||||
|
||||
// The dialogue here is a YUI dialogue and doesn't support Promises at all.
|
||||
// However, it is typically synchronous so this shoudl suffice.
|
||||
self.close();
|
||||
pendingPromise.resolve();
|
||||
});
|
||||
|
||||
// The list of selected competencies will be modified while looping (because of the listeners above).
|
||||
|
||||
@@ -26,8 +26,10 @@ define(['jquery',
|
||||
'tool_lp/dialogue',
|
||||
'core/str',
|
||||
'core/ajax',
|
||||
'core/templates'],
|
||||
function($, notification, Dialogue, str, ajax, templates) {
|
||||
'core/templates',
|
||||
'core/pending'
|
||||
],
|
||||
function($, notification, Dialogue, str, ajax, templates, Pending) {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@@ -48,6 +50,7 @@ define(['jquery',
|
||||
* @method configureSettings
|
||||
*/
|
||||
settingsMod.prototype.configureSettings = function(e) {
|
||||
var pendingPromise = new Pending();
|
||||
var courseid = $(e.target).closest('a').data('courseid');
|
||||
var currentValue = $(e.target).closest('a').data('pushratingstouserplans');
|
||||
var context = {
|
||||
@@ -56,16 +59,21 @@ define(['jquery',
|
||||
};
|
||||
e.preventDefault();
|
||||
|
||||
templates.render('tool_lp/course_competency_settings', context).done(function(html) {
|
||||
str.get_string('configurecoursecompetencysettings', 'tool_lp').done(function(title) {
|
||||
this._dialogue = new Dialogue(
|
||||
title,
|
||||
html,
|
||||
this.addListeners.bind(this)
|
||||
);
|
||||
}.bind(this)).fail(notification.exception);
|
||||
}.bind(this)).fail(notification.exception);
|
||||
$.when(
|
||||
str.get_string('configurecoursecompetencysettings', 'tool_lp'),
|
||||
templates.render('tool_lp/course_competency_settings', context)
|
||||
)
|
||||
.then(function(title, templateResult) {
|
||||
this._dialogue = new Dialogue(
|
||||
title,
|
||||
templateResult[0],
|
||||
this.addListeners.bind(this)
|
||||
);
|
||||
|
||||
return this._dialogue;
|
||||
}.bind(this))
|
||||
.then(pendingPromise.resolve)
|
||||
.catch(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -108,6 +116,7 @@ define(['jquery',
|
||||
* @method saveSettings
|
||||
*/
|
||||
settingsMod.prototype.saveSettings = function(e) {
|
||||
var pendingPromise = new Pending();
|
||||
e.preventDefault();
|
||||
|
||||
var newValue = this._find('input[name="pushratingstouserplans"]:checked').val();
|
||||
@@ -117,9 +126,12 @@ define(['jquery',
|
||||
ajax.call([
|
||||
{methodname: 'core_competency_update_course_competency_settings',
|
||||
args: {courseid: courseId, settings: settings}}
|
||||
])[0].done(function() {
|
||||
this.refreshCourseCompetenciesPage();
|
||||
}.bind(this)).fail(notification.exception);
|
||||
])[0]
|
||||
.then(function() {
|
||||
return this.refreshCourseCompetenciesPage();
|
||||
}.bind(this))
|
||||
.then(pendingPromise.resolve)
|
||||
.catch(notification.exception);
|
||||
|
||||
};
|
||||
|
||||
@@ -131,18 +143,23 @@ define(['jquery',
|
||||
*/
|
||||
settingsMod.prototype.refreshCourseCompetenciesPage = function() {
|
||||
var courseId = this._find('input[name="courseid"]').val();
|
||||
var pendingPromise = new Pending();
|
||||
|
||||
ajax.call([
|
||||
{methodname: 'tool_lp_data_for_course_competencies_page',
|
||||
args: {courseid: courseId}}
|
||||
])[0].done(function(context) {
|
||||
templates.render('tool_lp/course_competencies_page', context).done(function(html, js) {
|
||||
$('[data-region="coursecompetenciespage"]').replaceWith(html);
|
||||
templates.runTemplateJS(js);
|
||||
this._dialogue.close();
|
||||
}.bind(this)).fail(notification.exception);
|
||||
}.bind(this)).fail(notification.exception);
|
||||
])[0]
|
||||
.then(function(context) {
|
||||
return templates.render('tool_lp/course_competencies_page', context);
|
||||
})
|
||||
.then(function(html, js) {
|
||||
templates.replaceNode($('[data-region="coursecompetenciespage"]'), html, js);
|
||||
this._dialogue.close();
|
||||
|
||||
return;
|
||||
}.bind(this))
|
||||
.then(pendingPromise.resolve)
|
||||
.catch(notification.exception);
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/configurecoursecompetencysettings */ settingsMod;
|
||||
|
||||
@@ -102,15 +102,20 @@ define(['jquery',
|
||||
* @return {Promise}
|
||||
*/
|
||||
Grade.prototype.display = function() {
|
||||
return this._render().then(function(html) {
|
||||
return Str.get_string('rate', 'tool_lp').then(function(title) {
|
||||
this._popup = new Dialogue(
|
||||
title,
|
||||
html,
|
||||
this._afterRender.bind(this)
|
||||
);
|
||||
}.bind(this));
|
||||
}.bind(this)).fail(Notification.exception);
|
||||
return $.when(
|
||||
Str.get_string('rate', 'tool_lp'),
|
||||
this._render()
|
||||
)
|
||||
.then(function(title, templateResult) {
|
||||
this._popup = new Dialogue(
|
||||
title,
|
||||
templateResult[0],
|
||||
this._afterRender.bind(this)
|
||||
);
|
||||
|
||||
return this._popup;
|
||||
}.bind(this))
|
||||
.catch(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -95,7 +95,7 @@ define(['jquery',
|
||||
self = this;
|
||||
|
||||
var promise = ScaleValues.get_values(self._scaleId);
|
||||
promise.done(function(scalevalues) {
|
||||
promise.then(function(scalevalues) {
|
||||
options.push({
|
||||
value: '',
|
||||
name: self._chooseStr
|
||||
@@ -109,8 +109,13 @@ define(['jquery',
|
||||
});
|
||||
}
|
||||
|
||||
self._dialogue = new GradeDialogue(options);
|
||||
self._dialogue.on('rated', function(e, data) {
|
||||
return options;
|
||||
})
|
||||
.then(function(options) {
|
||||
return new GradeDialogue(options);
|
||||
})
|
||||
.then(function(dialogue) {
|
||||
dialogue.on('rated', function(e, data) {
|
||||
var args = self._args;
|
||||
args.grade = data.rating;
|
||||
args.note = data.note;
|
||||
@@ -123,7 +128,15 @@ define(['jquery',
|
||||
fail: notification.exception
|
||||
}]);
|
||||
});
|
||||
}).fail(notification.exception);
|
||||
|
||||
return dialogue;
|
||||
})
|
||||
.then(function(dialogue) {
|
||||
self._dialogue = dialogue;
|
||||
|
||||
return;
|
||||
})
|
||||
.fail(notification.exception);
|
||||
};
|
||||
|
||||
/** @type {Number} The scale id for this competency. */
|
||||
|
||||
@@ -422,6 +422,7 @@ class external extends external_api {
|
||||
'comppath' => competency_path_exporter::get_read_structure(),
|
||||
))),
|
||||
'manageurl' => new external_value(PARAM_LOCALURL, 'Url to the manage competencies page.'),
|
||||
'pluginbaseurl' => new external_value(PARAM_LOCALURL, 'Url to the course competencies page.'),
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ class course_competencies_page implements renderable, templatable {
|
||||
$data->courseid = $this->courseid;
|
||||
$data->pagecontextid = $this->context->id;
|
||||
$data->competencies = array();
|
||||
$data->pluginbaseurl = (new moodle_url('/admin/tool/lp'))->out(true);
|
||||
|
||||
$gradable = is_enrolled($this->context, $USER, 'moodle/competency:coursecompetencygradable');
|
||||
if ($gradable) {
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<div class="clearfix"></div>
|
||||
{{/canmanagecoursecompetencies}}
|
||||
{{#competency}}
|
||||
<a href="{{pluginbaseurl}}user_competency_in_course.php?courseid={{courseid}}&competencyid={{competency.id}}&userid={{gradableuserid}}"
|
||||
<a href="{{pluginbaseurl}}/user_competency_in_course.php?courseid={{courseid}}&competencyid={{competency.id}}&userid={{gradableuserid}}"
|
||||
id="competency-info-link-{{competency.id}}"
|
||||
title="{{#str}}viewdetails, tool_lp{{/str}}">
|
||||
<p><strong>{{{competency.shortname}}} <em>{{competency.idnumber}}</em></strong></p>
|
||||
|
||||
@@ -6,6 +6,7 @@ Feature: Manage competency frameworks
|
||||
|
||||
Background:
|
||||
Given I log in as "admin"
|
||||
And I change window size to "small"
|
||||
And I am on site homepage
|
||||
|
||||
Scenario: Create a new framework
|
||||
|
||||
@@ -6,6 +6,7 @@ Feature: Manage plearning plan templates
|
||||
|
||||
Background:
|
||||
Given I log in as "admin"
|
||||
And I change window size to "small"
|
||||
And I am on site homepage
|
||||
|
||||
Scenario: Create a new learning plan template
|
||||
|
||||
@@ -672,7 +672,7 @@ class manager {
|
||||
$attachment->charset = $partdata->getCharset();
|
||||
$attachment->description = $partdata->getDescription();
|
||||
$attachment->contentid = $partdata->getContentId();
|
||||
$attachment->filesize = $messagedata->getBodyPartSize($part);
|
||||
$attachment->filesize = $partdata->getBytes();
|
||||
|
||||
if (!empty($CFG->antiviruses)) {
|
||||
mtrace("--> Attempting virus scan of '{$attachment->filename}'");
|
||||
|
||||
@@ -99,7 +99,7 @@ class api {
|
||||
$lang = array();
|
||||
if (!empty($addoninfo['lang'])) {
|
||||
$stringmanager = get_string_manager();
|
||||
$langs = $stringmanager->get_list_of_translations();
|
||||
$langs = $stringmanager->get_list_of_translations(true);
|
||||
foreach ($langs as $langid => $langname) {
|
||||
foreach ($addoninfo['lang'] as $stringinfo) {
|
||||
$lang[$langid][$stringinfo[0]] =
|
||||
|
||||
@@ -30,7 +30,7 @@ require_once($CFG->libdir . '/externallib.php');
|
||||
|
||||
$serviceshortname = required_param('service', PARAM_ALPHANUMEXT);
|
||||
$passport = required_param('passport', PARAM_RAW); // Passport send from the app to validate the response URL.
|
||||
$urlscheme = optional_param('urlscheme', 'moodlemobile', PARAM_NOTAGS); // The URL scheme the app supports.
|
||||
$urlscheme = optional_param('urlscheme', 'moodlemobile', PARAM_ALPHANUM); // The URL scheme the app supports.
|
||||
$confirmed = optional_param('confirmed', false, PARAM_BOOL); // If we are being redirected after user confirmation.
|
||||
$oauthsso = optional_param('oauthsso', 0, PARAM_INT); // Id of the OpenID issuer (for OAuth direct SSO).
|
||||
|
||||
|
||||
@@ -66,6 +66,12 @@ function tool_mobile_create_app_download_url() {
|
||||
}
|
||||
|
||||
$downloadurl = new moodle_url($mobilesettings->setuplink);
|
||||
|
||||
// Do not update the URL if it is a custom one (we may break it completely).
|
||||
if ($mobilesettings->setuplink != 'https://download.moodle.org/mobile') {
|
||||
return $downloadurl;
|
||||
}
|
||||
|
||||
$downloadurl->param('version', $CFG->version);
|
||||
$downloadurl->param('lang', current_language());
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ if ($hassiteconfig) {
|
||||
|
||||
$temp->add(new admin_setting_configtext('tool_mobile/forcedurlscheme',
|
||||
new lang_string('forcedurlscheme_key', 'tool_mobile'),
|
||||
new lang_string('forcedurlscheme', 'tool_mobile'), '', PARAM_NOTAGS));
|
||||
new lang_string('forcedurlscheme', 'tool_mobile'), '', PARAM_ALPHANUM));
|
||||
|
||||
$ADMIN->add('mobileapp', $temp);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -123,9 +123,12 @@ 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');
|
||||
$showrequireconfirm = $this->_customdata['showrequireconfirm'] ?? false;
|
||||
if ($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,25 @@ if ($action == 'edit') {
|
||||
$PAGE->navbar->add(get_string('createnewissuer', 'tool_oauth2'));
|
||||
}
|
||||
|
||||
$mform = new \tool_oauth2\form\issuer(null, ['persistent' => $issuer]);
|
||||
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);
|
||||
} else {
|
||||
$showrequireconfirm = optional_param('showrequireconfirm', false, PARAM_BOOL);
|
||||
}
|
||||
|
||||
$mform = new \tool_oauth2\form\issuer(null, ['persistent' => $issuer, 'showrequireconfirm' => $showrequireconfirm]);
|
||||
}
|
||||
|
||||
if ($mform && $mform->is_cancelled()) {
|
||||
@@ -157,21 +175,31 @@ 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'));
|
||||
|
||||
// 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();
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/ajax","core/notification","core/modal_factory","core/modal_events"],function(a,b,c,d,e){var f={VIEW_POLICY:'[data-action="view"]'},g=function(){this.registerEvents()};return g.prototype.registerEvents=function(){a(f.VIEW_POLICY).click(function(f){f.preventDefault();var g=a(this).data("versionid"),h=a(this).data("behalfid"),i={versionid:g,behalfid:h},j={methodname:"tool_policy_get_policy_version",args:i},k=a.Deferred(),l=a.Deferred(),m=d.create({title:k,body:l,large:!0}).then(function(a){return a.getRoot().on(e.hidden,function(){a.destroy()}),a}).then(function(a){return a.show(),a})["catch"](c.exception),n=b.call([j]);a.when(n[0]).then(function(a){if(a.result.policy)return k.resolve(a.result.policy.name),l.resolve(a.result.policy.content),a;throw new Error(a.warnings[0].message)})["catch"](function(a){return m.then(function(a){return a.hide(),a.destroy(),a})["catch"](c.exception),c.addNotification({message:a,type:"error"})})})},{init:function(){return new g}}});
|
||||
define(["jquery","core/ajax","core/notification","core/modal_factory","core/modal_events"],function(a,b,c,d,e){var f=function(a){this.registerEvents(a)};return f.prototype.registerEvents=function(f){f.on("click",function(f){f.preventDefault();var g=a(this).data("versionid"),h=a(this).data("behalfid"),i={versionid:g,behalfid:h},j={methodname:"tool_policy_get_policy_version",args:i},k=a.Deferred(),l=a.Deferred(),m=d.create({title:k,body:l,large:!0}).then(function(a){return a.getRoot().on(e.hidden,function(){a.destroy()}),a}).then(function(a){return a.show(),a})["catch"](c.exception),n=b.call([j]);a.when(n[0]).then(function(a){if(a.result.policy)return k.resolve(a.result.policy.name),l.resolve(a.result.policy.content),a;throw new Error(a.warnings[0].message)})["catch"](function(a){return m.then(function(a){return a.hide(),a.destroy(),a})["catch"](c.exception),c.addNotification({message:a,type:"error"})})})},{init:function(b){return b=a(b),new f(b)}}});
|
||||
@@ -29,27 +29,18 @@ define([
|
||||
'core/modal_events'],
|
||||
function($, Ajax, Notification, ModalFactory, ModalEvents) {
|
||||
|
||||
/**
|
||||
* List of action selectors.
|
||||
*
|
||||
* @type {{VIEW_POLICY: string}}
|
||||
*/
|
||||
var ACTIONS = {
|
||||
VIEW_POLICY: '[data-action="view"]'
|
||||
};
|
||||
|
||||
/**
|
||||
* PolicyActions class.
|
||||
*/
|
||||
var PolicyActions = function() {
|
||||
this.registerEvents();
|
||||
var PolicyActions = function(root) {
|
||||
this.registerEvents(root);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register event listeners.
|
||||
*/
|
||||
PolicyActions.prototype.registerEvents = function() {
|
||||
$(ACTIONS.VIEW_POLICY).click(function(e) {
|
||||
PolicyActions.prototype.registerEvents = function(root) {
|
||||
root.on("click", function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var versionid = $(this).data('versionid');
|
||||
@@ -127,8 +118,9 @@ function($, Ajax, Notification, ModalFactory, ModalEvents) {
|
||||
* @method init
|
||||
* @return {PolicyActions}
|
||||
*/
|
||||
'init': function() {
|
||||
return new PolicyActions();
|
||||
'init': function(root) {
|
||||
root = $(root);
|
||||
return new PolicyActions(root);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -97,7 +97,7 @@ class accept_policy extends \moodleform {
|
||||
}
|
||||
}
|
||||
|
||||
$PAGE->requires->js_call_amd('tool_policy/policyactions', 'init');
|
||||
$PAGE->requires->js_call_amd('tool_policy/policyactions', 'init', ['[data-action="view"]']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,4 +178,4 @@ class accept_policy extends \moodleform {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +232,10 @@ class page_agreedocs implements renderable, templatable {
|
||||
];
|
||||
redirect(new moodle_url('/admin/tool/policy/view.php', $urlparams));
|
||||
}
|
||||
} else {
|
||||
// Update the policyagreed for the user to avoid infinite loop because there are no policies to-be-accepted.
|
||||
api::update_policyagreed($userid);
|
||||
$this->redirect_to_previous_url();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ require(['jquery', 'tool_policy/jquery-eu-cookie-law-popup', 'tool_policy/policy
|
||||
"<ul>{{#policies}}" +
|
||||
"<li>" +
|
||||
"<a href=\"{{pluginbaseurl}}/view.php?versionid={{id}}{{#returnurl}}&returnurl={{.}}{{/returnurl}}\" " +
|
||||
" data-action=\"view\" data-versionid=\"{{id}}\" data-behalfid=\"1\" >" +
|
||||
" data-action=\"view-guest\" data-versionid=\"{{id}}\" data-behalfid=\"1\" >" +
|
||||
"{{{name}}}" +
|
||||
"</a>" +
|
||||
"</li>" +
|
||||
@@ -81,7 +81,7 @@ require(['jquery', 'tool_policy/jquery-eu-cookie-law-popup', 'tool_policy/policy
|
||||
{{/haspolicies}}
|
||||
|
||||
// Initialise the JS for the modal window which displays the policy versions.
|
||||
ActionsMod.init();
|
||||
ActionsMod.init('[data-action="view-guest"]');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -123,6 +123,6 @@
|
||||
{{#js}}
|
||||
// Initialise the JS for the modal window which displays the policy versions.
|
||||
require(['tool_policy/policyactions'], function(ActionsMod) {
|
||||
ActionsMod.init();
|
||||
ActionsMod.init('[data-action="view"]');
|
||||
});
|
||||
{{/js}}
|
||||
|
||||
@@ -73,6 +73,6 @@
|
||||
{{#js}}
|
||||
// Initialise the JS for the modal window which displays the policy versions.
|
||||
require(['tool_policy/policyactions'], function(ActionsMod) {
|
||||
ActionsMod.init();
|
||||
ActionsMod.init('[data-action="view"]');
|
||||
});
|
||||
{{/js}}
|
||||
|
||||
@@ -80,7 +80,7 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
|
||||
And I press "Next"
|
||||
And I set the field "I agree to the This site policy" to "1"
|
||||
And I press "Next"
|
||||
And I should not see "Next"
|
||||
And I should see "Course overview"
|
||||
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
|
||||
And I click on "1 of 4 (25%)" "link" in the "This site policy" "table_row"
|
||||
And I click on "Consent not given" "link" in the "User One" "table_row"
|
||||
@@ -292,3 +292,23 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
|
||||
And I should see "2 of 2" in the "User One" "table_row"
|
||||
And "Consent given on behalf of user" "icon" should exist in the "User Two" "table_row"
|
||||
And I should see "2 of 2" in the "User Two" "table_row"
|
||||
|
||||
Scenario: View acceptances made by users on their own after inactivating a policy
|
||||
Given I log in as "user1"
|
||||
And I should see "This site policy"
|
||||
And I should not see "Course overview"
|
||||
And I press "Next"
|
||||
And I set the field "I agree to the This site policy" to "1"
|
||||
And I press "Next"
|
||||
And I should see "Course overview"
|
||||
And I log out
|
||||
And I log in as "admin"
|
||||
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
|
||||
And I click on "Actions" "link_or_button" in the "This privacy policy" "table_row"
|
||||
And I click on "Set status to \"Active\"" "link" in the "This privacy policy" "table_row"
|
||||
And I press "Continue"
|
||||
And I click on "Set status to \"Inactive\"" "link" in the "This privacy policy" "table_row"
|
||||
And I press "Continue"
|
||||
And I log out
|
||||
When I log in as "user1"
|
||||
Then I should see "Course overview"
|
||||
|
||||
@@ -108,6 +108,16 @@ class category_bin extends base_bin {
|
||||
|
||||
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
||||
|
||||
// As far as recycle bin is using MODE_AUTOMATED, it observes the backup_auto_storage
|
||||
// setting (storing backups @ real location. For recycle bin we want to ensure that
|
||||
// backup files are always stored in Moodle file area. In order to achieve that, we
|
||||
// hack the setting here via $CFG->forced_plugin_settings, so it won't interfere other
|
||||
// operations. See MDL-65218 for more information.
|
||||
// This hack will be removed once recycle bin switches to use its own backup mode, with
|
||||
// own preferences and 100% appart from MODLE_AUTOMATED.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
$CFG->forced_plugin_settings['backup'] = ['backup_auto_storage' => 0];
|
||||
|
||||
// Backup the course.
|
||||
$user = get_admin();
|
||||
$controller = new \backup_controller(
|
||||
@@ -115,11 +125,15 @@ class category_bin extends base_bin {
|
||||
$course->id,
|
||||
\backup::FORMAT_MOODLE,
|
||||
\backup::INTERACTIVE_NO,
|
||||
\backup::MODE_GENERAL,
|
||||
\backup::MODE_AUTOMATED,
|
||||
$user->id
|
||||
);
|
||||
$controller->execute_plan();
|
||||
|
||||
// We don't need the forced setting anymore, hence unsetting it.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
unset($CFG->forced_plugin_settings['backup']);
|
||||
|
||||
// Grab the result.
|
||||
$result = $controller->get_results();
|
||||
if (!isset($result['backup_destination'])) {
|
||||
@@ -233,7 +247,7 @@ class category_bin extends base_bin {
|
||||
$tempdir,
|
||||
$course->id,
|
||||
\backup::INTERACTIVE_NO,
|
||||
\backup::MODE_GENERAL,
|
||||
\backup::MODE_AUTOMATED,
|
||||
$user->id,
|
||||
\backup::TARGET_NEW_COURSE
|
||||
);
|
||||
|
||||
@@ -112,6 +112,16 @@ class course_bin extends base_bin {
|
||||
return;
|
||||
}
|
||||
|
||||
// As far as recycle bin is using MODE_AUTOMATED, it observes the backup_auto_storage
|
||||
// setting (storing backups @ real location. For recycle bin we want to ensure that
|
||||
// backup files are always stored in Moodle file area. In order to achieve that, we
|
||||
// hack the setting here via $CFG->forced_plugin_settings, so it won't interfere other
|
||||
// operations. See MDL-65218 for more information.
|
||||
// This hack will be removed once recycle bin switches to use its own backup mode, with
|
||||
// own preferences and 100% appart from MODLE_AUTOMATED.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
$CFG->forced_plugin_settings['backup'] = ['backup_auto_storage' => 0];
|
||||
|
||||
// Backup the activity.
|
||||
$user = get_admin();
|
||||
$controller = new \backup_controller(
|
||||
@@ -119,11 +129,15 @@ class course_bin extends base_bin {
|
||||
$cm->id,
|
||||
\backup::FORMAT_MOODLE,
|
||||
\backup::INTERACTIVE_NO,
|
||||
\backup::MODE_GENERAL,
|
||||
\backup::MODE_AUTOMATED,
|
||||
$user->id
|
||||
);
|
||||
$controller->execute_plan();
|
||||
|
||||
// We don't need the forced setting anymore, hence unsetting it.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
unset($CFG->forced_plugin_settings['backup']);
|
||||
|
||||
// Grab the result.
|
||||
$result = $controller->get_results();
|
||||
if (!isset($result['backup_destination'])) {
|
||||
@@ -224,7 +238,7 @@ class course_bin extends base_bin {
|
||||
$tempdir,
|
||||
$this->_courseid,
|
||||
\backup::INTERACTIVE_NO,
|
||||
\backup::MODE_GENERAL,
|
||||
\backup::MODE_AUTOMATED,
|
||||
$user->id,
|
||||
\backup::TARGET_EXISTING_ADDING
|
||||
);
|
||||
|
||||
@@ -176,4 +176,116 @@ class tool_recyclebin_category_bin_tests extends advanced_testcase {
|
||||
$course = reset($courses);
|
||||
$this->assertEquals('Test course 2', $course->fullname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider for test_course_restore_with_userdata() and test_course_restore_without_userdata()
|
||||
*
|
||||
* Used to verify that recycle bin is immune to various settings. Provides plugin, name, value for
|
||||
* direct usage with set_config()
|
||||
*/
|
||||
public function recycle_bin_settings_provider() {
|
||||
return [
|
||||
'backup/backup_auto_storage moodle' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 0],
|
||||
]],
|
||||
|
||||
'backup/backup_auto_storage external' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 1],
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
|
||||
]],
|
||||
|
||||
'backup/backup_auto_storage mixed' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 2],
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
|
||||
]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user data is restored when course is restored.
|
||||
*
|
||||
* @dataProvider recycle_bin_settings_provider
|
||||
* @param array $settings array of plugin, name, value stdClass().
|
||||
*/
|
||||
public function test_course_restore_with_userdata($settings) {
|
||||
global $DB;
|
||||
|
||||
// Force configuration changes from provider.
|
||||
foreach ($settings as $setting) {
|
||||
// Need to create a directory for backup_auto_destination.
|
||||
if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
|
||||
$setting->value = make_request_directory();
|
||||
}
|
||||
set_config($setting->name, $setting->value, $setting->plugin);
|
||||
}
|
||||
|
||||
// We want user data to be included for this test.
|
||||
set_config('backup_auto_users', true, 'backup');
|
||||
|
||||
$student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
|
||||
|
||||
// Delete course.
|
||||
delete_course($this->course, false);
|
||||
$this->assertFalse($DB->record_exists('course', ['id' => $this->course->id]));
|
||||
|
||||
// Verify there is now a backup @ cat recycle bin file area.
|
||||
$recyclebin = new \tool_recyclebin\category_bin($this->course->category);
|
||||
$this->assertEquals(1, count($recyclebin->get_items()));
|
||||
|
||||
// Restore the recycle bin item.
|
||||
$recyclebin->restore_item(current($recyclebin->get_items()));
|
||||
|
||||
// Get the new course.
|
||||
$newcourse = $DB->get_record('course', ['shortname' => $this->course->shortname], '*', MUST_EXIST);
|
||||
|
||||
// Check that it was removed from the recycle bin.
|
||||
$this->assertEquals(0, count($recyclebin->get_items()));
|
||||
|
||||
// Verify that student DOES continue enrolled.
|
||||
$this->assertTrue(is_enrolled(context_course::instance($newcourse->id), $student->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user data is not restored when course is restored.
|
||||
*
|
||||
* @dataProvider recycle_bin_settings_provider
|
||||
* @param array $settings array of plugin, name, value stdClass().
|
||||
*/
|
||||
public function test_course_restore_without_userdata($settings) {
|
||||
global $DB;
|
||||
|
||||
// Force configuration changes from provider.
|
||||
foreach ($settings as $setting) {
|
||||
// Need to create a directory for backup_auto_destination.
|
||||
if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
|
||||
$setting->value = make_request_directory();
|
||||
}
|
||||
set_config($setting->name, $setting->value, $setting->plugin);
|
||||
}
|
||||
|
||||
// We want user data to be included for this test.
|
||||
set_config('backup_auto_users', false, 'backup');
|
||||
|
||||
$student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
|
||||
|
||||
// Delete course.
|
||||
delete_course($this->course, false);
|
||||
$this->assertFalse($DB->record_exists('course', ['id' => $this->course->id]));
|
||||
|
||||
// Verify there is now a backup @ cat recycle bin file area.
|
||||
$recyclebin = new \tool_recyclebin\category_bin($this->course->category);
|
||||
$this->assertEquals(1, count($recyclebin->get_items()));
|
||||
|
||||
// Restore the recycle bin item.
|
||||
$recyclebin->restore_item(current($recyclebin->get_items()));
|
||||
|
||||
// Get the new course.
|
||||
$newcourse = $DB->get_record('course', ['shortname' => $this->course->shortname], '*', MUST_EXIST);
|
||||
|
||||
// Check that it was removed from the recycle bin.
|
||||
$this->assertEquals(0, count($recyclebin->get_items()));
|
||||
|
||||
// Verify that student DOES NOT continue enrolled.
|
||||
$this->assertFalse(is_enrolled(context_course::instance($newcourse->id), $student->id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class tool_recyclebin_course_bin_tests extends advanced_testcase {
|
||||
|
||||
$this->course = $this->getDataGenerator()->create_course();
|
||||
$this->quiz = $this->getDataGenerator()->get_plugin_generator('mod_quiz')->create_instance(array(
|
||||
'course' => $this->course->id
|
||||
'course' => $this->course->id, 'grade' => 100.0, 'sumgrades' => 1
|
||||
));
|
||||
}
|
||||
|
||||
@@ -173,4 +173,147 @@ class tool_recyclebin_course_bin_tests extends advanced_testcase {
|
||||
$deletedbook = reset($items);
|
||||
$this->assertEquals($book->name, $deletedbook->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider for test_coursemodule_restore_with_userdata() and test_coursemodule_restore_without_userdata()
|
||||
*
|
||||
* Used to verify that recycle bin is immune to various settings. Provides plugin, name, value for
|
||||
* direct usage with set_config()
|
||||
*/
|
||||
public function recycle_bin_settings_provider() {
|
||||
return [
|
||||
'backup/backup_auto_storage moodle' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 0],
|
||||
]],
|
||||
|
||||
'backup/backup_auto_storage external' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 1],
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
|
||||
]],
|
||||
|
||||
'backup/backup_auto_storage mixed' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 2],
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
|
||||
]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user data is restored when module is restored.
|
||||
*
|
||||
* @dataProvider recycle_bin_settings_provider
|
||||
* @param array $settings array of plugin, name, value stdClass().
|
||||
*/
|
||||
public function test_coursemodule_restore_with_userdata($settings) {
|
||||
// Force configuration changes from provider.
|
||||
foreach ($settings as $setting) {
|
||||
// Need to create a directory for backup_auto_destination.
|
||||
if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
|
||||
$setting->value = make_request_directory();
|
||||
}
|
||||
set_config($setting->name, $setting->value, $setting->plugin);
|
||||
}
|
||||
|
||||
$student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
|
||||
$this->setUser($student);
|
||||
|
||||
set_config('backup_auto_users', true, 'backup');
|
||||
$this->create_quiz_attempt($this->quiz, $student);
|
||||
|
||||
// Delete quiz.
|
||||
$cm = get_coursemodule_from_instance('quiz', $this->quiz->id);
|
||||
course_delete_module($cm->id);
|
||||
phpunit_util::run_all_adhoc_tasks();
|
||||
$quizzes = get_coursemodules_in_course('quiz', $this->course->id);
|
||||
$this->assertEquals(0, count($quizzes));
|
||||
|
||||
// Restore quiz.
|
||||
$recyclebin = new \tool_recyclebin\course_bin($this->course->id);
|
||||
foreach ($recyclebin->get_items() as $item) {
|
||||
$recyclebin->restore_item($item);
|
||||
}
|
||||
$quizzes = get_coursemodules_in_course('quiz', $this->course->id);
|
||||
$this->assertEquals(1, count($quizzes));
|
||||
$cm = array_pop($quizzes);
|
||||
|
||||
// Check if user quiz attempt data is restored.
|
||||
$attempts = quiz_get_user_attempts($cm->instance, $student->id);
|
||||
$this->assertEquals(1, count($attempts));
|
||||
$attempt = array_pop($attempts);
|
||||
$attemptobj = quiz_attempt::create($attempt->id);
|
||||
$this->assertEquals($student->id, $attemptobj->get_userid());
|
||||
$this->assertEquals(true, $attemptobj->is_finished());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user data is not restored when module is restored.
|
||||
*
|
||||
* @dataProvider recycle_bin_settings_provider
|
||||
* @param array $settings array of plugin, name, value stdClass().
|
||||
*/
|
||||
public function test_coursemodule_restore_without_userdata($settings) {
|
||||
// Force configuration changes from provider.
|
||||
foreach ($settings as $setting) {
|
||||
// Need to create a directory for backup_auto_destination.
|
||||
if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
|
||||
$setting->value = make_request_directory();
|
||||
}
|
||||
set_config($setting->name, $setting->value, $setting->plugin);
|
||||
}
|
||||
|
||||
$student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
|
||||
$this->setUser($student);
|
||||
|
||||
set_config('backup_auto_users', false, 'backup');
|
||||
$this->create_quiz_attempt($this->quiz, $student);
|
||||
|
||||
// Delete quiz.
|
||||
$cm = get_coursemodule_from_instance('quiz', $this->quiz->id);
|
||||
course_delete_module($cm->id);
|
||||
phpunit_util::run_all_adhoc_tasks();
|
||||
$quizzes = get_coursemodules_in_course('quiz', $this->course->id);
|
||||
$this->assertEquals(0, count($quizzes));
|
||||
|
||||
// Restore quiz.
|
||||
$recyclebin = new \tool_recyclebin\course_bin($this->course->id);
|
||||
foreach ($recyclebin->get_items() as $item) {
|
||||
$recyclebin->restore_item($item);
|
||||
}
|
||||
$quizzes = get_coursemodules_in_course('quiz', $this->course->id);
|
||||
$this->assertEquals(1, count($quizzes));
|
||||
$cm = array_pop($quizzes);
|
||||
|
||||
// Check if user quiz attempt data is restored.
|
||||
$attempts = quiz_get_user_attempts($cm->instance, $student->id);
|
||||
$this->assertEquals(0, count($attempts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a question to quiz and create a quiz attempt.
|
||||
* @param \stdClass $quiz Quiz
|
||||
* @param \stdClass $student User
|
||||
* @throws coding_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
private function create_quiz_attempt($quiz, $student) {
|
||||
// Add Question.
|
||||
$questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
$cat = $questiongenerator->create_question_category();
|
||||
$numq = $questiongenerator->create_question('numerical', null, array('category' => $cat->id));
|
||||
quiz_add_quiz_question($numq->id, $quiz);
|
||||
|
||||
// Create quiz attempt.
|
||||
$quizobj = quiz::create($quiz->id, $student->id);
|
||||
$quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
|
||||
$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
|
||||
$timenow = time();
|
||||
$attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $student->id);
|
||||
quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
|
||||
quiz_attempt_save_started($quizobj, $quba, $attempt);
|
||||
$attemptobj = quiz_attempt::create($attempt->id);
|
||||
$tosubmit = array(1 => array('answer' => '0'));
|
||||
$attemptobj->process_submitted_actions($timenow, false, $tosubmit);
|
||||
$attemptobj = quiz_attempt::create($attempt->id);
|
||||
$attemptobj->process_finish($timenow, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,6 +183,7 @@ function uu_validate_user_upload_columns(csv_import_reader $cir, $stdfields, $pr
|
||||
$processed = array();
|
||||
foreach ($columns as $key=>$unused) {
|
||||
$field = $columns[$key];
|
||||
$field = trim($field);
|
||||
$lcfield = core_text::strtolower($field);
|
||||
if (in_array($field, $stdfields) or in_array($lcfield, $stdfields)) {
|
||||
// standard fields are only lowercase
|
||||
|
||||
@@ -38,9 +38,6 @@ class load_xml_file extends XMLDBAction {
|
||||
$this->can_subaction = ACTION_NONE;
|
||||
//$this->can_subaction = ACTION_HAVE_SUBACTIONS;
|
||||
|
||||
// Set own custom attributes
|
||||
$this->sesskey_protected = false; // This action doesn't need sesskey protection
|
||||
|
||||
// Get needed strings
|
||||
$this->loadStrings(array(
|
||||
// 'key' => 'module',
|
||||
|
||||
@@ -169,7 +169,7 @@ class main_view extends XMLDBAction {
|
||||
file_exists($key . '/install.xml') &&
|
||||
is_readable($key . '/install.xml') &&
|
||||
empty($dbdir->xml_loaded)) {
|
||||
$b .= '<a href="index.php?action=load_xml_file&dir=' . urlencode(str_replace($CFG->dirroot, '', $key)) . '&time=' . time() . '&postaction=main_view#lastused">[' . $this->str['load'] . ']</a>';
|
||||
$b .= '<a href="index.php?action=load_xml_file&sesskey=' . sesskey() . '&dir=' . urlencode(str_replace($CFG->dirroot, '', $key)) . '&time=' . time() . '&postaction=main_view#lastused">[' . $this->str['load'] . ']</a>';
|
||||
} else {
|
||||
$b .= '[' . $this->str['load'] . ']';
|
||||
}
|
||||
@@ -239,7 +239,7 @@ class main_view extends XMLDBAction {
|
||||
is_readable($key . '/install.xml') &&
|
||||
!empty($dbdir->xml_loaded) &&
|
||||
empty($dbdir->xml_changed)) {
|
||||
$b .= '<a href="index.php?action=unload_xml_file&dir=' . urlencode(str_replace($CFG->dirroot, '', $key)) . '&time=' . time() . '&postaction=main_view#lastused">[' . $this->str['unload'] . ']</a>';
|
||||
$b .= '<a href="index.php?action=unload_xml_file&sesskey=' . sesskey() . '&dir=' . urlencode(str_replace($CFG->dirroot, '', $key)) . '&time=' . time() . '&postaction=main_view#lastused">[' . $this->str['unload'] . ']</a>';
|
||||
} else {
|
||||
$b .= '[' . $this->str['unload'] . ']';
|
||||
}
|
||||
|
||||
@@ -35,9 +35,6 @@ class unload_xml_file extends XMLDBAction {
|
||||
function init() {
|
||||
parent::init();
|
||||
|
||||
// Set own custom attributes
|
||||
$this->sesskey_protected = false; // This action doesn't need sesskey protection
|
||||
|
||||
// Get needed strings
|
||||
$this->loadStrings(array(
|
||||
// 'key' => 'module',
|
||||
|
||||
@@ -577,9 +577,14 @@ abstract class base {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Remove samples the target consider invalid.
|
||||
$this->analysabletarget->add_sample_data($samplesdata);
|
||||
$this->analysabletarget->filter_out_invalid_samples($sampleids, $analysable, $includetarget);
|
||||
try {
|
||||
// Remove samples the target consider invalid.
|
||||
$this->analysabletarget->add_sample_data($samplesdata);
|
||||
$this->analysabletarget->filter_out_invalid_samples($sampleids, $analysable, $includetarget);
|
||||
} catch (\Throwable $e) {
|
||||
$dataset->close_process();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (!$sampleids) {
|
||||
$result->status = \core_analytics\model::NO_DATASET;
|
||||
@@ -588,19 +593,24 @@ abstract class base {
|
||||
return $result;
|
||||
}
|
||||
|
||||
foreach ($this->indicators as $key => $indicator) {
|
||||
// The analyser attaches the main entities the sample depends on and are provided to the
|
||||
// indicator to calculate the sample.
|
||||
$this->indicators[$key]->add_sample_data($samplesdata);
|
||||
}
|
||||
try {
|
||||
foreach ($this->indicators as $key => $indicator) {
|
||||
// The analyser attaches the main entities the sample depends on and are provided to the
|
||||
// indicator to calculate the sample.
|
||||
$this->indicators[$key]->add_sample_data($samplesdata);
|
||||
}
|
||||
|
||||
// Here we start the memory intensive process that will last until $data var is
|
||||
// unset (until the method is finished basically).
|
||||
if ($includetarget) {
|
||||
$data = $timesplitting->calculate($sampleids, $this->get_samples_origin(), $this->indicators, $ranges,
|
||||
$this->analysabletarget);
|
||||
} else {
|
||||
$data = $timesplitting->calculate($sampleids, $this->get_samples_origin(), $this->indicators, $ranges);
|
||||
// Here we start the memory intensive process that will last until $data var is
|
||||
// unset (until the method is finished basically).
|
||||
if ($includetarget) {
|
||||
$data = $timesplitting->calculate($sampleids, $this->get_samples_origin(), $this->indicators, $ranges,
|
||||
$this->analysabletarget);
|
||||
} else {
|
||||
$data = $timesplitting->calculate($sampleids, $this->get_samples_origin(), $this->indicators, $ranges);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$dataset->close_process();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (!$data) {
|
||||
@@ -610,11 +620,16 @@ abstract class base {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Add extra metadata.
|
||||
$this->add_model_metadata($data);
|
||||
try {
|
||||
// Add extra metadata.
|
||||
$this->add_model_metadata($data);
|
||||
|
||||
// Write all calculated data to a file.
|
||||
$file = $dataset->store($data);
|
||||
// Write all calculated data to a file.
|
||||
$file = $dataset->store($data);
|
||||
} catch (\Throwable $e) {
|
||||
$dataset->close_process();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// Flag the model + analysable + timesplitting as analysed.
|
||||
$dataset->close_process();
|
||||
|
||||
@@ -196,7 +196,7 @@ abstract class base extends \core_analytics\calculable {
|
||||
$message->component = 'moodle';
|
||||
$message->name = 'insights';
|
||||
|
||||
$message->userfrom = get_admin();
|
||||
$message->userfrom = \core_user::get_noreply_user();
|
||||
$message->userto = $user;
|
||||
|
||||
$insighturl = new \moodle_url('/report/insights/insights.php?modelid=' . $modelid . '&contextid=' . $context->id);
|
||||
|
||||
@@ -1480,7 +1480,10 @@ class model {
|
||||
// 1 db read per context.
|
||||
$this->purge_insights_cache();
|
||||
|
||||
$this->model->trained = 0;
|
||||
if (!$this->is_static()) {
|
||||
$this->model->trained = 0;
|
||||
}
|
||||
|
||||
$this->model->timemodified = time();
|
||||
$this->model->usermodified = $USER->id;
|
||||
$DB->update_record('analytics_models', $this->model);
|
||||
|
||||
@@ -168,10 +168,34 @@ class analytics_model_testcase extends advanced_testcase {
|
||||
$this->assertEmpty($DB->count_records('analytics_predict_samples'));
|
||||
$this->assertEmpty($DB->count_records('analytics_used_files'));
|
||||
|
||||
// Check that the model is marked as not trained after clearing (as it is not a static one).
|
||||
$this->assertEquals(0, $DB->get_field('analytics_models', 'trained', array('id' => $this->modelobj->id)));
|
||||
|
||||
set_config('enabled_stores', '', 'tool_log');
|
||||
get_log_manager(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test behaviour of {\core_analytics\model::clear()} for static models.
|
||||
*/
|
||||
public function test_clear_static() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$statictarget = new test_static_target_shortname();
|
||||
$indicators['test_indicator_max'] = \core_analytics\manager::get_indicator('test_indicator_max');
|
||||
$model = \core_analytics\model::create($statictarget, $indicators, '\core\analytics\time_splitting\quarters');
|
||||
$modelobj = $model->get_model_obj();
|
||||
|
||||
// Static models are always considered trained.
|
||||
$this->assertEquals(1, $DB->get_field('analytics_models', 'trained', array('id' => $modelobj->id)));
|
||||
|
||||
$model->clear();
|
||||
|
||||
// Check that the model is still marked as trained even after clearing.
|
||||
$this->assertEquals(1, $DB->get_field('analytics_models', 'trained', array('id' => $modelobj->id)));
|
||||
}
|
||||
|
||||
public function test_model_manager() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
|
||||
@@ -99,6 +99,13 @@ class auth_db_testcase extends advanced_testcase {
|
||||
case 'mssql':
|
||||
set_config('type', 'mssqlnative', 'auth_db');
|
||||
set_config('sybasequoting', '1', 'auth_db');
|
||||
|
||||
// The native sqlsrv driver uses a comma as separator between host and port.
|
||||
$dbhost = $CFG->dbhost;
|
||||
if (!empty($dboptions['dbport'])) {
|
||||
$dbhost .= ',' . $dboptions['dbport'];
|
||||
}
|
||||
set_config('host', $dbhost, 'auth_db');
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -63,3 +63,36 @@ Feature: User must accept policy when logging in and signing up
|
||||
And I log in as "user1"
|
||||
And I open my profile in edit mode
|
||||
And the field "First name" matches value "User1"
|
||||
|
||||
Scenario Outline: Email validation during email registration
|
||||
Given the following config values are set as admin:
|
||||
| allowaccountssameemail | <allowsameemail> |
|
||||
| registerauth | email |
|
||||
| passwordpolicy | 0 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| s1 | John | Doe | s1@example.com |
|
||||
And I am on site homepage
|
||||
And I follow "Log in"
|
||||
When I press "Create new account"
|
||||
And I set the following fields to these values:
|
||||
| Username | s2 |
|
||||
| Password | test |
|
||||
| Email address | <email1> |
|
||||
| Email (again) | <email2> |
|
||||
| First name | Jane |
|
||||
| Surname | Doe |
|
||||
And I press "Create my new account"
|
||||
Then I should <expect> "This email address is already registered. Perhaps you created an account in the past?"
|
||||
And I should <expect2> "Invalid email address"
|
||||
|
||||
Examples:
|
||||
| allowsameemail | email1 | email2 | expect | expect2 |
|
||||
| 0 | s1@example.com | s1@example.com | see | not see |
|
||||
| 0 | S1@EXAMPLE.COM | S1@EXAMPLE.COM | see | not see |
|
||||
| 0 | s1@example.com | S1@EXAMPLE.COM | see | not see |
|
||||
| 0 | s2@example.com | s1@example.com | not see | see |
|
||||
| 1 | s1@example.com | s1@example.com | not see | not see |
|
||||
| 1 | S1@EXAMPLE.COM | S1@EXAMPLE.COM | not see | not see |
|
||||
| 1 | s1@example.com | S1@EXAMPLE.COM | not see | not see |
|
||||
| 1 | s1@example.com | s2@example.com | not see | see |
|
||||
|
||||
@@ -36,11 +36,7 @@ $string['auth_ldap_contexts_key'] = 'Contexts';
|
||||
$string['auth_ldap_create_context'] = 'If you enable user creation with email confirmation, specify the context where users are created. This context should be different from other users to prevent security issues. You don\'t need to add this context to ldap_context-variable, Moodle will search for users from this context automatically.<br /><b>Note!</b> You have to modify the method user_create() in file auth/ldap/auth.php to make user creation work';
|
||||
$string['auth_ldap_create_context_key'] = 'Context for new users';
|
||||
$string['auth_ldap_create_error'] = 'Error creating user in LDAP.';
|
||||
$string['auth_ldapdescription'] = 'This method provides authentication against an external LDAP server.
|
||||
If the given username and password are valid, Moodle creates a new user
|
||||
entry in its database. This module can read user attributes from LDAP and prefill
|
||||
wanted fields in Moodle. For following logins only the username and
|
||||
password are checked.';
|
||||
$string['auth_ldapdescription'] = 'This method provides authentication against an external LDAP server. If the given username and password are valid, Moodle creates a new user entry in its database. This plugin can read user attributes from LDAP and prefill wanted fields in Moodle. For following logins only the username and password are checked.';
|
||||
$string['auth_ldap_expiration_desc'] = 'Select \'{$a->no}\' to disable expired password checking or \'{$a->ldapserver}\' to read the password expiry time directly from the LDAP server.';
|
||||
$string['auth_ldap_expiration_key'] = 'Expiry';
|
||||
$string['auth_ldap_expiration_warning_desc'] = 'Number of days before password expiry warning is issued.';
|
||||
|
||||
@@ -47,5 +47,39 @@ function xmldb_auth_oauth2_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2018051401) {
|
||||
// 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, 2018051401, '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})';
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2018051401; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2018050800; // Requires this Moodle version.
|
||||
$plugin->component = 'auth_oauth2'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -92,3 +92,25 @@ Feature: availability_grouping
|
||||
# P1 should show but not B2.
|
||||
Then I should see "P1" in the "region-main" "region"
|
||||
And I should not see "P2" in the "region-main" "region"
|
||||
|
||||
@javascript
|
||||
Scenario: Check grouping access restriction message on course homepage
|
||||
Given the following "groupings" exist:
|
||||
| name | course | idnumber |
|
||||
| Grouping A | C1 | GA |
|
||||
And the following "grouping groups" exist:
|
||||
| grouping | group |
|
||||
| GA | GI1 |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber | groupmode | grouping |
|
||||
| assign | Test assign | Assign description | C1 | assign1 | 1 | GA |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I turn editing mode on
|
||||
And I open "Test assign" actions menu
|
||||
And I choose "Edit settings" in the open action menu
|
||||
And I expand all fieldsets
|
||||
And the field "groupingid" matches value "Grouping A"
|
||||
And I press "Add group/grouping access restriction"
|
||||
When I press "Save and return to course"
|
||||
Then I should see "Not available unless: You belong to a group in Grouping A"
|
||||
|
||||
@@ -311,11 +311,11 @@ class backup_controller extends base_controller {
|
||||
// Basic/initial prevention against time/memory limits
|
||||
core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted
|
||||
raise_memory_limit(MEMORY_EXTRA);
|
||||
// If this is not a course backup, inform the plan we are not
|
||||
// If this is not a course backup, or single activity backup (e.g. duplicate) inform the plan we are not
|
||||
// including all the activities for sure. This will affect any
|
||||
// task/step executed conditionally to stop including information
|
||||
// for section and activity backup. MDL-28180.
|
||||
if ($this->get_type() !== backup::TYPE_1COURSE) {
|
||||
if ($this->get_type() !== backup::TYPE_1COURSE && $this->get_type() !== backup::TYPE_1ACTIVITY) {
|
||||
$this->log('notifying plan about excluded activities by type', backup::LOG_DEBUG);
|
||||
$this->plan->set_excluding_activities();
|
||||
}
|
||||
|
||||
@@ -326,11 +326,11 @@ class restore_controller extends base_controller {
|
||||
// Basic/initial prevention against time/memory limits
|
||||
core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted
|
||||
raise_memory_limit(MEMORY_EXTRA);
|
||||
// If this is not a course restore, inform the plan we are not
|
||||
// If this is not a course restore or single activity restore (e.g. duplicate), inform the plan we are not
|
||||
// including all the activities for sure. This will affect any
|
||||
// task/step executed conditionally to stop processing information
|
||||
// for section and activity restore. MDL-28180.
|
||||
if ($this->get_type() !== backup::TYPE_1COURSE) {
|
||||
if ($this->get_type() !== backup::TYPE_1COURSE && $this->get_type() !== backup::TYPE_1ACTIVITY) {
|
||||
$this->log('notifying plan about excluded activities by type', backup::LOG_DEBUG);
|
||||
$this->plan->set_excluding_activities();
|
||||
}
|
||||
|
||||
@@ -2270,7 +2270,11 @@ class backup_questions_structure_step extends backup_structure_step {
|
||||
FROM {tag} t
|
||||
JOIN {tag_instance} ti ON ti.tagid = t.id
|
||||
WHERE ti.itemid = ?
|
||||
AND ti.itemtype = 'question'", array(backup::VAR_PARENTID));
|
||||
AND ti.itemtype = 'question'
|
||||
AND ti.component = 'core_question'",
|
||||
[
|
||||
backup::VAR_PARENTID
|
||||
]);
|
||||
|
||||
// don't need to annotate ids nor files
|
||||
// (already done by {@link backup_annotate_all_question_files}
|
||||
|
||||
@@ -580,6 +580,17 @@ abstract class backup_controller_dbops extends backup_dbops {
|
||||
'backup_import_competencies' => 'competencies'
|
||||
);
|
||||
self::apply_admin_config_defaults($controller, $settings, true);
|
||||
if ((!$controller->get_interactive()) &&
|
||||
$controller->get_type() == backup::TYPE_1ACTIVITY) {
|
||||
// This is duplicate - there is no concept of defaults - these settings must be on.
|
||||
$settings = array(
|
||||
'activities',
|
||||
'blocks',
|
||||
'filters',
|
||||
'questionbank'
|
||||
);
|
||||
self::force_enable_settings($controller, $settings);
|
||||
}
|
||||
break;
|
||||
case backup::MODE_AUTOMATED:
|
||||
// Load the automated defaults.
|
||||
@@ -607,6 +618,30 @@ abstract class backup_controller_dbops extends backup_dbops {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn these settings on. No defaults from admin settings.
|
||||
*
|
||||
* @param backup_controller $controller
|
||||
* @param array $settings a map from admin config names to setting names (Config name => Setting name)
|
||||
*/
|
||||
private static function force_enable_settings(backup_controller $controller, array $settings) {
|
||||
$plan = $controller->get_plan();
|
||||
foreach ($settings as $config => $settingname) {
|
||||
$value = true;
|
||||
if ($plan->setting_exists($settingname)) {
|
||||
$setting = $plan->get_setting($settingname);
|
||||
// We do not allow this setting to be locked for a duplicate function.
|
||||
if ($setting->get_status() !== base_setting::NOT_LOCKED) {
|
||||
$setting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
$setting->set_value($value);
|
||||
$setting->set_status(base_setting::LOCKED_BY_CONFIG);
|
||||
} else {
|
||||
$controller->log('Unknown setting: ' . $setting, BACKUP::LOG_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the controller settings default values from the admin config.
|
||||
*
|
||||
|
||||
@@ -183,6 +183,18 @@ abstract class restore_controller_dbops extends restore_dbops {
|
||||
);
|
||||
self::apply_admin_config_defaults($controller, $settings, true);
|
||||
}
|
||||
if ($controller->get_mode() == backup::MODE_IMPORT &&
|
||||
(!$controller->get_interactive()) &&
|
||||
$controller->get_type() == backup::TYPE_1ACTIVITY) {
|
||||
// This is duplicate - there is no concept of defaults - these settings must be on.
|
||||
$settings = array(
|
||||
'activities',
|
||||
'blocks',
|
||||
'filters',
|
||||
'questionbank'
|
||||
);
|
||||
self::force_enable_settings($controller, $settings);
|
||||
};
|
||||
|
||||
// Add some dependencies.
|
||||
$plan = $controller->get_plan();
|
||||
@@ -233,6 +245,30 @@ abstract class restore_controller_dbops extends restore_dbops {
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn these settings on. No defaults from admin settings.
|
||||
*
|
||||
* @param restore_controller $controller
|
||||
* @param array $settings a map from admin config names to setting names (Config name => Setting name)
|
||||
*/
|
||||
private static function force_enable_settings(restore_controller $controller, array $settings) {
|
||||
$plan = $controller->get_plan();
|
||||
foreach ($settings as $config => $settingname) {
|
||||
$value = true;
|
||||
if ($plan->setting_exists($settingname)) {
|
||||
$setting = $plan->get_setting($settingname);
|
||||
// We do not allow this setting to be locked for a duplicate function.
|
||||
if ($setting->get_status() !== base_setting::NOT_LOCKED) {
|
||||
$setting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
$setting->set_value($value);
|
||||
$setting->set_status(base_setting::LOCKED_BY_CONFIG);
|
||||
} else {
|
||||
$controller->log('Unknown setting: ' . $settingname, BACKUP::LOG_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the controller settings default values from the admin config.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@@ -82,7 +81,7 @@ abstract class setting_dependency {
|
||||
* Destroy all circular references. It helps PHP 5.2 a lot!
|
||||
*/
|
||||
public function destroy() {
|
||||
// No need to destroy anything recursively here, direct reset
|
||||
// No need to destroy anything recursively here, direct reset.
|
||||
$this->setting = null;
|
||||
$this->dependentsetting = null;
|
||||
}
|
||||
@@ -94,16 +93,19 @@ abstract class setting_dependency {
|
||||
* @return bool
|
||||
*/
|
||||
final public function process_change($changetype, $oldvalue) {
|
||||
// Check the type of change requested
|
||||
// Check the type of change requested.
|
||||
switch ($changetype) {
|
||||
// Process a status change
|
||||
case base_setting::CHANGED_STATUS: return $this->process_status_change($oldvalue);
|
||||
// Process a visibility change
|
||||
case base_setting::CHANGED_VISIBILITY: return $this->process_visibility_change($oldvalue);
|
||||
// Process a value change
|
||||
case base_setting::CHANGED_VALUE: return $this->process_value_change($oldvalue);
|
||||
// Process a status change.
|
||||
case base_setting::CHANGED_STATUS:
|
||||
return $this->process_status_change($oldvalue);
|
||||
// Process a visibility change.
|
||||
case base_setting::CHANGED_VISIBILITY:
|
||||
return $this->process_visibility_change($oldvalue);
|
||||
// Process a value change.
|
||||
case base_setting::CHANGED_VALUE:
|
||||
return $this->process_value_change($oldvalue);
|
||||
}
|
||||
// Throw an exception if we get this far
|
||||
// Throw an exception if we get this far.
|
||||
throw new backup_ui_exception('unknownchangetype');
|
||||
}
|
||||
/**
|
||||
@@ -112,11 +114,11 @@ abstract class setting_dependency {
|
||||
* @return bool
|
||||
*/
|
||||
protected function process_visibility_change($oldvisibility) {
|
||||
// Store the current dependent settings visibility for comparison
|
||||
// Store the current dependent settings visibility for comparison.
|
||||
$prevalue = $this->dependentsetting->get_visibility();
|
||||
// Set it regardless of whether we need to
|
||||
// Set it regardless of whether we need to.
|
||||
$this->dependentsetting->set_visibility($this->setting->get_visibility());
|
||||
// Return true if it changed
|
||||
// Return true if it changed.
|
||||
return ($prevalue != $this->dependentsetting->get_visibility());
|
||||
}
|
||||
/**
|
||||
@@ -182,15 +184,16 @@ class setting_dependency_disabledif_equals extends setting_dependency {
|
||||
*/
|
||||
public function __construct(base_setting $setting, base_setting $dependentsetting, $value, $defaultvalue = false) {
|
||||
parent::__construct($setting, $dependentsetting, $defaultvalue);
|
||||
$this->value = ($value)?(string)$value:0;
|
||||
$this->value = ($value) ? (string)$value : 0;
|
||||
}
|
||||
/**
|
||||
* Returns true if the dependent setting is locked by this setting_dependency.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_locked() {
|
||||
// If the setting is locked or the dependent setting should be locked then return true
|
||||
if ($this->setting->get_status() !== base_setting::NOT_LOCKED || $this->setting->get_value() == $this->value) {
|
||||
// If the setting is locked or the dependent setting should be locked then return true.
|
||||
if ($this->setting->get_status() !== base_setting::NOT_LOCKED ||
|
||||
$this->evaluate_disabled_condition($this->setting->get_value())) {
|
||||
return true;
|
||||
}
|
||||
// Else the dependent setting is not locked by this setting_dependency.
|
||||
@@ -208,17 +211,25 @@ class setting_dependency_disabledif_equals extends setting_dependency {
|
||||
return false;
|
||||
}
|
||||
$prevalue = $this->dependentsetting->get_value();
|
||||
// If the setting is the desired value enact the dependency
|
||||
if ($this->setting->get_value() == $this->value) {
|
||||
// If the setting is the desired value enact the dependency.
|
||||
$settingvalue = $this->setting->get_value();
|
||||
if ($this->evaluate_disabled_condition($settingvalue)) {
|
||||
// The dependent setting needs to be locked by hierachy and set to the
|
||||
// default value.
|
||||
$this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
|
||||
$this->dependentsetting->set_value($this->defaultvalue);
|
||||
|
||||
// For checkboxes the default value is false, but when the setting is
|
||||
// locked, the value should inherit from the parent setting.
|
||||
if ($this->defaultvalue === false) {
|
||||
$this->dependentsetting->set_value($settingvalue);
|
||||
} else {
|
||||
$this->dependentsetting->set_value($this->defaultvalue);
|
||||
}
|
||||
} else if ($this->dependentsetting->get_status() == base_setting::LOCKED_BY_HIERARCHY) {
|
||||
// We can unlock the dependent setting
|
||||
// We can unlock the dependent setting.
|
||||
$this->dependentsetting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
// Return true if the value has changed for the dependent setting
|
||||
// Return true if the value has changed for the dependent setting.
|
||||
return ($prevalue != $this->dependentsetting->get_value());
|
||||
}
|
||||
/**
|
||||
@@ -227,17 +238,18 @@ class setting_dependency_disabledif_equals extends setting_dependency {
|
||||
* @return bool
|
||||
*/
|
||||
protected function process_status_change($oldstatus) {
|
||||
// Store the dependent status
|
||||
// Store the dependent status.
|
||||
$prevalue = $this->dependentsetting->get_status();
|
||||
// Store the current status
|
||||
// Store the current status.
|
||||
$currentstatus = $this->setting->get_status();
|
||||
if ($currentstatus == base_setting::NOT_LOCKED) {
|
||||
if ($prevalue == base_setting::LOCKED_BY_HIERARCHY && $this->setting->get_value() != $this->value) {
|
||||
// Dependency has changes, is not fine, unlock the dependent setting
|
||||
if ($prevalue == base_setting::LOCKED_BY_HIERARCHY &&
|
||||
!$this->evaluate_disabled_condition($this->setting->get_value())) {
|
||||
// Dependency has changes, is not fine, unlock the dependent setting.
|
||||
$this->dependentsetting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
} else {
|
||||
// Make sure the dependent setting is also locked, in this case by hierarchy
|
||||
// Make sure the dependent setting is also locked, in this case by hierarchy.
|
||||
$this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
|
||||
}
|
||||
// Return true if the dependent setting has changed.
|
||||
@@ -248,17 +260,17 @@ class setting_dependency_disabledif_equals extends setting_dependency {
|
||||
* @return bool True if there were changes
|
||||
*/
|
||||
public function enforce() {
|
||||
// This will be set to true if ANYTHING changes
|
||||
// This will be set to true if ANYTHING changes.
|
||||
$changes = false;
|
||||
// First process any value changes
|
||||
// First process any value changes.
|
||||
if ($this->process_value_change($this->setting->get_value())) {
|
||||
$changes = true;
|
||||
}
|
||||
// Second process any status changes
|
||||
// Second process any status changes.
|
||||
if ($this->process_status_change($this->setting->get_status())) {
|
||||
$changes = true;
|
||||
}
|
||||
// Finally process visibility changes
|
||||
// Finally process visibility changes.
|
||||
if ($this->process_visibility_change($this->setting->get_visibility())) {
|
||||
$changes = true;
|
||||
}
|
||||
@@ -271,152 +283,72 @@ class setting_dependency_disabledif_equals extends setting_dependency {
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting'=>$this->dependentsetting->get_ui_name(),
|
||||
'dependenton'=>$this->setting->get_ui_name(),
|
||||
'condition'=>'eq',
|
||||
'value'=>$this->value
|
||||
'setting' => $this->dependentsetting->get_ui_name(),
|
||||
'dependenton' => $this->setting->get_ui_name(),
|
||||
'condition' => 'eq',
|
||||
'value' => $this->value
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
|
||||
* This function should be abstract, but there will probably be existing sub-classes so we must provide a default
|
||||
* implementation.
|
||||
* @param mixed $value The value of the parent setting.
|
||||
* @return bool
|
||||
*/
|
||||
protected function evaluate_disabled_condition($value) {
|
||||
return $value == $this->value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dependency that disables the secondary setting if the primary setting is
|
||||
* not equal to the provided value
|
||||
*
|
||||
* @copyright 2011 Darko Miletic <dmiletic@moodlerooms.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class setting_dependency_disabledif_not_equals extends setting_dependency_disabledif_equals {
|
||||
|
||||
/**
|
||||
* Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
|
||||
* @param mixed $value The value of the parent setting.
|
||||
* @return bool
|
||||
*/
|
||||
protected function evaluate_disabled_condition($value) {
|
||||
return $value != $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of properties suitable to be used to define a moodleforms
|
||||
* disabled command
|
||||
* @return array
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting' => $this->dependentsetting->get_ui_name(),
|
||||
'dependenton' => $this->setting->get_ui_name(),
|
||||
'condition' => 'notequal',
|
||||
'value' => $this->value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dependency that disables the secondary setting if the primary setting is
|
||||
* not equal to the provided value
|
||||
*
|
||||
* @copyright 2011 Darko Miletic <dmiletic@moodlerooms.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class setting_dependency_disabledif_not_equals extends setting_dependency_disabledif_equals {
|
||||
/**
|
||||
* Enforces the dependency if required.
|
||||
* @return bool True if there were changes
|
||||
*/
|
||||
public function enforce() {
|
||||
// This will be set to true if ANYTHING changes
|
||||
$changes = false;
|
||||
// First process any value changes
|
||||
if (!$this->process_value_change($this->setting->get_value())) {
|
||||
$changes = true;
|
||||
}
|
||||
// Second process any status changes
|
||||
if ($this->process_status_change($this->setting->get_status())) {
|
||||
$changes = true;
|
||||
}
|
||||
// Finally process visibility changes
|
||||
if ($this->process_visibility_change($this->setting->get_visibility())) {
|
||||
$changes = true;
|
||||
}
|
||||
return $changes;
|
||||
}
|
||||
/**
|
||||
* Returns an array of properties suitable to be used to define a moodleforms
|
||||
* disabled command
|
||||
* @return array
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting'=>$this->dependentsetting->get_ui_name(),
|
||||
'dependenton'=>$this->setting->get_ui_name(),
|
||||
'condition'=>'notequal',
|
||||
'value'=>$this->value
|
||||
);
|
||||
}
|
||||
}
|
||||
* Disable if a value is in a list.
|
||||
*/
|
||||
class setting_dependency_disabledif_in_array extends setting_dependency_disabledif_equals {
|
||||
|
||||
//with array
|
||||
class setting_dependency_disabledif_equals2 extends setting_dependency {
|
||||
/**
|
||||
* The value to compare to
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value;
|
||||
/**
|
||||
* Creates the dependency
|
||||
*
|
||||
* @param base_setting $setting
|
||||
* @param base_setting $dependentsetting
|
||||
* @param mixed $value
|
||||
* @param mixed $defaultvalue
|
||||
*/
|
||||
public function __construct(base_setting $setting, base_setting $dependentsetting, array $value, $defaultvalue = false) {
|
||||
parent::__construct($setting, $dependentsetting, $defaultvalue);
|
||||
$this->value = $value;
|
||||
}
|
||||
/**
|
||||
* Returns true if the dependent setting is locked by this setting_dependency.
|
||||
* Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
|
||||
* @param mixed $value The value of the parent setting.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_locked() {
|
||||
// If the setting is locked or the dependent setting should be locked then return true
|
||||
if ($this->setting->get_status() !== base_setting::NOT_LOCKED || in_array($this->setting->get_value(), $this->value)) {
|
||||
return true;
|
||||
}
|
||||
// Else the dependent setting is not locked by this setting_dependency.
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Processes a value change in the primary setting
|
||||
* @param mixed $oldvalue
|
||||
* @return bool
|
||||
*/
|
||||
protected function process_value_change($oldvalue) {
|
||||
$prevalue = $this->dependentsetting->get_value();
|
||||
// If the setting is the desired value enact the dependency
|
||||
if (in_array($this->setting->get_value(), $this->value)) {
|
||||
// The dependent setting needs to be locked by hierachy and set to the
|
||||
// default value.
|
||||
$this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
|
||||
$this->dependentsetting->set_value($this->defaultvalue);
|
||||
} else if ($this->dependentsetting->get_status() == base_setting::LOCKED_BY_HIERARCHY) {
|
||||
// We can unlock the dependent setting
|
||||
$this->dependentsetting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
// Return true if the value has changed for the dependent setting
|
||||
return ($prevalue != $this->dependentsetting->get_value());
|
||||
}
|
||||
/**
|
||||
* Processes a status change in the primary setting
|
||||
* @param mixed $oldstatus
|
||||
* @return bool
|
||||
*/
|
||||
protected function process_status_change($oldstatus) {
|
||||
// Store the dependent status
|
||||
$prevalue = $this->dependentsetting->get_status();
|
||||
// Store the current status
|
||||
$currentstatus = $this->setting->get_status();
|
||||
if ($currentstatus == base_setting::NOT_LOCKED) {
|
||||
if ($prevalue == base_setting::LOCKED_BY_HIERARCHY && !in_array($this->setting->get_value(), $this->value)) {
|
||||
// Dependency has changes, is not fine, unlock the dependent setting
|
||||
$this->dependentsetting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
} else {
|
||||
// Make sure the dependent setting is also locked, in this case by hierarchy
|
||||
$this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
|
||||
}
|
||||
// Return true if the dependent setting has changed.
|
||||
return ($prevalue != $this->dependentsetting->get_status());
|
||||
}
|
||||
/**
|
||||
* Enforces the dependency if required.
|
||||
* @return bool True if there were changes
|
||||
*/
|
||||
public function enforce() {
|
||||
// This will be set to true if ANYTHING changes
|
||||
$changes = false;
|
||||
// First process any value changes
|
||||
if ($this->process_value_change($this->setting->get_value())) {
|
||||
$changes = true;
|
||||
}
|
||||
// Second process any status changes
|
||||
if ($this->process_status_change($this->setting->get_status())) {
|
||||
$changes = true;
|
||||
}
|
||||
// Finally process visibility changes
|
||||
if ($this->process_visibility_change($this->setting->get_visibility())) {
|
||||
$changes = true;
|
||||
}
|
||||
return $changes;
|
||||
protected function evaluate_disabled_condition($value) {
|
||||
return in_array($value, $this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of properties suitable to be used to define a moodleforms
|
||||
* disabled command
|
||||
@@ -424,14 +356,19 @@ class setting_dependency_disabledif_equals2 extends setting_dependency {
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting'=>$this->dependentsetting->get_ui_name(),
|
||||
'dependenton'=>$this->setting->get_ui_name(),
|
||||
'condition'=>'eq',
|
||||
'value'=>$this->value
|
||||
'setting' => $this->dependentsetting->get_ui_name(),
|
||||
'dependenton' => $this->setting->get_ui_name(),
|
||||
'condition' => 'eq',
|
||||
'value' => $this->value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is here for backwards compatibility (terrible name).
|
||||
*/
|
||||
class setting_dependency_disabledif_equals2 extends setting_dependency_disabledif_in_array {
|
||||
}
|
||||
|
||||
/**
|
||||
* A dependency that disables the secondary element if the primary element is
|
||||
@@ -452,9 +389,9 @@ class setting_dependency_disabledif_checked extends setting_dependency_disabledi
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting'=>$this->dependentsetting->get_ui_name(),
|
||||
'dependenton'=>$this->setting->get_ui_name(),
|
||||
'condition'=>'checked'
|
||||
'setting' => $this->dependentsetting->get_ui_name(),
|
||||
'dependenton' => $this->setting->get_ui_name(),
|
||||
'condition' => 'checked'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -478,9 +415,9 @@ class setting_dependency_disabledif_not_checked extends setting_dependency_disab
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting'=>$this->dependentsetting->get_ui_name(),
|
||||
'dependenton'=>$this->setting->get_ui_name(),
|
||||
'condition'=>'notchecked'
|
||||
'setting' => $this->dependentsetting->get_ui_name(),
|
||||
'dependenton' => $this->setting->get_ui_name(),
|
||||
'condition' => 'notchecked'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -497,6 +434,16 @@ class setting_dependency_disabledif_not_empty extends setting_dependency_disable
|
||||
parent::__construct($setting, $dependentsetting, false, $defaultvalue);
|
||||
$this->value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
|
||||
* @param mixed $value The value of the parent setting.
|
||||
* @return bool
|
||||
*/
|
||||
protected function evaluate_disabled_condition($value) {
|
||||
return !empty($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of properties suitable to be used to define a moodleforms
|
||||
* disabled command
|
||||
@@ -504,50 +451,12 @@ class setting_dependency_disabledif_not_empty extends setting_dependency_disable
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting'=>$this->dependentsetting->get_ui_name(),
|
||||
'dependenton'=>$this->setting->get_ui_name(),
|
||||
'condition'=>'notequal',
|
||||
'value'=>''
|
||||
'setting' => $this->dependentsetting->get_ui_name(),
|
||||
'dependenton' => $this->setting->get_ui_name(),
|
||||
'condition' => 'notequal',
|
||||
'value' => ''
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Processes a value change in the primary setting
|
||||
* @param mixed $oldvalue
|
||||
* @return bool
|
||||
*/
|
||||
protected function process_value_change($oldvalue) {
|
||||
$prevalue = $this->dependentsetting->get_value();
|
||||
// If the setting is the desired value enact the dependency
|
||||
$value = $this->setting->get_value();
|
||||
if (!empty($value)) {
|
||||
// The dependent setting needs to be locked by hierachy and set to the
|
||||
// default value.
|
||||
$this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
|
||||
if ($this->defaultvalue === false) {
|
||||
$this->dependentsetting->set_value($value);
|
||||
} else {
|
||||
$this->dependentsetting->set_value($this->defaultvalue);
|
||||
}
|
||||
} else if ($this->dependentsetting->get_status() == base_setting::LOCKED_BY_HIERARCHY) {
|
||||
// We can unlock the dependent setting
|
||||
$this->dependentsetting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
// Return true if the value has changed for the dependent setting
|
||||
return ($prevalue != $this->dependentsetting->get_value());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the dependent setting is locked by this setting_dependency.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_locked() {
|
||||
// If the setting is locked or the dependent setting should be locked then return true
|
||||
if ($this->setting->get_status() !== base_setting::NOT_LOCKED || !empty($value)) {
|
||||
return true;
|
||||
}
|
||||
// Else the dependent setting is not locked by this setting_dependency.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -562,6 +471,16 @@ class setting_dependency_disabledif_empty extends setting_dependency_disabledif_
|
||||
parent::__construct($setting, $dependentsetting, false, $defaultvalue);
|
||||
$this->value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
|
||||
* @param mixed $value The value of the parent setting.
|
||||
* @return bool
|
||||
*/
|
||||
protected function evaluate_disabled_condition($value) {
|
||||
return empty($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of properties suitable to be used to define a moodleforms
|
||||
* disabled command
|
||||
@@ -569,47 +488,10 @@ class setting_dependency_disabledif_empty extends setting_dependency_disabledif_
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting'=>$this->dependentsetting->get_ui_name(),
|
||||
'dependenton'=>$this->setting->get_ui_name(),
|
||||
'condition'=>'notequal',
|
||||
'value'=>''
|
||||
'setting' => $this->dependentsetting->get_ui_name(),
|
||||
'dependenton' => $this->setting->get_ui_name(),
|
||||
'condition' => 'notequal',
|
||||
'value' => ''
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Processes a value change in the primary setting
|
||||
* @param mixed $oldvalue
|
||||
* @return bool
|
||||
*/
|
||||
protected function process_value_change($oldvalue) {
|
||||
$prevalue = $this->dependentsetting->get_value();
|
||||
// If the setting is the desired value enact the dependency
|
||||
$value = $this->setting->get_value();
|
||||
if (empty($value)) {
|
||||
// The dependent setting needs to be locked by hierachy and set to the
|
||||
// default value.
|
||||
$this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
|
||||
if ($this->defaultvalue === false) {
|
||||
$this->dependentsetting->set_value($value);
|
||||
} else {
|
||||
$this->dependentsetting->set_value($this->defaultvalue);
|
||||
}
|
||||
} else if ($this->dependentsetting->get_status() == base_setting::LOCKED_BY_HIERARCHY) {
|
||||
// We can unlock the dependent setting
|
||||
$this->dependentsetting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
// Return true if the value has changed for the dependent setting
|
||||
return ($prevalue != $this->dependentsetting->get_value());
|
||||
}
|
||||
/**
|
||||
* Returns true if the dependent setting is locked by this setting_dependency.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_locked() {
|
||||
// If the setting is locked or the dependent setting should be locked then return true
|
||||
if ($this->setting->get_status() !== base_setting::NOT_LOCKED || empty($value)) {
|
||||
return true;
|
||||
}
|
||||
// Else the dependent setting is not locked by this setting_dependency.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class backp_settings_testcase extends basic_testcase {
|
||||
/**
|
||||
* test base_setting class
|
||||
*/
|
||||
function test_base_setting() {
|
||||
public function test_base_setting() {
|
||||
// Instantiate base_setting and check everything
|
||||
$bs = new mock_base_setting('test', base_setting::IS_BOOLEAN);
|
||||
$this->assertTrue($bs instanceof base_setting);
|
||||
@@ -290,10 +290,54 @@ class backp_settings_testcase extends basic_testcase {
|
||||
$this->assertEquals($ubs3->get_status(), $ubs1->get_status());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that locked and unlocked states on dependent backup settings at the same level
|
||||
* correctly do not flow from the parent to the child setting when the setting is locked by permissions.
|
||||
*/
|
||||
public function test_dependency_empty_locked_by_permission_child_is_not_unlocked() {
|
||||
// Check dependencies are working ok.
|
||||
$bs1 = new mock_backup_setting('test1', base_setting::IS_INTEGER, 2);
|
||||
$bs1->set_level(1);
|
||||
$bs2 = new mock_backup_setting('test2', base_setting::IS_INTEGER, 2);
|
||||
$bs2->set_level(1); // Same level *must* work.
|
||||
$bs1->add_dependency($bs2, setting_dependency::DISABLED_EMPTY);
|
||||
|
||||
$bs1->set_status(base_setting::LOCKED_BY_PERMISSION);
|
||||
$this->assertEquals(base_setting::LOCKED_BY_HIERARCHY, $bs2->get_status());
|
||||
$this->assertEquals(base_setting::LOCKED_BY_PERMISSION, $bs1->get_status());
|
||||
$bs2->set_status(base_setting::LOCKED_BY_PERMISSION);
|
||||
$this->assertEquals(base_setting::LOCKED_BY_PERMISSION, $bs1->get_status());
|
||||
|
||||
// Unlocking the parent should NOT unlock the child.
|
||||
$bs1->set_status(base_setting::NOT_LOCKED);
|
||||
|
||||
$this->assertEquals(base_setting::LOCKED_BY_PERMISSION, $bs2->get_status());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that locked and unlocked states on dependent backup settings at the same level
|
||||
* correctly do flow from the parent to the child setting when the setting is locked by config.
|
||||
*/
|
||||
public function test_dependency_not_empty_locked_by_config_parent_is_unlocked() {
|
||||
$bs1 = new mock_backup_setting('test1', base_setting::IS_INTEGER, 0);
|
||||
$bs1->set_level(1);
|
||||
$bs2 = new mock_backup_setting('test2', base_setting::IS_INTEGER, 0);
|
||||
$bs2->set_level(1); // Same level *must* work.
|
||||
$bs1->add_dependency($bs2, setting_dependency::DISABLED_NOT_EMPTY);
|
||||
|
||||
$bs1->set_status(base_setting::LOCKED_BY_CONFIG);
|
||||
$this->assertEquals(base_setting::LOCKED_BY_HIERARCHY, $bs2->get_status());
|
||||
$this->assertEquals(base_setting::LOCKED_BY_CONFIG, $bs1->get_status());
|
||||
|
||||
// Unlocking the parent should unlock the child.
|
||||
$bs1->set_status(base_setting::NOT_LOCKED);
|
||||
$this->assertEquals(base_setting::NOT_LOCKED, $bs2->get_status());
|
||||
}
|
||||
|
||||
/**
|
||||
* test backup_setting class
|
||||
*/
|
||||
function test_backup_setting() {
|
||||
public function test_backup_setting() {
|
||||
// Instantiate backup_setting class and set level
|
||||
$bs = new mock_backup_setting('test', base_setting::IS_INTEGER, null);
|
||||
$bs->set_level(1);
|
||||
@@ -340,7 +384,7 @@ class backp_settings_testcase extends basic_testcase {
|
||||
/**
|
||||
* test activity_backup_setting class
|
||||
*/
|
||||
function test_activity_backup_setting() {
|
||||
public function test_activity_backup_setting() {
|
||||
$bs = new mock_activity_backup_setting('test', base_setting::IS_INTEGER, null);
|
||||
$this->assertEquals($bs->get_level(), backup_setting::ACTIVITY_LEVEL);
|
||||
|
||||
@@ -355,7 +399,7 @@ class backp_settings_testcase extends basic_testcase {
|
||||
/**
|
||||
* test section_backup_setting class
|
||||
*/
|
||||
function test_section_backup_setting() {
|
||||
public function test_section_backup_setting() {
|
||||
$bs = new mock_section_backup_setting('test', base_setting::IS_INTEGER, null);
|
||||
$this->assertEquals($bs->get_level(), backup_setting::SECTION_LEVEL);
|
||||
|
||||
@@ -370,7 +414,7 @@ class backp_settings_testcase extends basic_testcase {
|
||||
/**
|
||||
* test course_backup_setting class
|
||||
*/
|
||||
function test_course_backup_setting() {
|
||||
public function test_course_backup_setting() {
|
||||
$bs = new mock_course_backup_setting('test', base_setting::IS_INTEGER, null);
|
||||
$this->assertEquals($bs->get_level(), backup_setting::COURSE_LEVEL);
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ Feature: Duplicate activities
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
| backup_import_activities | 0 |
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Database" to section "1" and I fill the form with:
|
||||
|
||||
+6
-5
@@ -50,14 +50,15 @@ function badge_review_cron() {
|
||||
if (empty($CFG->badges_allowcoursebadges)) {
|
||||
$coursesql = '';
|
||||
} else {
|
||||
$coursesql = ' OR EXISTS (SELECT id FROM {course} WHERE visible = :visible AND startdate < :current) ';
|
||||
$coursesql = ' OR EXISTS (SELECT c.id FROM {course} c WHERE c.visible = :visible AND c.startdate < :current'
|
||||
. ' AND c.id = b.courseid)';
|
||||
$courseparams = array('visible' => true, 'current' => time());
|
||||
}
|
||||
|
||||
$sql = 'SELECT id
|
||||
FROM {badge}
|
||||
WHERE (status = :active OR status = :activelocked)
|
||||
AND (type = :site ' . $coursesql . ')';
|
||||
$sql = 'SELECT b.id
|
||||
FROM {badge} b
|
||||
WHERE (b.status = :active OR b.status = :activelocked)
|
||||
AND (b.type = :site ' . $coursesql . ')';
|
||||
$badgeparams = array(
|
||||
'active' => BADGE_STATUS_ACTIVE,
|
||||
'activelocked' => BADGE_STATUS_ACTIVE_LOCKED,
|
||||
|
||||
@@ -96,6 +96,7 @@ if (!has_any_capability(array(
|
||||
'moodle/badges:viewawarded',
|
||||
'moodle/badges:createbadge',
|
||||
'moodle/badges:awardbadge',
|
||||
'moodle/badges:configurecriteria',
|
||||
'moodle/badges:configuremessages',
|
||||
'moodle/badges:configuredetails',
|
||||
'moodle/badges:deletebadge'), $PAGE->context)) {
|
||||
|
||||
@@ -232,7 +232,7 @@ class block_base {
|
||||
$bc->footer = $this->content->footer;
|
||||
}
|
||||
} else {
|
||||
$bc->add_class('invisible');
|
||||
$bc->add_class('invisibleblock');
|
||||
}
|
||||
|
||||
if (!$this->hide_header()) {
|
||||
@@ -396,13 +396,13 @@ class block_base {
|
||||
function html_attributes() {
|
||||
$attributes = array(
|
||||
'id' => 'inst' . $this->instance->id,
|
||||
'class' => 'block_' . $this->name(). ' block',
|
||||
'class' => 'block_' . $this->name() . ' block',
|
||||
'role' => $this->get_aria_role()
|
||||
);
|
||||
if ($this->hide_header()) {
|
||||
$attributes['class'] .= ' no-header';
|
||||
}
|
||||
if ($this->instance_can_be_docked() && get_user_preferences('docked_block_instance_'.$this->instance->id, 0)) {
|
||||
if ($this->instance_can_be_docked() && get_user_preferences('docked_block_instance_' . $this->instance->id, 0)) {
|
||||
$attributes['class'] .= ' dock_on_load';
|
||||
}
|
||||
return $attributes;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
$string['everything'] = 'Everything';
|
||||
$string['courses'] = 'Categories and courses';
|
||||
$string['coursestructures'] = 'Categories, courses, and course structures';
|
||||
$string['courseactivities'] = 'Categories, courses, and course Activities';
|
||||
$string['courseactivities'] = 'Categories, courses, and course activities';
|
||||
$string['enabledockdesc'] = 'Allow the user to dock this block';
|
||||
$string['expansionlimit'] = 'Generate navigation for the following';
|
||||
$string['linkcategoriesdesc'] = 'Display categories as links';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -114,12 +114,8 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+7
@@ -1284,6 +1284,13 @@ class cache implements cache_loader {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may support purging cache of all data belonging to the
|
||||
* current user.
|
||||
*/
|
||||
public function purge_current_user() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+2
-1
@@ -150,7 +150,8 @@ class cachestore_redis extends cache_store implements cache_is_key_aware, cache_
|
||||
*/
|
||||
protected function new_redis($server, $prefix = '', $password = '') {
|
||||
$redis = new Redis();
|
||||
$port = null;
|
||||
// Check if it isn't a Unix socket to set default port.
|
||||
$port = ($server[0] === '/') ? null : 6379;
|
||||
if (strpos($server, ':')) {
|
||||
$serverconf = explode(':', $server);
|
||||
$server = $serverconf[0];
|
||||
|
||||
@@ -49,6 +49,7 @@ class core_calendar_export_form extends moodleform {
|
||||
|
||||
$export = array();
|
||||
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventsall', 'calendar'), 'all');
|
||||
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventsrelatedtocategories', 'calendar'), 'categories');
|
||||
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventsrelatedtocourses', 'calendar'), 'courses');
|
||||
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventsrelatedtogroups', 'calendar'), 'groups');
|
||||
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventspersonal', 'calendar'), 'user');
|
||||
|
||||
@@ -531,8 +531,7 @@ class provider implements
|
||||
AND ctx.contextlevel = :categorycontext)
|
||||
OR (e.courseid = ctx.instanceid
|
||||
AND (e.eventtype = 'course'
|
||||
OR e.eventtype = 'group'
|
||||
OR e.modulename != '0')
|
||||
OR e.eventtype = 'group')
|
||||
AND ctx.contextlevel = :coursecontext)
|
||||
OR (e.userid = ctx.instanceid
|
||||
AND e.eventtype = 'user'
|
||||
|
||||
@@ -40,8 +40,8 @@ $time = optional_param('preset_time', 'weeknow', PARAM_ALPHA);
|
||||
$now = $calendartype->timestamp_to_date_array(time());
|
||||
|
||||
// Let's see if we have sufficient and correct data
|
||||
$allowed_what = array('all', 'user', 'groups', 'courses');
|
||||
$allowed_time = array('weeknow', 'weeknext', 'monthnow', 'monthnext', 'recentupcoming', 'custom');
|
||||
$allowedwhat = ['all', 'user', 'groups', 'courses', 'categories'];
|
||||
$allowedtime = ['weeknow', 'weeknext', 'monthnow', 'monthnext', 'recentupcoming', 'custom'];
|
||||
|
||||
if (!empty($generateurl)) {
|
||||
$authtoken = sha1($user->id . $user->password . $CFG->calendar_exportsalt);
|
||||
@@ -56,9 +56,9 @@ if (!empty($generateurl)) {
|
||||
redirect($link->out());
|
||||
die;
|
||||
}
|
||||
|
||||
$paramcategory = false;
|
||||
if(!empty($what) && !empty($time)) {
|
||||
if(in_array($what, $allowed_what) && in_array($time, $allowed_time)) {
|
||||
if(in_array($what, $allowedwhat) && in_array($time, $allowedtime)) {
|
||||
$courses = enrol_get_users_courses($user->id, true, 'id, visible, shortname');
|
||||
// Array of courses that we will pass to calendar_get_legacy_events() which
|
||||
// is initially set to the list of the user's courses.
|
||||
@@ -78,6 +78,7 @@ if(!empty($what) && !empty($time)) {
|
||||
$courses[SITEID] = new stdClass;
|
||||
$courses[SITEID]->shortname = get_string('globalevents', 'calendar');
|
||||
$paramcourses[SITEID] = $courses[SITEID];
|
||||
$paramcategory = true;
|
||||
} else if ($what == 'groups') {
|
||||
$users = false;
|
||||
$paramcourses = array();
|
||||
@@ -85,6 +86,11 @@ if(!empty($what) && !empty($time)) {
|
||||
$users = $user->id;
|
||||
$groups = false;
|
||||
$paramcourses = array();
|
||||
} else if ($what == 'categories') {
|
||||
$users = $user->id;
|
||||
$groups = false;
|
||||
$paramcourses = array();
|
||||
$paramcategory = true;
|
||||
} else {
|
||||
$users = false;
|
||||
$groups = false;
|
||||
@@ -180,7 +186,9 @@ if(!empty($what) && !empty($time)) {
|
||||
die();
|
||||
}
|
||||
}
|
||||
$events = calendar_get_legacy_events($timestart, $timeend, $users, $groups, array_keys($paramcourses), false);
|
||||
|
||||
$events = calendar_get_legacy_events($timestart, $timeend, $users, $groups, array_keys($paramcourses), false, true,
|
||||
$paramcategory);
|
||||
|
||||
$ical = new iCalendar;
|
||||
$ical->add_property('method', 'PUBLISH');
|
||||
|
||||
@@ -48,6 +48,14 @@ Feature: Export calendar events
|
||||
When I click on "Get calendar URL" "button"
|
||||
Then I should see "&preset_what=groups&"
|
||||
|
||||
Scenario: Generating calendar URL for category events
|
||||
Given I follow "This month"
|
||||
And I click on "Export calendar" "button"
|
||||
And I set the field "Events related to categories" to "1"
|
||||
And I set the field "Recent and next 60 days" to "1"
|
||||
When I click on "Get calendar URL" "button"
|
||||
Then I should see "&preset_what=categories&"
|
||||
|
||||
Scenario: Generating calendar URL for user events
|
||||
Given I follow "This month"
|
||||
And I click on "Export calendar" "button"
|
||||
|
||||
@@ -365,12 +365,23 @@ class core_calendar_privacy_testcase extends provider_testcase {
|
||||
// Delete all Calendar Events for all Users by Context for Course 2.
|
||||
provider::delete_data_for_all_users_in_context($course2context);
|
||||
|
||||
// Verify all Calendar Events for Course 2 were deleted.
|
||||
$events = $DB->get_records('event', array('courseid' => $course2->id));
|
||||
// Verify all Calendar Events for Course 2 context were deleted.
|
||||
$events = $DB->get_records('event', array('courseid' => $course2->id, 'modulename' => '0'));
|
||||
$this->assertCount(0, $events);
|
||||
// Verify all Calendar Subscriptions for Course 2 were deleted.
|
||||
$subscriptions = $DB->get_records('event_subscriptions', array('courseid' => $course2->id));
|
||||
$this->assertCount(0, $subscriptions);
|
||||
|
||||
// Verify all Calendar Events for the assignment exists still.
|
||||
$events = $DB->get_records('event', array('modulename' => 'assign'));
|
||||
$this->assertCount(2, $events);
|
||||
|
||||
// Delete all Calendar Events for all Users by Context for the assignment.
|
||||
provider::delete_data_for_all_users_in_context($modulecontext);
|
||||
|
||||
// Verify all Calendar Events for the assignment context were deleted.
|
||||
$events = $DB->get_records('event', array('modulename' => 'assign'));
|
||||
$this->assertCount(0, $events);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-7
@@ -28,7 +28,6 @@ require_once($CFG->dirroot.'/cohort/upload_form.php');
|
||||
require_once($CFG->libdir . '/csvlib.class.php');
|
||||
|
||||
$contextid = optional_param('contextid', 0, PARAM_INT);
|
||||
$returnurl = optional_param('returnurl', '', PARAM_URL);
|
||||
|
||||
require_login();
|
||||
|
||||
@@ -56,13 +55,9 @@ if ($context->contextlevel == CONTEXT_COURSECAT) {
|
||||
navigation_node::override_active_url(new moodle_url('/cohort/index.php', array()));
|
||||
}
|
||||
|
||||
$uploadform = new cohort_upload_form(null, array('contextid' => $context->id, 'returnurl' => $returnurl));
|
||||
$uploadform = new cohort_upload_form(null, array('contextid' => $context->id));
|
||||
|
||||
if ($returnurl) {
|
||||
$returnurl = new moodle_url($returnurl);
|
||||
} else {
|
||||
$returnurl = new moodle_url('/cohort/index.php', array('contextid' => $context->id));
|
||||
}
|
||||
$returnurl = new moodle_url('/cohort/index.php', array('contextid' => $context->id));
|
||||
|
||||
if ($uploadform->is_cancelled()) {
|
||||
redirect($returnurl);
|
||||
|
||||
@@ -48,9 +48,6 @@ class cohort_upload_form extends moodleform {
|
||||
$mform = $this->_form;
|
||||
$data = (object)$this->_customdata;
|
||||
|
||||
$mform->addElement('hidden', 'returnurl');
|
||||
$mform->setType('returnurl', PARAM_URL);
|
||||
|
||||
$mform->addElement('header', 'cohortfileuploadform', get_string('uploadafile'));
|
||||
|
||||
$filepickeroptions = array();
|
||||
|
||||
@@ -192,8 +192,8 @@ class course_module_competency extends persistent {
|
||||
$sql = 'SELECT COUNT(comp.id)
|
||||
FROM {' . self::TABLE . '} coursemodulecomp
|
||||
JOIN {' . competency::TABLE . '} comp
|
||||
ON coursecomp.competencyid = comp.id
|
||||
WHERE coursecomp.cmid = ? ';
|
||||
ON coursemodulecomp.competencyid = comp.id
|
||||
WHERE coursemodulecomp.cmid = ? ';
|
||||
$params = array($cmid);
|
||||
|
||||
$results = $DB->count_records_sql($sql, $params);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user