Compare commits
499 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f2c730fecf | |||
| 812779dfa2 | |||
| ed46f0deb1 | |||
| 9c1f1cda90 | |||
| b78438abc0 | |||
| 19389fc511 | |||
| 63ed941a93 | |||
| 2d90c646be | |||
| 324dfc764c | |||
| f517d1c20b | |||
| f7f477a65d | |||
| 01da07a42b | |||
| 5cb84b5ba4 | |||
| fb270fd5de | |||
| 2111c597ac | |||
| cd663df1ad | |||
| b68c14c98d | |||
| 2fe7bf0585 | |||
| 59ea75fd37 | |||
| c80603ddc4 | |||
| 8b7e92206c | |||
| 787d05f3d3 | |||
| acb579245a | |||
| f704c30f8a | |||
| e54fe38e2b | |||
| 921e63c880 | |||
| 693918c30e | |||
| 6bb02c27d5 | |||
| 63a07d3a21 | |||
| ac1c429ebd | |||
| 42f3477091 | |||
| 9b2438597a | |||
| 7a5e665072 | |||
| 2c908ec30b | |||
| f595e3cbae | |||
| 1bacd164c3 | |||
| 76dd58bb4f | |||
| 8e293f17cf | |||
| 4afd408b55 | |||
| 8d9a3b3b62 | |||
| 0aee829cd3 | |||
| 88929ca824 | |||
| 5770e51478 | |||
| e72516ccde | |||
| 38ca8793b6 | |||
| faf0cd9098 | |||
| f7714bb67b | |||
| 764247be6b | |||
| 289ac435d1 | |||
| dcfe7694d4 | |||
| 34d16a7737 | |||
| 863ffc884f | |||
| f8bebe363d | |||
| 45151248c4 | |||
| 39ed46b158 | |||
| de290738c9 | |||
| 004ec71505 | |||
| 045b5235f2 | |||
| 2d066f19ba | |||
| b4e7dcaa8a | |||
| e618263437 | |||
| 04a2fc7427 | |||
| 7ed6f826ad | |||
| 07a06264dc | |||
| 73416d1b9a | |||
| f70a6d3245 | |||
| 2d18472382 | |||
| 04babc675b | |||
| eeae1784c6 | |||
| 112e95f6b4 | |||
| 3e061bf55d | |||
| deb3e69e37 | |||
| 0532dad71b | |||
| a0102708b6 | |||
| a12afaf7dd | |||
| 57897685c0 | |||
| 216655b54d | |||
| 899d7c1bb2 | |||
| 440085ecf8 | |||
| a5e8cb4aea | |||
| d8d912443f | |||
| e3effa63fd | |||
| 870e0fb822 | |||
| 4e8c1f2681 | |||
| 12f025749a | |||
| 29006301ed | |||
| c1eb2c3ddf | |||
| 7783fcd76f | |||
| 4dbc9b18b4 | |||
| e9b36ac653 | |||
| db5fb4ce76 | |||
| 008e28478d | |||
| 510fafed8a | |||
| 7e911af220 | |||
| d25a7f21a3 | |||
| 3c6dac879f | |||
| d81cdaaf13 | |||
| 4a057abc11 | |||
| d102926592 | |||
| 05ea4e002d | |||
| c6c1b3596f | |||
| bf671547ab | |||
| 1b78a209a0 | |||
| 42486cac98 | |||
| 9b27f063c8 | |||
| 83a36ad89b | |||
| 61040870bb | |||
| 999f4ac9fa | |||
| 7c4b6a84dd | |||
| 03d3dfbfc5 | |||
| cf447b8c0f | |||
| a6af290ea4 | |||
| 267ac74ef3 | |||
| a4b06536fb | |||
| 6303e895e4 | |||
| db6bbcbc16 | |||
| 55f1261257 | |||
| 6227a1f3e7 | |||
| 0f60d011dc | |||
| 21fa68e52f | |||
| 085df3ed05 | |||
| 4645ff7ddc | |||
| 2a6e070cd4 | |||
| 3e8e8641b3 | |||
| 6f1e7d2db2 | |||
| 330ac09e0d | |||
| 37e2d30c84 | |||
| 4105c6c329 | |||
| 07bd586492 | |||
| ada9b1ed77 | |||
| 60c034fe27 | |||
| 8850deb74d | |||
| 4077b738a0 | |||
| 0ab4c9d029 | |||
| 3925c74634 | |||
| 2c48f2ed4c | |||
| 88d1474104 | |||
| b621820ea1 | |||
| a2a8653aa2 | |||
| bfa4e840d8 | |||
| dda30b1906 | |||
| c0070d8d88 | |||
| 582ab13d73 | |||
| f826599632 | |||
| cf7167cbae | |||
| 999f2b3aea | |||
| 5ef46dad4b | |||
| 724b6446d1 | |||
| 535a794f90 | |||
| fa59a794f6 | |||
| f48a890017 | |||
| 78d8dc13c8 | |||
| e42056413c | |||
| e61ab464f3 | |||
| f0066ff80c | |||
| 5ef5c6c1e2 | |||
| a4c72b69e8 | |||
| a95cd718f9 | |||
| df91b1d861 | |||
| f34fc42d9b | |||
| 43c3b0ec7b | |||
| 2a841b12fd | |||
| 73c991dd3f | |||
| ecc60ffeb7 | |||
| 83ef244a83 | |||
| f6c149226f | |||
| c0c2921586 | |||
| 2abcbc0251 | |||
| 270705c279 | |||
| 26156e72d9 | |||
| 7a56557c45 | |||
| 4c23fb82ab | |||
| 30fbe29595 | |||
| 5e425cc89b | |||
| b2a786539e | |||
| 744dc6e647 | |||
| 0e956b0452 | |||
| 82dd025984 | |||
| b2e9074f3c | |||
| 73ed5ce9fb | |||
| cd16f43ba1 | |||
| 49b881c876 | |||
| ef19d61d4d | |||
| a54229b629 | |||
| 373fd13876 | |||
| 244c69709e | |||
| 675564277d | |||
| 3e7b053221 | |||
| abcec0fe9a | |||
| fa0a765c20 | |||
| 5ad9e84664 | |||
| 79a143130b | |||
| 08b7d0bacc | |||
| b1a381ffcf | |||
| 3719994057 | |||
| be2641dc8c | |||
| 3c726433cc | |||
| 78a4b45ffc | |||
| 3900f28e28 | |||
| 48d0e75068 | |||
| c7a0fb64a0 | |||
| 8542d1875f | |||
| 087fd13442 | |||
| dd96737556 | |||
| fde7c5f003 | |||
| 4afb72f2de | |||
| 23f751ee38 | |||
| 79fc19f5d7 | |||
| f89ee726b4 | |||
| 9592c8e99c | |||
| 18453fec76 | |||
| 4656c6ab53 | |||
| 36700a125e | |||
| 80154bd033 | |||
| fd37fba94b | |||
| f3586f59c4 | |||
| 384f71b42c | |||
| 55f47d94f7 | |||
| 673d38078c | |||
| e732d0d9ca | |||
| 28e38410c5 | |||
| b16b0a61a4 | |||
| 19f5623bf6 | |||
| 6967547062 | |||
| 519413892e | |||
| ceee447066 | |||
| 934c72979b | |||
| bf42737370 | |||
| 6966842459 | |||
| 9e06074bbe | |||
| 41ea5affc4 | |||
| 8e0c012bbb | |||
| f071a177b0 | |||
| e0b7f5492a | |||
| 664d834cd1 | |||
| faa3b4022f | |||
| 2d16c385d7 | |||
| b2959dbe9b | |||
| 5eb62533cd | |||
| 7e5947b986 | |||
| 592f05a064 | |||
| 4f2e496184 | |||
| 793e74fef3 | |||
| bc11b6c138 | |||
| 788727862d | |||
| adbbc7bd9e | |||
| 18450eab27 | |||
| aef3679316 | |||
| fe32471819 | |||
| c8316e2894 | |||
| 13dfdb0b5f | |||
| 36814aaec6 | |||
| fef242fa65 | |||
| 422d1c4625 | |||
| 0bd78bb334 | |||
| 14cd37913c | |||
| 3cca19d176 | |||
| 468efb3171 | |||
| b56a1518ad | |||
| fed2e156e2 | |||
| 51a0c7cda7 | |||
| 6292878644 | |||
| 201e19d7b6 | |||
| ae2a9c0e66 | |||
| dc2dd0cf72 | |||
| 9c08bf4a6a | |||
| ba1f0fbd39 | |||
| e784850b5a | |||
| 6ae671b784 | |||
| dbe6a4d188 | |||
| 9427be40aa | |||
| acfbe693b2 | |||
| 760c41ca09 | |||
| 8d7e6ecfc8 | |||
| aa39908cef | |||
| fba1d33198 | |||
| f304fd2e6c | |||
| c538b47443 | |||
| ea2a48fb4a | |||
| f40124002b | |||
| 8f8ace5cdd | |||
| efc6ed5e34 | |||
| 7a1b7e41d9 | |||
| f8b446f399 | |||
| 708cabea4d | |||
| 9f4662b228 | |||
| 186f4563cb | |||
| 53e96ae4e1 | |||
| 39ab2e44d0 | |||
| 5367656ae8 | |||
| 2fca03ec2a | |||
| f2ebd53dba | |||
| 886bc21b8b | |||
| 5b1efbcab4 | |||
| 9dfc203245 | |||
| 278642a380 | |||
| 1101fd22f2 | |||
| db956269fe | |||
| 1538a20679 | |||
| 4eac169974 | |||
| 5445d98144 | |||
| 258c0449b6 | |||
| 0a39d64927 | |||
| c40f1f184f | |||
| fc7ab00709 | |||
| a76c20c20e | |||
| 824c31e592 | |||
| cba386b636 | |||
| 438b185951 | |||
| 3f1b091aa2 | |||
| 0fec60deea | |||
| 9e28b040df | |||
| f7e646a5c1 | |||
| d2b8475e3e | |||
| af9e696b79 | |||
| aa29beabe8 | |||
| 71a673cc3b | |||
| 105dbe93d5 | |||
| d141109dc2 | |||
| 97cf440401 | |||
| bb2f54224c | |||
| 6c20557845 | |||
| 349c877872 | |||
| 5ae74cad15 | |||
| e393866a36 | |||
| 99c0919993 | |||
| c83dc49d3c | |||
| 9a59701f0f | |||
| 1cd8f3c645 | |||
| c3e67edfac | |||
| 92b71540b3 | |||
| bba645d102 | |||
| 7cc923f443 | |||
| da53c524f1 | |||
| f603096c47 | |||
| 480c254ac1 | |||
| 5169661ee2 | |||
| a76630d700 | |||
| 475f4a6272 | |||
| 9e74aef0a9 | |||
| 58aabfcedd | |||
| eccf806ad0 | |||
| 1198f5ae01 | |||
| cc9161bebc | |||
| bea43ad49a | |||
| 9c41fd2377 | |||
| b0b7c2d808 | |||
| 3ca610e89a | |||
| 71d7788366 | |||
| d758f054b9 | |||
| f67638b05f | |||
| c604a8079a | |||
| 9b36c64056 | |||
| b110dd77a4 | |||
| 086199c4e9 | |||
| b42123f38c | |||
| c440b9947e | |||
| b64a526dba | |||
| b9bfc5f15a | |||
| 94ae0d9f3b | |||
| da9d46b5c0 | |||
| b3351c0133 | |||
| e8ffea7bd5 | |||
| 25e135af74 | |||
| e4e04cdb84 | |||
| 222f015381 | |||
| 5a9e656628 | |||
| 5ff9df857e | |||
| c97952e6af | |||
| e2e8a3e859 | |||
| edfb84865a | |||
| 78d7e090b2 | |||
| 774522ff7e | |||
| 3a9daf6f9a | |||
| 1913d60133 | |||
| 3aeec06364 | |||
| de64c655e2 | |||
| 7d3f51c262 | |||
| b4d7288e94 | |||
| 43e413f93f | |||
| afcae55712 | |||
| 65e2d354aa | |||
| c19e95c871 | |||
| 6237bb2f2f | |||
| 87082d0f1c | |||
| 229171be1a | |||
| 62a703b655 | |||
| c4c810491a | |||
| 46388b5e48 | |||
| 54bd82d4ae | |||
| ffd5282986 | |||
| 07bff34437 | |||
| 66649405b5 | |||
| 702e65c9ed | |||
| 81c559d05e | |||
| fa23afbe30 | |||
| 6d5f68a832 | |||
| 88690b92e4 | |||
| 472b438486 | |||
| 04c92d672f | |||
| 57ebd7b1b1 | |||
| 1cda99f39b | |||
| 93d5459125 | |||
| 62845bce83 | |||
| d9eedd1a68 | |||
| 3dfecf93cc | |||
| 0858e593e6 | |||
| ddf83f51d4 | |||
| ba8ca6604d | |||
| f8da8ae382 | |||
| 5c86569096 | |||
| 921d045174 | |||
| c81ae06997 | |||
| 3228da2e5c | |||
| 236351fdc3 | |||
| 752fdd2479 | |||
| 29a036aa59 | |||
| 299f1bcdbd | |||
| 09de24667f | |||
| a7d7354c85 | |||
| 31830a539d | |||
| 67ccf63800 | |||
| 292e991b86 | |||
| 65e345c3f4 | |||
| 554f0bd206 | |||
| 38d6b6eb17 | |||
| 931d7216cc | |||
| 6dfc37accf | |||
| 9e273e7556 | |||
| 2d747884c6 | |||
| af2c296364 | |||
| ec1a6a6996 | |||
| 3b8a871233 | |||
| ccfd014d42 | |||
| 0c0714ae3b | |||
| 4d7f88d55c | |||
| d5f6d962e2 | |||
| 97f5b7b8a2 | |||
| 410d8eda9a | |||
| c42974048a | |||
| fce8127ff0 | |||
| 864e404348 | |||
| cfbc7d903f | |||
| 0b9dc284e8 | |||
| e92d84244c | |||
| e9f7276481 | |||
| b1dd1c8656 | |||
| 6b5f44b214 | |||
| 068c7a8706 | |||
| e3adeba886 | |||
| 666ac939d5 | |||
| d2fe097e20 | |||
| b1298c7690 | |||
| 6748b5ee5c | |||
| 583cf99ebd | |||
| c1ca23afa3 | |||
| 0c96741ef7 | |||
| b73158d3d0 | |||
| f6fd6787d1 | |||
| 60fef215f6 | |||
| 4587188ad9 | |||
| bc575d2bea | |||
| 806f6f642c | |||
| acad969077 | |||
| 066751547d | |||
| 296b602f2c | |||
| 76db660264 | |||
| e5381b67e9 | |||
| 309a919074 | |||
| 367a26d088 | |||
| 8d4e07d86d | |||
| 89eb7cf302 | |||
| b37f9889d0 | |||
| c8900a5152 | |||
| 4c4d53ebaf | |||
| a26ce935c7 | |||
| 2a3e89465c | |||
| 6d8019438f | |||
| b19e9ae457 | |||
| c6a1e9bed3 | |||
| 0df5893423 | |||
| dd818fdb44 | |||
| 1fd2bc4810 | |||
| 3f3f7ce52a | |||
| 088c4fb280 | |||
| 0ed92e6a37 | |||
| afc8bd9672 | |||
| c2dd91bfef | |||
| b0ac83b82b | |||
| 272ad5b577 | |||
| 7805d03a9e | |||
| ecb40b963d | |||
| b45e110c3a | |||
| 928497310e | |||
| 5755d70f2e | |||
| ff8aaa2dd4 | |||
| 5ebde631c0 | |||
| 21c633a8c6 | |||
| 25fe23f5f5 |
@@ -0,0 +1,3 @@
|
||||
--errors=errors,duplicate-properties
|
||||
--warnings=known-properties,display-property-grouping,empty-rules,important
|
||||
--exclude-list=vendor/,lib/editor/tinymce/,lib/yuilib/,theme/bootstrapbase/style/
|
||||
@@ -132,7 +132,7 @@ if ($courseid == SITEID) {
|
||||
$PAGE->set_heading($course->fullname.': '.$fullname);
|
||||
}
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading($title, 3);
|
||||
echo $OUTPUT->heading($title);
|
||||
echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthnormal');
|
||||
|
||||
// Display them.
|
||||
|
||||
@@ -48,5 +48,8 @@ function xmldb_tool_customlang_upgrade($oldversion) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,14 @@ class update_langpacks_task extends \core\task\scheduled_task {
|
||||
* Run langpack update
|
||||
*/
|
||||
public function execute() {
|
||||
global $CFG;
|
||||
|
||||
if (!empty($CFG->skiplangupgrade)) {
|
||||
mtrace('Langpack update skipped. ($CFG->skiplangupgrade set)');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$controller = new \tool_langimport\controller();
|
||||
if ($controller->update_all_installed_languages()) {
|
||||
foreach ($controller->info as $message) {
|
||||
|
||||
@@ -213,11 +213,14 @@ class manager implements \core\log\manager {
|
||||
* @param string $info Additional description information
|
||||
* @param int $cm The course_module->id if there is one
|
||||
* @param int|\stdClass $user If log regards $user other than $USER
|
||||
* @param string $ip Override the IP, should only be used for restore.
|
||||
* @param int $time Override the log time, should only be used for restore.
|
||||
*/
|
||||
public function legacy_add_to_log($courseid, $module, $action, $url = '', $info = '', $cm = 0, $user = 0) {
|
||||
public function legacy_add_to_log($courseid, $module, $action, $url = '', $info = '',
|
||||
$cm = 0, $user = 0, $ip = null, $time = null) {
|
||||
$this->init();
|
||||
if (isset($this->stores['logstore_legacy'])) {
|
||||
$this->stores['logstore_legacy']->legacy_add_to_log($courseid, $module, $action, $url, $info, $cm, $user);
|
||||
$this->stores['logstore_legacy']->legacy_add_to_log($courseid, $module, $action, $url, $info, $cm, $user, $ip, $time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,5 +46,8 @@ function xmldb_tool_log_upgrade($oldversion) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -36,5 +36,8 @@ function xmldb_logstore_database_upgrade($oldversion) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -143,8 +143,10 @@ class store implements \tool_log\log\store, \core\log\sql_select_reader {
|
||||
* @param string $info Additional description information
|
||||
* @param int $cm The course_module->id if there is one
|
||||
* @param int|\stdClass $user If log regards $user other than $USER
|
||||
* @param string $ip Override the IP, should only be used for restore.
|
||||
* @param int $time Override the log time, should only be used for restore.
|
||||
*/
|
||||
public function legacy_add_to_log($courseid, $module, $action, $url, $info, $cm, $user) {
|
||||
public function legacy_add_to_log($courseid, $module, $action, $url, $info, $cm, $user, $ip = null, $time = null) {
|
||||
// Note that this function intentionally does not follow the normal Moodle DB access idioms.
|
||||
// This is for a good reason: it is the most frequently used DB update function,
|
||||
// so it has been optimised for speed.
|
||||
@@ -172,9 +174,9 @@ class store implements \tool_log\log\store, \core\log\sql_select_reader {
|
||||
}
|
||||
}
|
||||
|
||||
$remoteaddr = getremoteaddr();
|
||||
$remoteaddr = (is_null($ip)) ? getremoteaddr() : $ip;
|
||||
|
||||
$timenow = time();
|
||||
$timenow = (is_null($time)) ? time() : $time;
|
||||
if (!empty($url)) { // Could break doing html_entity_decode on an empty var.
|
||||
$url = html_entity_decode($url, ENT_QUOTES, 'UTF-8');
|
||||
} else {
|
||||
|
||||
@@ -114,5 +114,8 @@ function xmldb_logstore_standard_upgrade($oldversion) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -291,6 +291,11 @@ class manager {
|
||||
// First flag this message to prevent another running hitting this message while we look at the headers.
|
||||
$this->add_flag_to_message($messageid, self::MESSAGE_FLAGGED);
|
||||
|
||||
if ($this->is_bulk_message($message, $messageid)) {
|
||||
mtrace("- The message has a bulk header set. This is likely an auto-generated reply - discarding.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Record the user that this script is currently being run as. This is important when re-processing existing
|
||||
// messages, as cron_setup_user is called multiple times.
|
||||
$originaluser = $USER;
|
||||
@@ -363,7 +368,10 @@ class manager {
|
||||
|
||||
// Process and retrieve the message data for this message.
|
||||
// This includes fetching the full content, as well as all headers, and attachments.
|
||||
$this->process_message_data($envelope, $messagedata, $messageid);
|
||||
if (!$this->process_message_data($envelope, $messagedata, $messageid)) {
|
||||
mtrace("--- Message could not be found on the server. Is another process removing messages?");
|
||||
return;
|
||||
}
|
||||
|
||||
// When processing validation replies, we need to skip the sender verification phase as this has been
|
||||
// manually completed.
|
||||
@@ -403,7 +411,7 @@ class manager {
|
||||
mtrace("-- Returning to the original user.");
|
||||
cron_setup_user($originaluser);
|
||||
return;
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
// An unknown error occurred. The user is not informed, but the administrator is.
|
||||
mtrace("-- Message processing failed. An unexpected exception was thrown. Details follow.");
|
||||
mtrace($e->getMessage());
|
||||
@@ -457,36 +465,25 @@ class manager {
|
||||
$query = new \Horde_Imap_Client_Fetch_Query();
|
||||
$query->imapDate();
|
||||
|
||||
// Fetch all of the message parts too.
|
||||
$typemap = $structure->contentTypeMap();
|
||||
foreach ($typemap as $part => $type) {
|
||||
// The header.
|
||||
$query->headerText(array(
|
||||
'id' => $part,
|
||||
));
|
||||
}
|
||||
// Fetch the message header.
|
||||
$query->headerText();
|
||||
|
||||
// Retrieve the message with the above components.
|
||||
$messagedata = $this->client->fetch($mailbox, $query, array('ids' => $messageid))->first();
|
||||
|
||||
// Store the data for this message.
|
||||
$headers = '';
|
||||
|
||||
foreach ($typemap as $part => $type) {
|
||||
// Grab all of the header data into a string.
|
||||
$headers .= $messagedata->getHeaderText($part);
|
||||
|
||||
// We don't handle any of the other MIME content at this stage.
|
||||
if (!$messagedata) {
|
||||
// Message was not found! Somehow it has been removed or is no longer returned.
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = new \stdClass();
|
||||
|
||||
// The message ID should always be in the first part.
|
||||
$data = new \stdClass();
|
||||
$data->messageid = $messagedata->getHeaderText(0, \Horde_Imap_Client_Data_Fetch::HEADER_PARSE)->getValue('Message-ID');
|
||||
$data->subject = $envelope->subject;
|
||||
$data->timestamp = $messagedata->getImapDate()->__toString();
|
||||
$data->envelope = $envelope;
|
||||
$data->data = $this->addressmanager->get_data();
|
||||
$data->headers = $headers;
|
||||
$data->headers = $messagedata->getHeaderText();
|
||||
|
||||
$this->currentmessagedata = $data;
|
||||
|
||||
@@ -496,9 +493,8 @@ class manager {
|
||||
/**
|
||||
* Process a message again to add body and attachment data.
|
||||
*
|
||||
* @param Horde_Imap_Client_Data_Envelope $envelope The Envelope of the message
|
||||
* @param Horde_Imap_Client_Data_Fetch $basemessagedata The structure and part of the message body
|
||||
* @param string|Horde_Imap_Client_Ids $messageid The Hore message Uid
|
||||
* @param \Horde_Imap_Client_Data_Fetch $basemessagedata The structure and part of the message body
|
||||
* @param string|\Horde_Imap_Client_Ids $messageid The Hore message Uid
|
||||
* @return \stdClass The current value of the messagedata
|
||||
*/
|
||||
private function process_message_data_body(
|
||||
@@ -616,6 +612,9 @@ class manager {
|
||||
private function process_message_part_attachment($messagedata, $partdata, $part, $filename) {
|
||||
global $CFG;
|
||||
|
||||
// For Antivirus, the repository/lib.php must be included as it is not autoloaded.
|
||||
require_once($CFG->dirroot . '/repository/lib.php');
|
||||
|
||||
// If a filename is present, assume that this part is an attachment.
|
||||
$attachment = new \stdClass();
|
||||
$attachment->filename = $filename;
|
||||
@@ -647,7 +646,7 @@ class manager {
|
||||
// Perform a virus scan now.
|
||||
try {
|
||||
\repository::antivir_scan_file($filepath, $attachment->filename, true);
|
||||
} catch (moodle_exception $e) {
|
||||
} catch (\moodle_exception $e) {
|
||||
mtrace("--> A virus was found in the attachment '{$attachment->filename}'.");
|
||||
$this->inform_attachment_virus();
|
||||
return;
|
||||
@@ -736,6 +735,43 @@ class manager {
|
||||
return in_array($flag, $flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to determine whether this message is a bulk message (e.g. automated reply).
|
||||
*
|
||||
* @param \Horde_Imap_Client_Data_Fetch $message The message to process
|
||||
* @param string|\Horde_Imap_Client_Ids $messageid The Hore message Uid
|
||||
* @return boolean
|
||||
*/
|
||||
private function is_bulk_message(
|
||||
\Horde_Imap_Client_Data_Fetch $message,
|
||||
$messageid) {
|
||||
$query = new \Horde_Imap_Client_Fetch_Query();
|
||||
$query->headerText(array('peek' => true));
|
||||
|
||||
$messagedata = $this->client->fetch($this->get_mailbox(), $query, array('ids' => $messageid))->first();
|
||||
|
||||
// Assume that this message is not bulk to begin with.
|
||||
$isbulk = false;
|
||||
|
||||
// An auto-reply may itself include the Bulk Precedence.
|
||||
$precedence = $messagedata->getHeaderText(0, \Horde_Imap_Client_Data_Fetch::HEADER_PARSE)->getValue('Precedence');
|
||||
$isbulk = $isbulk || strtolower($precedence) == 'bulk';
|
||||
|
||||
// If the X-Autoreply header is set, and not 'no', then this is an automatic reply.
|
||||
$autoreply = $messagedata->getHeaderText(0, \Horde_Imap_Client_Data_Fetch::HEADER_PARSE)->getValue('X-Autoreply');
|
||||
$isbulk = $isbulk || ($autoreply && $autoreply != 'no');
|
||||
|
||||
// If the X-Autorespond header is set, and not 'no', then this is an automatic response.
|
||||
$autorespond = $messagedata->getHeaderText(0, \Horde_Imap_Client_Data_Fetch::HEADER_PARSE)->getValue('X-Autorespond');
|
||||
$isbulk = $isbulk || ($autorespond && $autorespond != 'no');
|
||||
|
||||
// If the Auto-Submitted header is set, and not 'no', then this is a non-human response.
|
||||
$autosubmitted = $messagedata->getHeaderText(0, \Horde_Imap_Client_Data_Fetch::HEADER_PARSE)->getValue('Auto-Submitted');
|
||||
$isbulk = $isbulk || ($autosubmitted && $autosubmitted != 'no');
|
||||
|
||||
return $isbulk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the message to the appropriate handler.
|
||||
*
|
||||
@@ -758,7 +794,7 @@ class manager {
|
||||
$error->message = $e->getMessage();
|
||||
throw new \core\message\inbound\processing_failed_exception('messageprocessingfailed', 'tool_messageinbound', $error);
|
||||
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
mtrace("-> The Inbound Message handler threw an exception. Unable to process this message. User informed.");
|
||||
mtrace("--> " . $e->getMessage());
|
||||
// An unknown error occurred. Still inform the user but, this time do not include the specific
|
||||
|
||||
@@ -46,7 +46,7 @@ class invalid_recipient_handler extends \core\message\inbound\handler {
|
||||
/**
|
||||
* Do not allow changes to the address validation setting.
|
||||
*/
|
||||
public function allow_validateaddress_change() {
|
||||
public function can_change_validateaddress() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,5 +50,8 @@ function xmldb_tool_monitor_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2014102000, 'tool', 'monitor');
|
||||
}
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -404,12 +404,11 @@ class tool_monitor_eventobservers_testcase extends advanced_testcase {
|
||||
* Run adhoc tasks.
|
||||
*/
|
||||
protected function run_adhock_tasks() {
|
||||
ob_start();
|
||||
while ($task = \core\task\manager::get_next_adhoc_task(time())) {
|
||||
$task->execute();
|
||||
\core\task\manager::adhoc_task_complete($task);
|
||||
}
|
||||
ob_clean(); // Suppress mtrace debugging info.
|
||||
$this->expectOutputRegex("/^Sending message to the user with id \d+ for the subscription with id \d+\.\.\..Sent./ms");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
$string['cannotfit'] = 'The replacement is longer than original and shortening is not allow, cannot continue.';
|
||||
$string['disclaimer'] = 'I understand the risks of this operation';
|
||||
$string['doit'] = 'Yes, do it!';
|
||||
$string['excludedtables'] = 'Several tables are not updated as part of the text replacement. This include configuration, log, events, and session tables.';
|
||||
$string['excludedtables'] = 'Several tables are not updated as part of the text replacement. These include configuration, log, events, and session tables.';
|
||||
$string['pageheader'] = 'Search and replace text throughout the whole database';
|
||||
$string['notifyfinished'] = '...finished';
|
||||
$string['notifyrebuilding'] = 'Rebuilding course cache...';
|
||||
|
||||
@@ -53,22 +53,27 @@ class tool_task_edit_scheduled_task_form extends moodleform {
|
||||
$mform->addElement('text', 'minute', get_string('taskscheduleminute', 'tool_task'));
|
||||
$mform->setType('minute', PARAM_RAW);
|
||||
$mform->addHelpButton('minute', 'taskscheduleminute', 'tool_task');
|
||||
$mform->addRule('minute', get_string('required'), 'required');
|
||||
|
||||
$mform->addElement('text', 'hour', get_string('taskschedulehour', 'tool_task'));
|
||||
$mform->setType('hour', PARAM_RAW);
|
||||
$mform->addHelpButton('hour', 'taskschedulehour', 'tool_task');
|
||||
$mform->addRule('hour', get_string('required'), 'required');
|
||||
|
||||
$mform->addElement('text', 'day', get_string('taskscheduleday', 'tool_task'));
|
||||
$mform->setType('day', PARAM_RAW);
|
||||
$mform->addHelpButton('day', 'taskscheduleday', 'tool_task');
|
||||
$mform->addRule('day', get_string('required'), 'required');
|
||||
|
||||
$mform->addElement('text', 'month', get_string('taskschedulemonth', 'tool_task'));
|
||||
$mform->setType('month', PARAM_RAW);
|
||||
$mform->addHelpButton('month', 'taskschedulemonth', 'tool_task');
|
||||
$mform->addRule('month', get_string('required'), 'required');
|
||||
|
||||
$mform->addElement('text', 'dayofweek', get_string('taskscheduledayofweek', 'tool_task'));
|
||||
$mform->setType('dayofweek', PARAM_RAW);
|
||||
$mform->addHelpButton('dayofweek', 'taskscheduledayofweek', 'tool_task');
|
||||
$mform->addRule('dayofweek', get_string('required'), 'required');
|
||||
|
||||
$mform->addElement('advcheckbox', 'disabled', get_string('disabled', 'tool_task'));
|
||||
$mform->addHelpButton('disabled', 'disabled', 'tool_task');
|
||||
@@ -92,5 +97,63 @@ class tool_task_edit_scheduled_task_form extends moodleform {
|
||||
// Do not use defaults for existing values, the set_data() is the correct way.
|
||||
$this->set_data(\core\task\manager::record_from_scheduled_task($task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom validations.
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $files
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function validation($data, $files) {
|
||||
$error = parent::validation($data, $files);
|
||||
$fields = array('minute', 'hour', 'day', 'month', 'dayofweek');
|
||||
foreach ($fields as $field) {
|
||||
if (!self::validate_fields($field, $data[$field])) {
|
||||
$error[$field] = get_string('invaliddata', 'core_error');
|
||||
}
|
||||
}
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that validates the submitted data.
|
||||
*
|
||||
* Explanation of the regex:-
|
||||
*
|
||||
* \A\*\z - matches *
|
||||
* \A[0-5]?[0-9]\z - matches entries like 23
|
||||
* \A\*\/[0-5]?[0-9]\z - matches entries like * / 5
|
||||
* \A[0-5]?[0-9](,[0-5]?[0-9])*\z - matches entries like 1,2,3
|
||||
* \A[0-5]?[0-9]-[0-5]?[0-9]\z - matches entries like 2-10
|
||||
*
|
||||
* @param string $field field to validate
|
||||
* @param string $value value
|
||||
*
|
||||
* @return bool true if validation passes, false other wise.
|
||||
*/
|
||||
public static function validate_fields($field, $value) {
|
||||
switch ($field) {
|
||||
case 'minute' :
|
||||
case 'hour' :
|
||||
$regex = "/\A\*\z|\A[0-5]?[0-9]\z|\A\*\/[0-5]?[0-9]\z|\A[0-5]?[0-9](,[0-5]?[0-9])*\z|\A[0-5]?[0-9]-[0-5]?[0-9]\z/";
|
||||
break;
|
||||
case 'day':
|
||||
$regex = "/\A\*\z|\A([1-2]?[0-9]|3[0-1])\z|\A\*\/([1-2]?[0-9]|3[0-1])\z|";
|
||||
$regex .= "\A([1-2]?[0-9]|3[0-1])(,([1-2]?[0-9]|3[0-1]))*\z|\A([1-2]?[0-9]|3[0-1])-([1-2]?[0-9]|3[0-1])\z/";
|
||||
break;
|
||||
case 'month':
|
||||
$regex = "/\A\*\z|\A([0-9]|1[0-2])\z|\A\*\/([0-9]|1[0-2])\z|\A([0-9]|1[0-2])(,([0-9]|1[0-2]))*\z|";
|
||||
$regex .= "\A([0-9]|1[0-2])-([0-9]|1[0-2])\z/";
|
||||
break;
|
||||
case 'dayofweek':
|
||||
$regex = "/\A\*\z|\A[0-6]\z|\A\*\/[0-6]\z|\A[0-6](,[0-6])*\z|\A[0-6]-[0-6]\z/";
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return (bool)preg_match($regex, $value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@ if ($execute = $options['execute']) {
|
||||
} else {
|
||||
$task->set_cron_lock($cronlock);
|
||||
}
|
||||
get_mailer('buffer');
|
||||
$task->execute();
|
||||
if (isset($predbqueries)) {
|
||||
mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
|
||||
@@ -130,6 +131,7 @@ if ($execute = $options['execute']) {
|
||||
}
|
||||
mtrace("Task completed.");
|
||||
\core\task\manager::scheduled_task_complete($task);
|
||||
get_mailer('close');
|
||||
exit(0);
|
||||
} catch (Exception $e) {
|
||||
if ($DB->is_transaction_started()) {
|
||||
@@ -139,6 +141,7 @@ if ($execute = $options['execute']) {
|
||||
mtrace("... used " . (microtime(true) - $pretime) . " seconds");
|
||||
mtrace("Task failed: " . $e->getMessage());
|
||||
\core\task\manager::scheduled_task_failed($task);
|
||||
get_mailer('close');
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* File containing tests for the mform class.
|
||||
*
|
||||
* @package tool_task
|
||||
* @copyright 2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
|
||||
/**
|
||||
* Mform test class.
|
||||
*
|
||||
* @package tool_task
|
||||
* @copyright 2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
|
||||
*/
|
||||
class tool_task_form_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test validations for minute field.
|
||||
*/
|
||||
public function test_validate_fields_minute() {
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '*');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '1');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '20');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '65');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '*/');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '*/1');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '*/20');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '*/65');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '1,2');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '2,20');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '20,30,45');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '65,20,30');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '25,75');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '1-2');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '2-20');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '20-30');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '65-20');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '25-75');
|
||||
$this->assertFalse($valid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test validations for minute hour.
|
||||
*/
|
||||
public function test_validate_fields_hour() {
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '*');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '1');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '20');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '65');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '*/');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '*/1');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '*/20');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '*/65');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '1,2');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '2,20');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '20,30,45');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '65,20,30');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '25,75');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '1-2');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '2-20');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '20-30');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '65-20');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '25-75');
|
||||
$this->assertFalse($valid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test validations for day field.
|
||||
*/
|
||||
public function test_validate_fields_day() {
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '*');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '1');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '20');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '65');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '35');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '*/');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '*/1');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '*/20');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '*/65');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '*/35');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '1,2');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '2,20');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '20,30,25');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '65,20,30');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '25,35');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '1-2');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '2-20');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '20-30');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '65-20');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '25-35');
|
||||
$this->assertFalse($valid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test validations for month field.
|
||||
*/
|
||||
public function test_validate_fields_month() {
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '*');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '1');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '10');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '13');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '35');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '*/');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '*/1');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '*/12');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '*/13');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '*/35');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '1,2');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '2,11');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '2,10,12');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '65,2,13');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '25,35');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '1-2');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '2-12');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '3-6');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '65-2');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '25-26');
|
||||
$this->assertFalse($valid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test validations for dayofweek field.
|
||||
*/
|
||||
public function test_validate_fields_dayofweek() {
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '*');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '0');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '1');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '6');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '7');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '20');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '*/');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '*/1');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '*/6');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '*/13');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '*/35');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '1,2');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '2,6');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '2,6,3');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '65,2,13');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '25,35');
|
||||
$this->assertFalse($valid);
|
||||
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '1-2');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '2-6');
|
||||
$this->assertTrue($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '65-2');
|
||||
$this->assertFalse($valid);
|
||||
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '3-7');
|
||||
$this->assertFalse($valid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.path-admin-tool-xmldb a[name="lastused"] {
|
||||
padding-top: 50px;
|
||||
}
|
||||
@@ -55,5 +55,8 @@ function xmldb_auth_cas_upgrade($oldversion) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -742,7 +742,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
} while ($entry = ldap_next_entry($ldapconnection, $entry));
|
||||
}
|
||||
unset($ldap_result); // Free mem.
|
||||
} while ($ldap_pagedresults && !empty($ldap_cookie));
|
||||
} while ($ldap_pagedresults && $ldap_cookie !== null && $ldap_cookie != '');
|
||||
}
|
||||
|
||||
// If LDAP paged results were used, the current connection must be completely
|
||||
|
||||
@@ -45,5 +45,8 @@ function xmldb_auth_ldap_upgrade($oldversion) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ $string['auth_ldap_passtype'] = 'Specify the format of new or changed passwords
|
||||
$string['auth_ldap_passtype_key'] = 'Password format';
|
||||
$string['auth_ldap_passwdexpire_settings'] = 'LDAP password expiration settings.';
|
||||
$string['auth_ldap_preventpassindb'] = 'Select yes to prevent passwords from being stored in Moodle\'s DB.';
|
||||
$string['auth_ldap_preventpassindb_key'] = 'Cache passwords';
|
||||
$string['auth_ldap_preventpassindb_key'] = 'Don\'t cache passwords';
|
||||
$string['auth_ldap_search_sub'] = 'Search users from subcontexts.';
|
||||
$string['auth_ldap_search_sub_key'] = 'Search subcontexts';
|
||||
$string['auth_ldap_server_settings'] = 'LDAP server settings';
|
||||
|
||||
@@ -50,5 +50,8 @@ function xmldb_auth_manual_upgrade($oldversion) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -50,5 +50,8 @@ function xmldb_auth_mnet_upgrade($oldversion) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@ Moodle Configuration with Dual login
|
||||
--
|
||||
|
||||
Also see:
|
||||
https://spaces.internet2.edu/display/SHIB2/NativeSPRequestMapper and
|
||||
https://spaces.internet2.edu/display/SHIB2/NativeSPAccessControl
|
||||
https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPRequestMapper and
|
||||
https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPAccessControl
|
||||
|
||||
2. As Moodle admin, go to the 'Administrations >> Users >> Authentication' and
|
||||
click on the the 'Shibboleth' settings.
|
||||
@@ -101,8 +101,8 @@ Moodle Configuration with Dual login
|
||||
to the same host. If no SessionInitiator URL is given, the default one
|
||||
'/Shibboleth.sso' (only works for Shibboleth 1.3.x) will be used. For
|
||||
Shibboleth 2.x you have to add '/Shibboleth.sso/DS' as a SessionInitiator.
|
||||
Also see https://spaces.internet2.edu/display/SHIB/SessionInitiator
|
||||
and https://spaces.internet2.edu/display/SHIB2/NativeSPSessionInitiator
|
||||
Also see https://wiki.shibboleth.net/confluence/display/SHIB/SessionInitiator
|
||||
and https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPSessionInitiator
|
||||
|
||||
Important Note: If you upgraded from a previous version of Moodle and now
|
||||
want to use the integrated WAYF, you have to make sure that
|
||||
@@ -324,8 +324,8 @@ logout. Hopefully, the Moodle logout helps to motivate the developers to
|
||||
implement SLO. On the other hand, the easiest and safest way to log out
|
||||
still is to tell users to quit their web browsers :)
|
||||
|
||||
Also see https://spaces.internet2.edu/display/SHIB2/SLOIssues and
|
||||
https://spaces.internet2.edu/display/SHIB2/NativeSPLogoutInitiator for some
|
||||
Also see https://wiki.shibboleth.net/confluence/display/SHIB2/SLOIssues and
|
||||
https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPLogoutInitiator for some
|
||||
background information on this topic.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
+22
-28
@@ -1,13 +1,15 @@
|
||||
<?php
|
||||
|
||||
// Implements logout for Shibboleth authenticated users according to:
|
||||
// - https://spaces.internet2.edu/display/SHIB2/NativeSPLogoutInitiator
|
||||
// - https://spaces.internet2.edu/display/SHIB2/NativeSPNotify
|
||||
// - https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPLogoutInitiator
|
||||
// - https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPNotify
|
||||
|
||||
require_once("../../config.php");
|
||||
|
||||
require_once($CFG->dirroot."/auth/shibboleth/auth.php");
|
||||
|
||||
$action = optional_param('action', '', PARAM_ALPHA);
|
||||
$redirect = optional_param('return', '', PARAM_URL);
|
||||
|
||||
// Find out whether host supports https
|
||||
$protocol = 'http://';
|
||||
@@ -15,38 +17,32 @@ if (is_https()) {
|
||||
$protocol = 'https://';
|
||||
}
|
||||
|
||||
// Front channel logout
|
||||
if (
|
||||
isset($_GET['return'])
|
||||
&& isset($_GET['action'])
|
||||
&& $_GET['action'] == 'logout'
|
||||
){
|
||||
|
||||
// Logout out user from application
|
||||
// E.g. destroy application session/cookie etc
|
||||
require_logout();
|
||||
|
||||
// Finally, send user to the return URL
|
||||
redirect($_GET['return']);
|
||||
// If the shibboleth plugin is not enable, throw an exception.
|
||||
if (!is_enabled_auth('shibboleth')) {
|
||||
throw new moodle_exception(get_string('pluginnotenabled', 'auth', 'shibboleth'));
|
||||
}
|
||||
|
||||
// Back channel logout
|
||||
elseif (!empty($HTTP_RAW_POST_DATA)) {
|
||||
// Front channel logout.
|
||||
if ($action == 'logout' && !empty($redirect)) {
|
||||
|
||||
// Requires PHP 5
|
||||
if ($USER->auth == 'shibboleth') {
|
||||
// Logout out user from application.
|
||||
require_logout();
|
||||
// Finally, send user to the return URL.
|
||||
redirect($redirect);
|
||||
}
|
||||
|
||||
} else if (!empty($HTTP_RAW_POST_DATA)) {
|
||||
|
||||
// Set SOAP header
|
||||
// Back channel logout.
|
||||
// Set SOAP header.
|
||||
$server = new SoapServer($protocol.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'/LogoutNotification.wsdl');
|
||||
|
||||
|
||||
$server->addFunction("LogoutNotification");
|
||||
$server->handle();
|
||||
}
|
||||
|
||||
// Return WSDL
|
||||
else {
|
||||
} else {
|
||||
|
||||
// Return WSDL.
|
||||
header('Content-Type: text/xml');
|
||||
|
||||
echo <<<WSDL
|
||||
@@ -66,8 +62,8 @@ Because neither of these two variants seems to be the case, the WSDL file for
|
||||
the web service is returned.
|
||||
|
||||
For more information see:
|
||||
- https://spaces.internet2.edu/display/SHIB2/NativeSPLogoutInitiator
|
||||
- https://spaces.internet2.edu/display/SHIB2/NativeSPNotify
|
||||
- https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPLogoutInitiator
|
||||
- https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPNotify
|
||||
-->
|
||||
|
||||
<types>
|
||||
@@ -119,9 +115,7 @@ For more information see:
|
||||
</definitions>
|
||||
WSDL;
|
||||
exit;
|
||||
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function LogoutNotification($SessionID){
|
||||
|
||||
@@ -80,6 +80,9 @@ class backup_course_task extends backup_task {
|
||||
$this->add_step(new backup_enrolments_structure_step('course_enrolments', 'enrolments.xml'));
|
||||
}
|
||||
|
||||
// Annotate enrolment custom fields.
|
||||
$this->add_step(new backup_enrolments_execution_step('annotate_enrol_custom_fields'));
|
||||
|
||||
// Annotate all the groups and groupings belonging to the course
|
||||
$this->add_step(new backup_annotate_course_groups_and_groupings('annotate_course_groups'));
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Defines backup_enrol_plugin class.
|
||||
*
|
||||
* @package core_backup
|
||||
* @subpackage moodle2
|
||||
* @category backup
|
||||
* @copyright 2014 University of Wisconsin
|
||||
* @author Matt petro
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Base class for enrol backup plugins.
|
||||
*
|
||||
* @package core_backup
|
||||
* @copyright 2014 University of Wisconsin
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class backup_enrol_plugin extends backup_plugin {
|
||||
// Use default parent behaviour.
|
||||
}
|
||||
@@ -44,6 +44,7 @@ require_once($CFG->dirroot . '/backup/moodle2/backup_theme_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_report_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_coursereport_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_plagiarism_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_enrol_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_subplugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_settingslib.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_stepslib.php');
|
||||
|
||||
@@ -129,11 +129,10 @@ class backup_root_task extends backup_task {
|
||||
$activities->add_dependency($badges);
|
||||
$users->add_dependency($badges);
|
||||
|
||||
// Define calendar events (dependent of users)
|
||||
// Define calendar events.
|
||||
$events = new backup_calendarevents_setting('calendarevents', base_setting::IS_BOOLEAN, true);
|
||||
$events->set_ui(new backup_setting_ui_checkbox($events, get_string('rootsettingcalendarevents', 'backup')));
|
||||
$this->add_setting($events);
|
||||
$users->add_dependency($events);
|
||||
|
||||
// Define completion (dependent of users)
|
||||
$completion = new backup_userscompletion_setting('userscompletion', base_setting::IS_BOOLEAN, true);
|
||||
|
||||
@@ -547,7 +547,8 @@ class backup_enrolments_structure_step extends backup_structure_step {
|
||||
|
||||
$enrol->annotate_ids('role', 'roleid');
|
||||
|
||||
//TODO: let plugins annotate custom fields too and add more children
|
||||
// Add enrol plugin structure.
|
||||
$this->add_plugin_structure('enrol', $enrol, false);
|
||||
|
||||
return $enrolments;
|
||||
}
|
||||
@@ -1742,9 +1743,23 @@ class backup_zip_contents extends backup_execution_step implements file_progress
|
||||
// Get the zip packer
|
||||
$zippacker = get_file_packer('application/vnd.moodle.backup');
|
||||
|
||||
// Track overall progress for the 2 long-running steps (archive to
|
||||
// pathname, get backup information).
|
||||
$reporter = $this->task->get_progress();
|
||||
$reporter->start_progress('backup_zip_contents', 2);
|
||||
|
||||
// Zip files
|
||||
$result = $zippacker->archive_to_pathname($files, $zipfile, true, $this);
|
||||
|
||||
// If any sub-progress happened, end it.
|
||||
if ($this->startedprogress) {
|
||||
$this->task->get_progress()->end_progress();
|
||||
$this->startedprogress = false;
|
||||
} else {
|
||||
// No progress was reported, manually move it on to the next overall task.
|
||||
$reporter->progress(1);
|
||||
}
|
||||
|
||||
// Something went wrong.
|
||||
if ($result === false) {
|
||||
@unlink($zipfile);
|
||||
@@ -1752,16 +1767,20 @@ class backup_zip_contents extends backup_execution_step implements file_progress
|
||||
}
|
||||
// Read to make sure it is a valid backup. Refer MDL-37877 . Delete it, if found not to be valid.
|
||||
try {
|
||||
backup_general_helper::get_backup_information_from_mbz($zipfile);
|
||||
backup_general_helper::get_backup_information_from_mbz($zipfile, $this);
|
||||
} catch (backup_helper_exception $e) {
|
||||
@unlink($zipfile);
|
||||
throw new backup_step_exception('error_zip_packing', '', $e->debuginfo);
|
||||
}
|
||||
|
||||
// If any progress happened, end it.
|
||||
// If any sub-progress happened, end it.
|
||||
if ($this->startedprogress) {
|
||||
$this->task->get_progress()->end_progress();
|
||||
$this->startedprogress = false;
|
||||
} else {
|
||||
$reporter->progress(2);
|
||||
}
|
||||
$reporter->end_progress();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1830,6 +1849,47 @@ class backup_activity_grade_items_to_ids extends backup_execution_step {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This step allows enrol plugins to annotate custom fields.
|
||||
*
|
||||
* @package core_backup
|
||||
* @copyright 2014 University of Wisconsin
|
||||
* @author Matt Petro
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class backup_enrolments_execution_step extends backup_execution_step {
|
||||
|
||||
/**
|
||||
* Function that will contain all the code to be executed.
|
||||
*/
|
||||
protected function define_execution() {
|
||||
global $DB;
|
||||
|
||||
$plugins = enrol_get_plugins(true);
|
||||
$enrols = $DB->get_records('enrol', array(
|
||||
'courseid' => $this->task->get_courseid()));
|
||||
|
||||
// Allow each enrol plugin to add annotations.
|
||||
foreach ($enrols as $enrol) {
|
||||
if (isset($plugins[$enrol->enrol])) {
|
||||
$plugins[$enrol->enrol]->backup_annotate_custom_fields($this, $enrol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotate a single name/id pair.
|
||||
* This can be called from {@link enrol_plugin::backup_annotate_custom_fields()}.
|
||||
*
|
||||
* @param string $itemname
|
||||
* @param int $itemid
|
||||
*/
|
||||
public function annotate_id($itemname, $itemid) {
|
||||
backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), $itemname, $itemid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This step will annotate all the groups and groupings belonging to the course
|
||||
*/
|
||||
@@ -1925,13 +1985,22 @@ class backup_annotate_all_question_files extends backup_execution_step {
|
||||
$components = backup_qtype_plugin::get_components_and_fileareas();
|
||||
// Let's loop
|
||||
foreach($rs as $record) {
|
||||
// We don't need to specify filearea nor itemid as far as by
|
||||
// component and context it's enough to annotate the whole bank files
|
||||
// This backups "questiontext", "generalfeedback" and "answerfeedback" fileareas (all them
|
||||
// belonging to the "question" component
|
||||
backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', null, null);
|
||||
// Again, it is enough to pick files only by context and component
|
||||
// Do it for qtype specific components
|
||||
// Backup all the file areas the are managed by the core question component.
|
||||
// That is, by the question_type base class. In particular, we don't want
|
||||
// to include files belonging to responses here.
|
||||
backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'questiontext', null);
|
||||
backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'generalfeedback', null);
|
||||
backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'answer', null);
|
||||
backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'answerfeedback', null);
|
||||
backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'hint', null);
|
||||
backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'correctfeedback', null);
|
||||
backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'partiallycorrectfeedback', null);
|
||||
backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'incorrectfeedback', null);
|
||||
|
||||
// For files belonging to question types, we make the leap of faith that
|
||||
// all the files belonging to the question type are part of the question definition,
|
||||
// so we can just backup all the files in bulk, without specifying each
|
||||
// file area name separately.
|
||||
foreach ($components as $component => $fileareas) {
|
||||
backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, $component, null, null);
|
||||
}
|
||||
@@ -2299,7 +2368,8 @@ class backup_course_completion_structure_step extends backup_structure_step {
|
||||
$cc = new backup_nested_element('course_completion');
|
||||
|
||||
$criteria = new backup_nested_element('course_completion_criteria', array('id'), array(
|
||||
'course','criteriatype', 'module', 'moduleinstance', 'courseinstanceshortname', 'enrolperiod', 'timeend', 'gradepass', 'role'
|
||||
'course', 'criteriatype', 'module', 'moduleinstance', 'courseinstanceshortname', 'enrolperiod',
|
||||
'timeend', 'gradepass', 'role', 'roleshortname'
|
||||
));
|
||||
|
||||
$criteriacompletions = new backup_nested_element('course_completion_crit_completions');
|
||||
@@ -2322,12 +2392,15 @@ class backup_course_completion_structure_step extends backup_structure_step {
|
||||
$cc->add_child($coursecompletions);
|
||||
$cc->add_child($aggregatemethod);
|
||||
|
||||
// We need to get the courseinstances shortname rather than an ID for restore
|
||||
$criteria->set_source_sql("SELECT ccc.*, c.shortname AS courseinstanceshortname
|
||||
FROM {course_completion_criteria} ccc
|
||||
LEFT JOIN {course} c ON c.id = ccc.courseinstance
|
||||
WHERE ccc.course = ?", array(backup::VAR_COURSEID));
|
||||
|
||||
// We need some extra data for the restore.
|
||||
// - courseinstances shortname rather than an ID.
|
||||
// - roleshortname in case restoring on a different site.
|
||||
$sourcesql = "SELECT ccc.*, c.shortname AS courseinstanceshortname, r.shortname AS roleshortname
|
||||
FROM {course_completion_criteria} ccc
|
||||
LEFT JOIN {course} c ON c.id = ccc.courseinstance
|
||||
LEFT JOIN {role} r ON r.id = ccc.role
|
||||
WHERE ccc.course = ?";
|
||||
$criteria->set_source_sql($sourcesql, array(backup::VAR_COURSEID));
|
||||
|
||||
$aggregatemethod->set_source_table('course_completion_aggr_methd', array('course' => backup::VAR_COURSEID));
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Defines restore_enrol_plugin class.
|
||||
*
|
||||
* @package core_backup
|
||||
* @subpackage moodle2
|
||||
* @category backup
|
||||
* @copyright 2014 University of Wisconsin
|
||||
* @author Matt petro
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Base class for enrol backup plugins.
|
||||
*
|
||||
* @package core_backup
|
||||
* @copyright 2014 University of Wisconsin
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class restore_enrol_plugin extends restore_plugin {
|
||||
// Use default parent behaviour.
|
||||
}
|
||||
@@ -43,6 +43,7 @@ require_once($CFG->dirroot . '/backup/moodle2/restore_report_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/restore_coursereport_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/restore_plagiarism_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/restore_gradingform_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/restore_enrol_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
|
||||
@@ -52,6 +53,7 @@ require_once($CFG->dirroot . '/backup/moodle2/backup_report_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_coursereport_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_plagiarism_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_gradingform_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_enrol_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/restore_subplugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/restore_settingslib.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/restore_stepslib.php');
|
||||
|
||||
@@ -195,7 +195,7 @@ class restore_root_task extends restore_task {
|
||||
$activities->add_dependency($badges);
|
||||
$users->add_dependency($badges);
|
||||
|
||||
// Define Calendar events (dependent of users)
|
||||
// Define Calendar events.
|
||||
$defaultvalue = false; // Safer default
|
||||
$changeable = false;
|
||||
if (isset($rootsettings['calendarevents']) && $rootsettings['calendarevents']) { // Only enabled when available
|
||||
@@ -206,7 +206,6 @@ class restore_root_task extends restore_task {
|
||||
$events->set_ui(new backup_setting_ui_checkbox($events, get_string('rootsettingcalendarevents', 'backup')));
|
||||
$events->get_ui()->set_changeable($changeable);
|
||||
$this->add_setting($events);
|
||||
$users->add_dependency($events);
|
||||
|
||||
// Define completion (dependent of users)
|
||||
$defaultvalue = false; // Safer default
|
||||
|
||||
@@ -1866,12 +1866,12 @@ class restore_enrolments_structure_step extends restore_structure_step {
|
||||
|
||||
protected function define_structure() {
|
||||
|
||||
$paths = array();
|
||||
$enrol = new restore_path_element('enrol', '/enrolments/enrols/enrol');
|
||||
$enrolment = new restore_path_element('enrolment', '/enrolments/enrols/enrol/user_enrolments/enrolment');
|
||||
// Attach local plugin stucture to enrol element.
|
||||
$this->add_plugin_structure('enrol', $enrol);
|
||||
|
||||
$paths[] = new restore_path_element('enrol', '/enrolments/enrols/enrol');
|
||||
$paths[] = new restore_path_element('enrolment', '/enrolments/enrols/enrol/user_enrolments/enrolment');
|
||||
|
||||
return $paths;
|
||||
return array($enrol, $enrolment);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2337,15 +2337,17 @@ class restore_calendarevents_structure_step extends restore_structure_step {
|
||||
}
|
||||
|
||||
public function process_calendarevents($data) {
|
||||
global $DB, $SITE;
|
||||
global $DB, $SITE, $USER;
|
||||
|
||||
$data = (object)$data;
|
||||
$oldid = $data->id;
|
||||
$restorefiles = true; // We'll restore the files
|
||||
// Find the userid and the groupid associated with the event. Return if not found.
|
||||
// Find the userid and the groupid associated with the event.
|
||||
$data->userid = $this->get_mappingid('user', $data->userid);
|
||||
if ($data->userid === false) {
|
||||
return;
|
||||
// Blank user ID means that we are dealing with module generated events such as quiz starting times.
|
||||
// Use the current user ID for these events.
|
||||
$data->userid = $USER->id;
|
||||
}
|
||||
if (!empty($data->groupid)) {
|
||||
$data->groupid = $this->get_mappingid('group', $data->groupid);
|
||||
@@ -2502,18 +2504,42 @@ class restore_course_completion_structure_step extends restore_structure_step {
|
||||
$data->timeend = $this->apply_date_offset($data->timeend);
|
||||
|
||||
// Map the role from the criteria
|
||||
if (!empty($data->role)) {
|
||||
$data->role = $this->get_mappingid('role', $data->role);
|
||||
}
|
||||
if (isset($data->role) && $data->role != '') {
|
||||
// Newer backups should include roleshortname, which makes this much easier.
|
||||
if (!empty($data->roleshortname)) {
|
||||
$roleinstanceid = $DB->get_field('role', 'id', array('shortname' => $data->roleshortname));
|
||||
if (!$roleinstanceid) {
|
||||
$this->log(
|
||||
'Could not match the role shortname in course_completion_criteria, so skipping',
|
||||
backup::LOG_DEBUG
|
||||
);
|
||||
return;
|
||||
}
|
||||
$data->role = $roleinstanceid;
|
||||
} else {
|
||||
$data->role = $this->get_mappingid('role', $data->role);
|
||||
}
|
||||
|
||||
$skipcriteria = false;
|
||||
// Check we have an id, otherwise it causes all sorts of bugs.
|
||||
if (!$data->role) {
|
||||
$this->log(
|
||||
'Could not match role in course_completion_criteria, so skipping',
|
||||
backup::LOG_DEBUG
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If the completion criteria is for a module we need to map the module instance
|
||||
// to the new module id.
|
||||
if (!empty($data->moduleinstance) && !empty($data->module)) {
|
||||
$data->moduleinstance = $this->get_mappingid('course_module', $data->moduleinstance);
|
||||
if (empty($data->moduleinstance)) {
|
||||
$skipcriteria = true;
|
||||
$this->log(
|
||||
'Could not match the module instance in course_completion_criteria, so skipping',
|
||||
backup::LOG_DEBUG
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$data->module = null;
|
||||
@@ -2524,28 +2550,30 @@ class restore_course_completion_structure_step extends restore_structure_step {
|
||||
if (!empty($data->courseinstanceshortname)) {
|
||||
$courseinstanceid = $DB->get_field('course', 'id', array('shortname'=>$data->courseinstanceshortname));
|
||||
if (!$courseinstanceid) {
|
||||
$skipcriteria = true;
|
||||
$this->log(
|
||||
'Could not match the course instance in course_completion_criteria, so skipping',
|
||||
backup::LOG_DEBUG
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$courseinstanceid = null;
|
||||
}
|
||||
$data->courseinstance = $courseinstanceid;
|
||||
|
||||
if (!$skipcriteria) {
|
||||
$params = array(
|
||||
'course' => $data->course,
|
||||
'criteriatype' => $data->criteriatype,
|
||||
'enrolperiod' => $data->enrolperiod,
|
||||
'courseinstance' => $data->courseinstance,
|
||||
'module' => $data->module,
|
||||
'moduleinstance' => $data->moduleinstance,
|
||||
'timeend' => $data->timeend,
|
||||
'gradepass' => $data->gradepass,
|
||||
'role' => $data->role
|
||||
);
|
||||
$newid = $DB->insert_record('course_completion_criteria', $params);
|
||||
$this->set_mapping('course_completion_criteria', $data->id, $newid);
|
||||
}
|
||||
$params = array(
|
||||
'course' => $data->course,
|
||||
'criteriatype' => $data->criteriatype,
|
||||
'enrolperiod' => $data->enrolperiod,
|
||||
'courseinstance' => $data->courseinstance,
|
||||
'module' => $data->module,
|
||||
'moduleinstance' => $data->moduleinstance,
|
||||
'timeend' => $data->timeend,
|
||||
'gradepass' => $data->gradepass,
|
||||
'role' => $data->role
|
||||
);
|
||||
$newid = $DB->insert_record('course_completion_criteria', $params);
|
||||
$this->set_mapping('course_completion_criteria', $data->id, $newid);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2605,7 +2633,21 @@ class restore_course_completion_structure_step extends restore_structure_step {
|
||||
'timecompleted' => $this->apply_date_offset($data->timecompleted),
|
||||
'reaggregate' => $data->reaggregate
|
||||
);
|
||||
$DB->insert_record('course_completions', $params);
|
||||
|
||||
$existing = $DB->get_record('course_completions', array(
|
||||
'userid' => $data->userid,
|
||||
'course' => $data->course
|
||||
));
|
||||
|
||||
// MDL-46651 - If cron writes out a new record before we get to it
|
||||
// then we should replace it with the Truth data from the backup.
|
||||
// This may be obsolete after MDL-48518 is resolved
|
||||
if ($existing) {
|
||||
$params['id'] = $existing->id;
|
||||
$DB->update_record('course_completions', $params);
|
||||
} else {
|
||||
$DB->insert_record('course_completions', $params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2723,7 +2765,7 @@ class restore_course_logs_structure_step extends restore_structure_step {
|
||||
$manager = get_log_manager();
|
||||
if (method_exists($manager, 'legacy_add_to_log')) {
|
||||
$manager->legacy_add_to_log($data->course, $data->module, $data->action, $data->url,
|
||||
$data->info, $data->cmid, $data->userid);
|
||||
$data->info, $data->cmid, $data->userid, $data->ip, $data->time);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2772,7 +2814,7 @@ class restore_activity_logs_structure_step extends restore_course_logs_structure
|
||||
$manager = get_log_manager();
|
||||
if (method_exists($manager, 'legacy_add_to_log')) {
|
||||
$manager->legacy_add_to_log($data->course, $data->module, $data->action, $data->url,
|
||||
$data->info, $data->cmid, $data->userid);
|
||||
$data->info, $data->cmid, $data->userid, $data->ip, $data->time);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3145,11 +3187,52 @@ class restore_block_instance_structure_step extends restore_structure_step {
|
||||
}
|
||||
|
||||
if (!$bi->instance_allow_multiple()) {
|
||||
if ($DB->record_exists_sql("SELECT bi.id
|
||||
FROM {block_instances} bi
|
||||
JOIN {block} b ON b.name = bi.blockname
|
||||
WHERE bi.parentcontextid = ?
|
||||
AND bi.blockname = ?", array($data->parentcontextid, $data->blockname))) {
|
||||
// The block cannot be added twice, so we will check if the same block is already being
|
||||
// displayed on the same page. For this, rather than mocking a page and using the block_manager
|
||||
// we use a similar query to the one in block_manager::load_blocks(), this will give us
|
||||
// a very good idea of the blocks already displayed in the context.
|
||||
$params = array(
|
||||
'blockname' => $data->blockname
|
||||
);
|
||||
|
||||
// Context matching test.
|
||||
$context = context::instance_by_id($data->parentcontextid);
|
||||
$contextsql = 'bi.parentcontextid = :contextid';
|
||||
$params['contextid'] = $context->id;
|
||||
|
||||
$parentcontextids = $context->get_parent_context_ids();
|
||||
if ($parentcontextids) {
|
||||
list($parentcontextsql, $parentcontextparams) =
|
||||
$DB->get_in_or_equal($parentcontextids, SQL_PARAMS_NAMED);
|
||||
$contextsql = "($contextsql OR (bi.showinsubcontexts = 1 AND bi.parentcontextid $parentcontextsql))";
|
||||
$params = array_merge($params, $parentcontextparams);
|
||||
}
|
||||
|
||||
// Page type pattern test.
|
||||
$pagetypepatterns = matching_page_type_patterns_from_pattern($data->pagetypepattern);
|
||||
list($pagetypepatternsql, $pagetypepatternparams) =
|
||||
$DB->get_in_or_equal($pagetypepatterns, SQL_PARAMS_NAMED);
|
||||
$params = array_merge($params, $pagetypepatternparams);
|
||||
|
||||
// Sub page pattern test.
|
||||
$subpagepatternsql = 'bi.subpagepattern IS NULL';
|
||||
if ($data->subpagepattern !== null) {
|
||||
$subpagepatternsql = "($subpagepatternsql OR bi.subpagepattern = :subpagepattern)";
|
||||
$params['subpagepattern'] = $data->subpagepattern;
|
||||
}
|
||||
|
||||
$exists = $DB->record_exists_sql("SELECT bi.id
|
||||
FROM {block_instances} bi
|
||||
JOIN {block} b ON b.name = bi.blockname
|
||||
WHERE bi.blockname = :blockname
|
||||
AND $contextsql
|
||||
AND bi.pagetypepattern $pagetypepatternsql
|
||||
AND $subpagepatternsql", $params);
|
||||
if ($exists) {
|
||||
// There is at least one very similar block visible on the page where we
|
||||
// are trying to restore the block. In these circumstances the block API
|
||||
// would not allow the user to add another instance of the block, so we
|
||||
// apply the same rule here.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -6,6 +6,7 @@ require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
|
||||
|
||||
$contextid = required_param('contextid', PARAM_INT);
|
||||
$stage = optional_param('stage', restore_ui::STAGE_CONFIRM, PARAM_INT);
|
||||
$cancel = optional_param('cancel', '', PARAM_ALPHA);
|
||||
|
||||
list($context, $course, $cm) = get_context_info_array($contextid);
|
||||
|
||||
@@ -30,7 +31,10 @@ $PAGE->set_title($courseshortname . ': ' . get_string('restore'));
|
||||
$PAGE->set_heading($coursefullname);
|
||||
|
||||
$renderer = $PAGE->get_renderer('core','backup');
|
||||
echo $OUTPUT->header();
|
||||
if (empty($cancel)) {
|
||||
// Do not print the header if user cancelled the process, as we are going to redirect the user.
|
||||
echo $OUTPUT->header();
|
||||
}
|
||||
|
||||
// Prepare a progress bar which can display optionally during long-running
|
||||
// operations while setting up the UI.
|
||||
|
||||
@@ -396,18 +396,36 @@ abstract class backup_cron_automated_helper {
|
||||
$results = $bc->get_results();
|
||||
$outcome = self::outcome_from_results($results);
|
||||
$file = $results['backup_destination']; // May be empty if file already moved to target location.
|
||||
if (!file_exists($dir) || !is_dir($dir) || !is_writable($dir)) {
|
||||
|
||||
if (empty($dir) && $storage !== 0) {
|
||||
// This is intentionally left as a warning instead of an error because of the current behaviour of backup settings.
|
||||
// See MDL-48266 for details.
|
||||
$bc->log('No directory specified for automated backups',
|
||||
backup::LOG_WARNING);
|
||||
$outcome = self::BACKUP_STATUS_WARNING;
|
||||
} else if ($storage !== 0 && (!file_exists($dir) || !is_dir($dir) || !is_writable($dir))) {
|
||||
// If we need to copy the backup file to an external dir and it is not writable, change status to error.
|
||||
$bc->log('Specified backup directory is not writable - ',
|
||||
backup::LOG_ERROR, $dir);
|
||||
$dir = null;
|
||||
$outcome = self::BACKUP_STATUS_ERROR;
|
||||
}
|
||||
|
||||
// Copy file only if there was no error.
|
||||
if ($file && !empty($dir) && $storage !== 0 && $outcome != self::BACKUP_STATUS_ERROR) {
|
||||
$filename = backup_plan_dbops::get_default_backup_filename($format, $type, $course->id, $users, $anonymised,
|
||||
!$config->backup_shortname);
|
||||
if (!$file->copy_content_to($dir.'/'.$filename)) {
|
||||
$bc->log('Attempt to copy backup file to the specified directory failed - ',
|
||||
backup::LOG_ERROR, $dir);
|
||||
$outcome = self::BACKUP_STATUS_ERROR;
|
||||
}
|
||||
if ($outcome != self::BACKUP_STATUS_ERROR && $storage === 1) {
|
||||
$file->delete();
|
||||
if (!$file->delete()) {
|
||||
$outcome = self::BACKUP_STATUS_WARNING;
|
||||
$bc->log('Attempt to delete the backup file from course automated backup area failed - ',
|
||||
backup::LOG_WARNING, $file->get_filename());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,9 +574,6 @@ abstract class backup_cron_automated_helper {
|
||||
return true;
|
||||
}
|
||||
|
||||
$backupword = str_replace(' ', '_', core_text::strtolower(get_string('backupfilename')));
|
||||
$backupword = trim(clean_filename($backupword), '_');
|
||||
|
||||
if (!file_exists($dir) || !is_dir($dir) || !is_writable($dir)) {
|
||||
$dir = null;
|
||||
}
|
||||
@@ -573,9 +588,6 @@ abstract class backup_cron_automated_helper {
|
||||
$files = array();
|
||||
// Store all the matching files into timemodified => stored_file array.
|
||||
foreach ($fs->get_area_files($context->id, $component, $filearea, $itemid) as $file) {
|
||||
if (strpos($file->get_filename(), $backupword) !== 0) {
|
||||
continue;
|
||||
}
|
||||
$files[$file->get_timemodified()] = $file;
|
||||
}
|
||||
if (count($files) <= $keep) {
|
||||
@@ -595,8 +607,8 @@ abstract class backup_cron_automated_helper {
|
||||
if (!empty($dir) && ($storage == 1 || $storage == 2)) {
|
||||
// Calculate backup filename regex, ignoring the date/time/info parts that can be
|
||||
// variable, depending of languages, formats and automated backup settings.
|
||||
$filename = $backupword . '-' . backup::FORMAT_MOODLE . '-' . backup::TYPE_1COURSE . '-' . $course->id . '-';
|
||||
$regex = '#^'.preg_quote($filename, '#').'.*\.mbz$#';
|
||||
$filename = backup::FORMAT_MOODLE . '-' . backup::TYPE_1COURSE . '-' . $course->id . '-';
|
||||
$regex = '#' . preg_quote($filename, '#') . '.*\.mbz$#';
|
||||
|
||||
// Store all the matching files into filename => timemodified array.
|
||||
$files = array();
|
||||
|
||||
@@ -230,11 +230,15 @@ abstract class backup_general_helper extends backup_helper {
|
||||
* This will only extract the moodle_backup.xml file from an MBZ
|
||||
* file and then call {@link self::get_backup_information()}.
|
||||
*
|
||||
* This can be a long-running (multi-minute) operation for large backups.
|
||||
* Pass a $progress value to receive progress updates.
|
||||
*
|
||||
* @param string $filepath absolute path to the MBZ file.
|
||||
* @param file_progress $progress Progress updates
|
||||
* @return stdClass containing information.
|
||||
* @since Moodle 2.4
|
||||
*/
|
||||
public static function get_backup_information_from_mbz($filepath) {
|
||||
public static function get_backup_information_from_mbz($filepath, file_progress $progress = null) {
|
||||
global $CFG;
|
||||
if (!is_readable($filepath)) {
|
||||
throw new backup_helper_exception('missing_moodle_backup_file', $filepath);
|
||||
@@ -245,7 +249,7 @@ abstract class backup_general_helper extends backup_helper {
|
||||
$tmpdir = $CFG->tempdir . '/backup/' . $tmpname;
|
||||
$fp = get_file_packer('application/vnd.moodle.backup');
|
||||
|
||||
$extracted = $fp->extract_to_pathname($filepath, $tmpdir, array('moodle_backup.xml'));
|
||||
$extracted = $fp->extract_to_pathname($filepath, $tmpdir, array('moodle_backup.xml'), $progress);
|
||||
$moodlefile = $tmpdir . '/' . 'moodle_backup.xml';
|
||||
if (!$extracted || !is_readable($moodlefile)) {
|
||||
throw new backup_helper_exception('missing_moodle_backup_xml_file', $moodlefile);
|
||||
|
||||
@@ -298,6 +298,10 @@ abstract class backup_helper {
|
||||
@chmod($filedest, $CFG->filepermissions); // may fail because the permissions may not make sense outside of dataroot
|
||||
unlink($filepath);
|
||||
return null;
|
||||
} else {
|
||||
$bc = backup_controller::load_controller($backupid);
|
||||
$bc->log('Attempt to copy backup file to the specified directory using filesystem failed - ',
|
||||
backup::LOG_WARNING, $dir);
|
||||
}
|
||||
// bad luck, try to deal with the file the old way - keep backup in file area if we can not copy to ext system
|
||||
}
|
||||
|
||||
@@ -301,6 +301,11 @@ class core_backup_renderer extends plugin_renderer_base {
|
||||
$html .= html_writer::end_tag('form');
|
||||
}
|
||||
|
||||
// If we are restoring an activity, then include the current course.
|
||||
if (!$wholecourse) {
|
||||
$courses->invalidate_results(); // Clean list of courses.
|
||||
$courses->set_include_currentcourse();
|
||||
}
|
||||
if (!empty($courses) && ($courses->get_count() > 0 || $courses->get_search())) {
|
||||
// Existing course
|
||||
$hasrestoreoption = true;
|
||||
@@ -311,10 +316,7 @@ class core_backup_renderer extends plugin_renderer_base {
|
||||
$html .= $this->backup_detail_input(get_string('restoretoexistingcourseadding', 'backup'), 'radio', 'target', backup::TARGET_EXISTING_ADDING, array('checked'=>'checked'));
|
||||
$html .= $this->backup_detail_input(get_string('restoretoexistingcoursedeleting', 'backup'), 'radio', 'target', backup::TARGET_EXISTING_DELETING);
|
||||
} else {
|
||||
// We only allow restore adding to existing for now. Enforce it here.
|
||||
$html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'target', 'value'=>backup::TARGET_EXISTING_ADDING));
|
||||
$courses->invalidate_results(); // Clean list of courses
|
||||
$courses->set_include_currentcourse(); // Show current course in the list
|
||||
}
|
||||
$selectacoursehtml = $this->backup_detail_pair(get_string('selectacourse', 'backup'), $this->render($courses));
|
||||
// Display the course selection as required if the form was submitted but this data was not supplied.
|
||||
|
||||
@@ -223,7 +223,7 @@ abstract class award_criteria {
|
||||
if (!empty($this->params)) {
|
||||
if (count($this->params) > 1) {
|
||||
echo $OUTPUT->box(get_string('criteria_descr_' . $this->criteriatype, 'badges',
|
||||
strtoupper($agg[$data->get_aggregation_method($this->criteriatype)])), array('clearfix'));
|
||||
core_text::strtoupper($agg[$data->get_aggregation_method($this->criteriatype)])), array('clearfix'));
|
||||
} else {
|
||||
echo $OUTPUT->box(get_string('criteria_descr_single_' . $this->criteriatype , 'badges'), array('clearfix'));
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class award_criteria_overall extends award_criteria {
|
||||
echo html_writer::table($table);
|
||||
} else {
|
||||
echo $OUTPUT->box(get_string('criteria_descr_' . $this->criteriatype, 'badges',
|
||||
strtoupper($agg[$data->get_aggregation_method()])), 'clearfix');
|
||||
core_text::strtoupper($agg[$data->get_aggregation_method()])), 'clearfix');
|
||||
}
|
||||
echo $OUTPUT->box_end();
|
||||
}
|
||||
|
||||
+4
-4
@@ -140,7 +140,7 @@ class core_badges_renderer extends plugin_renderer_base {
|
||||
$dl = array();
|
||||
$dl[get_string('name')] = $badge->name;
|
||||
$dl[get_string('description', 'badges')] = $badge->description;
|
||||
$dl[get_string('createdon', 'search')] = $badge->timecreated;
|
||||
$dl[get_string('createdon', 'search')] = userdate($badge->timecreated);
|
||||
$dl[get_string('badgeimage', 'badges')] = print_badge_image($badge, $context, 'large');
|
||||
$display .= $this->definition_list($dl);
|
||||
|
||||
@@ -362,7 +362,7 @@ class core_badges_renderer extends plugin_renderer_base {
|
||||
$items[] = get_string('criteria_descr_single_' . $type , 'badges') . $c->get_details();
|
||||
} else {
|
||||
$items[] = get_string('criteria_descr_' . $type , 'badges',
|
||||
strtoupper($agg[$badge->get_aggregation_method($type)])) . $c->get_details();
|
||||
core_text::strtoupper($agg[$badge->get_aggregation_method($type)])) . $c->get_details();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -716,7 +716,7 @@ class core_badges_renderer extends plugin_renderer_base {
|
||||
}
|
||||
} else {
|
||||
$output .= get_string('criteria_descr_' . $short . BADGE_CRITERIA_TYPE_OVERALL, 'badges',
|
||||
strtoupper($agg[$badge->get_aggregation_method()]));
|
||||
core_text::strtoupper($agg[$badge->get_aggregation_method()]));
|
||||
}
|
||||
$items = array();
|
||||
unset($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]);
|
||||
@@ -725,7 +725,7 @@ class core_badges_renderer extends plugin_renderer_base {
|
||||
$items[] = get_string('criteria_descr_single_' . $short . $type , 'badges') . $c->get_details($short);
|
||||
} else {
|
||||
$items[] = get_string('criteria_descr_' . $short . $type , 'badges',
|
||||
strtoupper($agg[$badge->get_aggregation_method($type)])) . $c->get_details($short);
|
||||
core_text::strtoupper($agg[$badge->get_aggregation_method($type)])) . $c->get_details($short);
|
||||
}
|
||||
}
|
||||
$output .= html_writer::alist($items, array(), 'ul');
|
||||
|
||||
@@ -73,5 +73,8 @@ function xmldb_block_badges_upgrade($oldversion, $block) {
|
||||
upgrade_block_savepoint(true, 2014062600, $blockname);
|
||||
}
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -73,5 +73,8 @@ function xmldb_block_calendar_month_upgrade($oldversion, $block) {
|
||||
upgrade_block_savepoint(true, 2014062600, $blockname);
|
||||
}
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -73,5 +73,8 @@ function xmldb_block_calendar_upcoming_upgrade($oldversion, $block) {
|
||||
upgrade_block_savepoint(true, 2014062600, $blockname);
|
||||
}
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ class block_comments extends block_base {
|
||||
$args->displaycancel = false;
|
||||
$comment = new comment($args);
|
||||
$comment->set_view_permission(true);
|
||||
$comment->set_fullwidth();
|
||||
|
||||
$this->content = new stdClass();
|
||||
$this->content->text = $comment->output(true);
|
||||
|
||||
@@ -63,5 +63,8 @@ function xmldb_block_community_upgrade($oldversion) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,8 @@ class block_completionstatus extends block_base {
|
||||
|
||||
// Create empty content.
|
||||
$this->content = new stdClass();
|
||||
$this->content->text = '';
|
||||
$this->content->footer = '';
|
||||
|
||||
// Can edit settings?
|
||||
$can_edit = has_capability('moodle/course:update', $context);
|
||||
@@ -67,13 +69,13 @@ class block_completionstatus extends block_base {
|
||||
// Don't display if completion isn't enabled!
|
||||
if (!completion_info::is_enabled_for_site()) {
|
||||
if ($can_edit) {
|
||||
$this->content->text = get_string('completionnotenabledforsite', 'completion');
|
||||
$this->content->text .= get_string('completionnotenabledforsite', 'completion');
|
||||
}
|
||||
return $this->content;
|
||||
|
||||
} else if (!$info->is_enabled()) {
|
||||
if ($can_edit) {
|
||||
$this->content->text = get_string('completionnotenabledforcourse', 'completion');
|
||||
$this->content->text .= get_string('completionnotenabledforcourse', 'completion');
|
||||
}
|
||||
return $this->content;
|
||||
}
|
||||
@@ -84,7 +86,7 @@ class block_completionstatus extends block_base {
|
||||
// Check if this course has any criteria.
|
||||
if (empty($completions)) {
|
||||
if ($can_edit) {
|
||||
$this->content->text = get_string('nocriteriaset', 'completion');
|
||||
$this->content->text .= get_string('nocriteriaset', 'completion');
|
||||
}
|
||||
return $this->content;
|
||||
}
|
||||
@@ -230,11 +232,11 @@ class block_completionstatus extends block_base {
|
||||
$rows = array_merge($rows, $srows);
|
||||
|
||||
$table->data = $rows;
|
||||
$this->content->text = html_writer::table($table);
|
||||
$this->content->text .= html_writer::table($table);
|
||||
|
||||
// Display link to detailed view.
|
||||
$details = new moodle_url('/blocks/completionstatus/details.php', array('course' => $course->id));
|
||||
$this->content->footer = html_writer::link($details, get_string('moredetails', 'completion'));
|
||||
$this->content->footer .= html_writer::link($details, get_string('moredetails', 'completion'));
|
||||
} else {
|
||||
// If user is not enrolled, show error.
|
||||
$this->content->text = get_string('nottracked', 'completion');
|
||||
|
||||
@@ -70,5 +70,8 @@ function xmldb_block_completionstatus_upgrade($oldversion, $block) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -74,5 +74,8 @@ function xmldb_block_course_summary_upgrade($oldversion, $block) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -84,6 +84,12 @@ class block_edit_form extends moodleform {
|
||||
$weightoptions[$last] = get_string('bracketlast', 'block', $last);
|
||||
|
||||
$regionoptions = $this->page->theme->get_all_block_regions();
|
||||
foreach ($this->page->blocks->get_regions() as $region) {
|
||||
// Make sure to add all custom regions of this particular page too.
|
||||
if (!isset($regionoptions[$region])) {
|
||||
$regionoptions[$region] = $region;
|
||||
}
|
||||
}
|
||||
|
||||
$parentcontext = context::instance_by_id($this->block->instance->parentcontextid);
|
||||
$mform->addElement('hidden', 'bui_parentcontextid', $parentcontext->id);
|
||||
|
||||
@@ -50,5 +50,8 @@ function xmldb_block_html_upgrade($oldversion) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -75,5 +75,8 @@ function xmldb_block_navigation_upgrade($oldversion, $block) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -84,5 +84,8 @@ function xmldb_block_recent_activity_upgrade($oldversion, $block) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
$feed->set_cache_duration($CFG->block_rss_client_timeout*60);
|
||||
}
|
||||
|
||||
if(debugging() && $feed->error()){
|
||||
if ($CFG->debugdeveloper && $feed->error()) {
|
||||
return '<p>'. $feedrecord->url .' Failed with code: '.$feed->error().'</p>';
|
||||
}
|
||||
|
||||
@@ -314,8 +314,7 @@
|
||||
$feed->init();
|
||||
|
||||
if ($feed->error()) {
|
||||
mtrace ('error');
|
||||
mtrace ('SimplePie failed with error:'.$feed->error());
|
||||
mtrace('Error: could not load/find the RSS feed');
|
||||
$status = false;
|
||||
} else {
|
||||
mtrace ('ok');
|
||||
|
||||
@@ -89,7 +89,7 @@ class feed_edit_form extends moodleform {
|
||||
$rss->init();
|
||||
|
||||
if ($rss->error()) {
|
||||
$errors['url'] = get_string('errorloadingfeed', 'block_rss_client', $rss->error());
|
||||
$errors['url'] = get_string('couldnotfindloadrssfeed', 'block_rss_client');
|
||||
} else {
|
||||
$this->title = $rss->get_title();
|
||||
$this->description = $rss->get_description();
|
||||
|
||||
@@ -33,6 +33,7 @@ $string['clientshowchannellinklabel'] = 'Should a link to the original site (cha
|
||||
$string['clientshowimagelabel'] = 'Show channel image if available :';
|
||||
$string['configblock'] = 'Configure this block';
|
||||
$string['couldnotfindfeed'] = 'Could not find feed with id';
|
||||
$string['couldnotfindloadrssfeed'] = 'Could not find or load the RSS feed.';
|
||||
$string['customtitlelabel'] = 'Custom title (leave blank to use title supplied by feed):';
|
||||
$string['deletefeedconfirm'] = 'Are you sure you want to delete this feed?';
|
||||
$string['disabledrssfeeds'] = 'RSS feeds are disabled';
|
||||
|
||||
@@ -86,5 +86,8 @@ function xmldb_block_section_links_upgrade($oldversion, $block) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -74,5 +74,8 @@ function xmldb_block_selfcompletion_upgrade($oldversion, $block) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -75,5 +75,8 @@ function xmldb_block_settings_upgrade($oldversion, $block) {
|
||||
// Moodle v2.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -32,3 +32,27 @@ Feature: Add and configure blocks throughout the site
|
||||
And I follow "Course 1"
|
||||
# The first block matching the pattern should be top-left block
|
||||
And I should see "Comments" in the "//*[@id='region-pre' or @id='block-region-side-pre']/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' block ')]" "xpath_element"
|
||||
|
||||
Scenario: My home does not allow assignation of roles on blocks
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| manager1 | Manager | 1 | manager1@asd.com |
|
||||
And I log in as "manager1"
|
||||
And I click on "My home" "link" in the "Navigation" "block"
|
||||
When I press "Customise this page"
|
||||
Then I should not see "Assign roles in Navigation block"
|
||||
|
||||
Scenario: Blocks on courses can have roles assigned to them
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | teacher | 1 | teacher@example.com |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Turn editing on"
|
||||
Then I should see "Assign roles in Search forums block"
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
=== 2.7 ===
|
||||
|
||||
* blog_entry->add_association() does not accept any params.
|
||||
* blog_entry->add_associations() accepts only one param.
|
||||
* blog_entry->add_associations() does not accept any params.
|
||||
* blog_entry->add_association() accepts only one param.
|
||||
|
||||
Vendored
+11
-11
@@ -236,36 +236,36 @@ The second method is a lot more intensive for the system. There are defined inva
|
||||
When you invalidate by event the cache API finds all of the definitions that subscribe to the event, it then loads the stores for each of those definitions and purges the keys from each store.
|
||||
This is obviously a recursive, and therefore, intense process.
|
||||
|
||||
### Unit tests
|
||||
Both the cache API and the cache stores have unit tests.
|
||||
Please be aware that several of the cache stores require configuration in order to be able operate in the unit tests.
|
||||
Tests for stores requiring configuration that havn't been configured will be skipped.
|
||||
### Testing
|
||||
Both the cache API and the cache stores have tests.
|
||||
Please be aware that several of the cache stores require configuration in order to be able operate in the tests.
|
||||
Tests for stores requiring configuration that haven't been configured will be skipped.
|
||||
All configuration is done in your sites config.php through definitions.
|
||||
The following snippet illustates how to configure the three core cache stores that require configuration.
|
||||
The following snippet illustrates how to configure the three core cache stores that require configuration.
|
||||
|
||||
define('TEST_CACHESTORE_MEMCACHE_TESTSERVERS', '127.0.0.1:11211');
|
||||
define('TEST_CACHESTORE_MEMCACHED_TESTSERVERS', '127.0.0.1:11211');
|
||||
define('TEST_CACHESTORE_MONGODB_TESTSERVER', 'mongodb://localhost:27017');
|
||||
|
||||
As of Moodle 2.8 it is also possible to set the default cache stores used when running unit tests.
|
||||
As of Moodle 2.8 it is also possible to set the default cache stores used when running tests.
|
||||
You can do this by adding the following define to your config.php file:
|
||||
|
||||
// xxx is one of Memcache, Memecached, mongodb or other cachestore with a test define.
|
||||
// xxx is one of Memcache, Memcached, mongodb or other cachestore with a test define.
|
||||
define('TEST_CACHE_USING_APPLICATION_STORE', 'xxx');
|
||||
|
||||
This allows you to run tests against a defined test store. It uses the defined value to identify a store to test against with a matching TEST_CACHESTORE define.
|
||||
Alternatively you can also run unit tests against an actual cache config.
|
||||
Alternatively you can also run tests against an actual cache config.
|
||||
To do this you must add the following to your config.php file:
|
||||
|
||||
define('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH', true');
|
||||
$CFG->altcacheconfigpath = '/a/temp/directory/yoursite.php'
|
||||
|
||||
This tells Moodle to use the config at $CFG->altcacheconfigpath when running unit tests.
|
||||
This tells Moodle to use the config at $CFG->altcacheconfigpath when running tests.
|
||||
There are a couple of considerations to using this method:
|
||||
* By setting $CFG->altcacheconfigpath your site will store the cache config in the specified path, not just the unit test cache config but your site config as well.
|
||||
* By setting $CFG->altcacheconfigpath your site will store the cache config in the specified path, not just the test cache config but your site config as well.
|
||||
* If you have configured your cache before setting $CFG->altcacheconfigpath you will need to copy it from moodledata/muc/config.php to the destination you specified.
|
||||
* This allows you to share a cache config between sites.
|
||||
* It also allows you to use unit tests to test your sites cache config.
|
||||
* It also allows you to use tests to test your sites cache config.
|
||||
|
||||
Please be aware that if you are using Memcache or Memcached it is recommended to use dedicated Memcached servers.
|
||||
When caches get purged the memcached servers you have configured get purged, any data stored within them whether it belongs to Moodle or not will be removed.
|
||||
|
||||
Vendored
+4
-4
@@ -242,7 +242,8 @@ class cache_definition {
|
||||
protected $datasourcefile = null;
|
||||
|
||||
/**
|
||||
* The data source class aggregate to use. This is a super advanced setting.
|
||||
* Deprecated - this is completely unused.
|
||||
* @deprecated since 2.8.2
|
||||
* @var string
|
||||
*/
|
||||
protected $datasourceaggregate = null;
|
||||
@@ -324,11 +325,11 @@ class cache_definition {
|
||||
*
|
||||
* @param string $id
|
||||
* @param array $definition
|
||||
* @param string $datasourceaggregate
|
||||
* @param string $unused Used to be datasourceaggregate but that was removed and this is now unused.
|
||||
* @return cache_definition
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public static function load($id, array $definition, $datasourceaggregate = null) {
|
||||
public static function load($id, array $definition, $unused = null) {
|
||||
global $CFG;
|
||||
|
||||
if (!array_key_exists('mode', $definition)) {
|
||||
@@ -520,7 +521,6 @@ class cache_definition {
|
||||
$cachedefinition->overrideclassfile = $overrideclassfile;
|
||||
$cachedefinition->datasource = $datasource;
|
||||
$cachedefinition->datasourcefile = $datasourcefile;
|
||||
$cachedefinition->datasourceaggregate = $datasourceaggregate;
|
||||
$cachedefinition->staticacceleration = $staticacceleration;
|
||||
$cachedefinition->staticaccelerationsize = $staticaccelerationsize;
|
||||
$cachedefinition->ttl = $ttl;
|
||||
|
||||
Vendored
+16
-23
@@ -126,8 +126,8 @@ class cache_factory {
|
||||
// situation. It will use disabled alternatives where available.
|
||||
require_once($CFG->dirroot.'/cache/disabledlib.php');
|
||||
self::$instance = new cache_factory_disabled();
|
||||
} else if (defined('PHPUNIT_TEST') && PHPUNIT_TEST) {
|
||||
// We're using the regular factory.
|
||||
} else if ((defined('PHPUNIT_TEST') && PHPUNIT_TEST) || defined('BEHAT_SITE_RUNNING')) {
|
||||
// We're using the test factory.
|
||||
require_once($CFG->dirroot.'/cache/tests/fixtures/lib.php');
|
||||
self::$instance = new cache_phpunit_factory();
|
||||
if (defined('CACHE_DISABLE_STORES') && CACHE_DISABLE_STORES !== false) {
|
||||
@@ -186,17 +186,17 @@ class cache_factory {
|
||||
* @param string $component
|
||||
* @param string $area
|
||||
* @param array $identifiers
|
||||
* @param string $aggregate
|
||||
* @param string $unused Used to be data source aggregate however that was removed and this is now unused.
|
||||
* @return cache_application|cache_session|cache_request
|
||||
*/
|
||||
public function create_cache_from_definition($component, $area, array $identifiers = array(), $aggregate = null) {
|
||||
public function create_cache_from_definition($component, $area, array $identifiers = array(), $unused = null) {
|
||||
$definitionname = $component.'/'.$area;
|
||||
if (isset($this->cachesfromdefinitions[$definitionname])) {
|
||||
$cache = $this->cachesfromdefinitions[$definitionname];
|
||||
$cache->set_identifiers($identifiers);
|
||||
return $cache;
|
||||
}
|
||||
$definition = $this->create_definition($component, $area, $aggregate);
|
||||
$definition = $this->create_definition($component, $area);
|
||||
$definition->set_identifiers($identifiers);
|
||||
$cache = $this->create_cache($definition, $identifiers);
|
||||
// Loaders are always held onto to speed up subsequent requests.
|
||||
@@ -335,16 +335,17 @@ class cache_factory {
|
||||
|
||||
// The class to use.
|
||||
$class = 'cache_config';
|
||||
$unittest = defined('PHPUNIT_TEST') && PHPUNIT_TEST;
|
||||
// Are we running tests of some form?
|
||||
$testing = (defined('PHPUNIT_TEST') && PHPUNIT_TEST) || defined('BEHAT_SITE_RUNNING');
|
||||
|
||||
// Check if this is a PHPUnit test and redirect to the phpunit config classes if it is.
|
||||
if ($unittest) {
|
||||
if ($testing) {
|
||||
require_once($CFG->dirroot.'/cache/locallib.php');
|
||||
require_once($CFG->dirroot.'/cache/tests/fixtures/lib.php');
|
||||
// We have just a single class for PHP unit tests. We don't care enough about its
|
||||
// performance to do otherwise and having a single method allows us to inject things into it
|
||||
// while testing.
|
||||
$class = 'cache_config_phpunittest';
|
||||
$class = 'cache_config_testing';
|
||||
}
|
||||
|
||||
// Check if we need to create a config file with defaults.
|
||||
@@ -352,7 +353,7 @@ class cache_factory {
|
||||
|
||||
if ($writer || $needtocreate) {
|
||||
require_once($CFG->dirroot.'/cache/locallib.php');
|
||||
if (!$unittest) {
|
||||
if (!$testing) {
|
||||
$class .= '_writer';
|
||||
}
|
||||
}
|
||||
@@ -392,14 +393,13 @@ class cache_factory {
|
||||
* Creates a definition instance or returns the existing one if it has already been created.
|
||||
* @param string $component
|
||||
* @param string $area
|
||||
* @param string $aggregate
|
||||
* @param string $unused This used to be data source aggregate - however that functionality has been removed and
|
||||
* this argument is now unused.
|
||||
* @return cache_definition
|
||||
* @throws coding_exception If the definition cannot be found.
|
||||
*/
|
||||
public function create_definition($component, $area, $aggregate = null) {
|
||||
public function create_definition($component, $area, $unused = null) {
|
||||
$id = $component.'/'.$area;
|
||||
if ($aggregate) {
|
||||
$id .= '::'.$aggregate;
|
||||
}
|
||||
if (!isset($this->definitions[$id])) {
|
||||
// This is the first time this definition has been requested.
|
||||
if ($this->is_initialising()) {
|
||||
@@ -421,13 +421,6 @@ class cache_factory {
|
||||
// To serve this purpose and avoid errors we are going to make use of an ad-hoc cache rather than
|
||||
// search for the definition which would possibly cause an infitite loop trying to initialise the cache.
|
||||
$definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, $component, $area);
|
||||
if ($aggregate !== null) {
|
||||
// If you get here you deserve a warning. We have to use an ad-hoc cache here, so we can't find the definition and therefor
|
||||
// can't find any information about the datasource or any of its aggregated.
|
||||
// Best of luck.
|
||||
debugging('An unknown cache was requested during development with an aggregate that could not be loaded. Ad-hoc cache used instead.', DEBUG_DEVELOPER);
|
||||
$aggregate = null;
|
||||
}
|
||||
} else {
|
||||
// Either a typo of the developer has just created the definition and is using it for the first time.
|
||||
$this->reset();
|
||||
@@ -440,10 +433,10 @@ class cache_factory {
|
||||
debugging('Cache definitions reparsed causing cache reset in order to locate definition.
|
||||
You should bump the version number to ensure definitions are reprocessed.', DEBUG_DEVELOPER);
|
||||
}
|
||||
$definition = cache_definition::load($id, $definition, $aggregate);
|
||||
$definition = cache_definition::load($id, $definition);
|
||||
}
|
||||
} else {
|
||||
$definition = cache_definition::load($id, $definition, $aggregate);
|
||||
$definition = cache_definition::load($id, $definition);
|
||||
}
|
||||
}
|
||||
$this->definitions[$id] = $definition;
|
||||
|
||||
Vendored
-8
@@ -280,13 +280,6 @@ class cache_helper {
|
||||
/**
|
||||
* Purges the cache for a specific definition.
|
||||
*
|
||||
* If you need to purge a definition that requires identifiers or an aggregate and you don't
|
||||
* know the details of those please use cache_helper::purge_stores_used_by_definition instead.
|
||||
* It is a more aggressive purge and will purge all data within the store, not just the data
|
||||
* belonging to the given definition.
|
||||
*
|
||||
* @todo MDL-36660: Change the signature: $aggregate must be added.
|
||||
*
|
||||
* @param string $component
|
||||
* @param string $area
|
||||
* @param array $identifiers
|
||||
@@ -298,7 +291,6 @@ class cache_helper {
|
||||
// Initialise, in case of a store.
|
||||
if ($cache instanceof cache_store) {
|
||||
$factory = cache_factory::instance();
|
||||
// TODO MDL-36660: Providing $aggregate is required for purging purposes: $definition->get_id()
|
||||
$definition = $factory->create_definition($component, $area, null);
|
||||
$definition->set_identifiers($identifiers);
|
||||
$cache->initialise($definition);
|
||||
|
||||
Vendored
+3
-3
@@ -166,12 +166,12 @@ class cache implements cache_loader {
|
||||
* @param string $component The component for the definition
|
||||
* @param string $area The area for the definition
|
||||
* @param array $identifiers Any additional identifiers that should be provided to the definition.
|
||||
* @param string $aggregate Super advanced feature. More docs later.
|
||||
* @param string $unused Used to be datasourceaggregate but that was removed and this is now unused.
|
||||
* @return cache_application|cache_session|cache_store
|
||||
*/
|
||||
public static function make($component, $area, array $identifiers = array(), $aggregate = null) {
|
||||
public static function make($component, $area, array $identifiers = array(), $unused = null) {
|
||||
$factory = cache_factory::instance();
|
||||
return $factory->create_cache_from_definition($component, $area, $identifiers, $aggregate);
|
||||
return $factory->create_cache_from_definition($component, $area, $identifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+12
@@ -377,4 +377,16 @@ abstract class cache_store implements cache_store_interface {
|
||||
public function get_warnings() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this cache store instance is both suitable for testing, and ready for testing.
|
||||
*
|
||||
* Cache stores that support being used as the default store for unit and acceptance testing should
|
||||
* override this function and return true if there requirements have been met.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ready_to_be_used_for_testing() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+5
-5
@@ -184,10 +184,10 @@ class cache_factory_disabled extends cache_factory {
|
||||
*
|
||||
* @param string $component
|
||||
* @param string $area
|
||||
* @param string $aggregate Unused.
|
||||
* @param string $unused Used to be datasourceaggregate but that was removed and this is now unused.
|
||||
* @return cache_definition
|
||||
*/
|
||||
public function create_definition($component, $area, $aggregate = null) {
|
||||
public function create_definition($component, $area, $unused = null) {
|
||||
return cache_definition::load_adhoc(cache_store::MODE_REQUEST, $component, $area);
|
||||
}
|
||||
|
||||
@@ -208,11 +208,11 @@ class cache_factory_disabled extends cache_factory {
|
||||
* @param string $component
|
||||
* @param string $area
|
||||
* @param array $identifiers
|
||||
* @param string $aggregate
|
||||
* @param string $unused Used to be datasourceaggregate but that was removed and this is now unused.
|
||||
* @return cache_application|cache_session|cache_request
|
||||
*/
|
||||
public function create_cache_from_definition($component, $area, array $identifiers = array(), $aggregate = null) {
|
||||
$definition = $this->create_definition($component, $area, $aggregate);
|
||||
public function create_cache_from_definition($component, $area, array $identifiers = array(), $unused = null) {
|
||||
$definition = $this->create_definition($component, $area);
|
||||
$cache = $this->create_cache($definition, $identifiers);
|
||||
return $cache;
|
||||
}
|
||||
|
||||
+2
-2
@@ -66,5 +66,5 @@ ipaddress:port
|
||||
</pre>';
|
||||
$string['sessionhandlerconflict'] = 'Warning: A memcache instance ({$a}) has being configured to use the same memcached server as sessions. Purging all caches will lead to sessions also being purged.';
|
||||
$string['testservers'] = 'Test servers';
|
||||
$string['testservers_desc'] = 'The test servers get used for unit tests and for performance tests. It is entirely optional to set up test servers. Servers should be defined one per line and consist of a server address and optionally a port and weight.
|
||||
If no port is provided then the default port (11211) is used.';
|
||||
$string['testservers_desc'] = 'One or more connection strings for memcache servers to test against. If a test server has been specified then memcache performance can be tested using the cache performance page in the administration block.
|
||||
As an example: 127.0.0.1:11211';
|
||||
|
||||
Vendored
+12
@@ -633,4 +633,16 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
|
||||
}
|
||||
return $warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this cache store instance is both suitable for testing, and ready for testing.
|
||||
*
|
||||
* Cache stores that support being used as the default store for unit and acceptance testing should
|
||||
* override this function and return true if there requirements have been met.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ready_to_be_used_for_testing() {
|
||||
return defined('TEST_CACHESTORE_MEMCACHE_TESTSERVERS');
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -80,11 +80,11 @@ ipaddress:port
|
||||
</pre>';
|
||||
$string['sessionhandlerconflict'] = 'Warning: A memcached instance ({$a}) has being configured to use the same memcached server as sessions. Purging all caches will lead to sessions also being purged.';
|
||||
$string['testservers'] = 'Test servers';
|
||||
$string['testservers_desc'] = 'The test servers get used for unit tests and for performance tests. It is entirely optional to set up test servers. Servers should be defined one per line and consist of a server address and optionally a port and weight.
|
||||
If no port is provided then the default port (11211) is used.';
|
||||
$string['testservers_desc'] = 'One or more connection strings for memcached servers to test against. If a test server has been specified then memcached performance can be tested using the cache performance page in the administration block.
|
||||
As an example: 127.0.0.1:11211';
|
||||
$string['usecompression'] = 'Use compression';
|
||||
$string['usecompression_help'] = 'Enables or disables payload compression. When enabled, item values longer than a certain threshold (currently 100 bytes) will be compressed during storage and decompressed during retrieval transparently.';
|
||||
$string['useserialiser'] = 'Use serialiser';
|
||||
$string['useserialiser_help'] = 'Specifies the serializer to use for serializing non-scalar values.
|
||||
The valid serializers are Memcached::SERIALIZER_PHP or Memcached::SERIALIZER_IGBINARY.
|
||||
The latter is supported only when memcached is configured with --enable-memcached-igbinary option and the igbinary extension is loaded.';
|
||||
The latter is supported only when memcached is configured with --enable-memcached-igbinary option and the igbinary extension is loaded.';
|
||||
|
||||
Vendored
+12
@@ -696,4 +696,16 @@ class cachestore_memcached extends cache_store implements cache_is_configurable
|
||||
}
|
||||
return $warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this cache store instance is both suitable for testing, and ready for testing.
|
||||
*
|
||||
* Cache stores that support being used as the default store for unit and acceptance testing should
|
||||
* override this function and return true if there requirements have been met.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ready_to_be_used_for_testing() {
|
||||
return defined('TEST_CACHESTORE_MEMCACHED_TESTSERVERS');
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -35,7 +35,8 @@ $string['replicaset_help'] = 'The name of the replica set to connect to. If this
|
||||
$string['server'] = 'Server';
|
||||
$string['server_help'] = 'This is the connection string for the server you want to use. Multiple servers can be specified using a comma-separated list.';
|
||||
$string['testserver'] = 'Test server';
|
||||
$string['testserver_desc'] = 'This is the connection string for the test server you want to use. Test servers are entirely optional, by specifiying a test server you can run PHPunit tests for this store and can run the performance tests.';
|
||||
$string['testserver_desc'] = 'The connection string for a server to use for testing. If a test server has been specified then MongoDB performance can be tested using the cache performance page in the administration block.
|
||||
As an example: mongodb://127.0.0.1:27017';
|
||||
$string['username'] = 'Username';
|
||||
$string['username_help'] = 'The username to use when making a connection.';
|
||||
$string['usesafe'] = 'Use safe';
|
||||
|
||||
Vendored
+13
-1
@@ -85,7 +85,7 @@ class cachestore_mongodb extends cache_store implements cache_is_configurable {
|
||||
* Determines if and what safe setting is to be used.
|
||||
* @var bool|int
|
||||
*/
|
||||
protected $usesafe = false;
|
||||
protected $usesafe = true;
|
||||
|
||||
/**
|
||||
* If set to true then multiple identifiers will be requested and used.
|
||||
@@ -599,4 +599,16 @@ class cachestore_mongodb extends cache_store implements cache_is_configurable {
|
||||
public function my_name() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this cache store instance is both suitable for testing, and ready for testing.
|
||||
*
|
||||
* Cache stores that support being used as the default store for unit and acceptance testing should
|
||||
* override this function and return true if there requirements have been met.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ready_to_be_used_for_testing() {
|
||||
return defined('TEST_CACHESTORE_MONGODB_TESTSERVER');
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -49,7 +49,7 @@ class cachestore_session_test extends cachestore_tests {
|
||||
* Test the maxsize option.
|
||||
*/
|
||||
public function test_maxsize() {
|
||||
$config = cache_config_phpunittest::instance();
|
||||
$config = cache_config_testing::instance();
|
||||
$config->phpunit_add_definition('phpunit/one', array(
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'component' => 'phpunit',
|
||||
@@ -171,7 +171,7 @@ class cachestore_session_test extends cachestore_tests {
|
||||
}
|
||||
|
||||
public function test_ttl() {
|
||||
$config = cache_config_phpunittest::instance();
|
||||
$config = cache_config_testing::instance();
|
||||
$config->phpunit_add_definition('phpunit/three', array(
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'component' => 'phpunit',
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ class cachestore_static_test extends cachestore_tests {
|
||||
*/
|
||||
public function test_maxsize() {
|
||||
$defid = 'phpunit/testmaxsize';
|
||||
$config = cache_config_phpunittest::instance();
|
||||
$config = cache_config_testing::instance();
|
||||
$config->phpunit_add_definition($defid, array(
|
||||
'mode' => cache_store::MODE_REQUEST,
|
||||
'component' => 'phpunit',
|
||||
|
||||
+2
-2
@@ -48,7 +48,7 @@ class core_cache_administration_helper_testcase extends advanced_testcase {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
cache_factory::reset();
|
||||
cache_config_phpunittest::create_default_configuration();
|
||||
cache_config_testing::create_default_configuration();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +193,7 @@ class core_cache_administration_helper_testcase extends advanced_testcase {
|
||||
set_debugging(DEBUG_ALL);
|
||||
|
||||
// First with simplekeys
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/hashtest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
|
||||
Vendored
+63
-39
@@ -47,7 +47,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
cache_factory::reset();
|
||||
cache_config_phpunittest::create_default_configuration();
|
||||
cache_config_testing::create_default_configuration();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,10 +63,34 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* @return string
|
||||
*/
|
||||
protected function get_expected_application_cache_store() {
|
||||
global $CFG;
|
||||
$expected = 'cachestore_file';
|
||||
|
||||
// Verify if we are using any of the available ways to use a different application store within tests.
|
||||
if (defined('TEST_CACHE_USING_APPLICATION_STORE') && preg_match('#[a-zA-Z][a-zA-Z0-9_]*#', TEST_CACHE_USING_APPLICATION_STORE)) {
|
||||
// 1st way. Using some of the testing servers.
|
||||
$expected = 'cachestore_'.(string)TEST_CACHE_USING_APPLICATION_STORE;
|
||||
|
||||
} else if (defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH && !empty($CFG->altcacheconfigpath)) {
|
||||
// 2nd way. Using an alternative configuration.
|
||||
$defaultstores = cache_helper::get_stores_suitable_for_mode_default();
|
||||
$instance = cache_config::instance();
|
||||
// Iterate over defined mode mappings until we get an application one not being the default.
|
||||
foreach ($instance->get_mode_mappings() as $mapping) {
|
||||
// If the store is not for application mode, ignore.
|
||||
if ($mapping['mode'] !== cache_store::MODE_APPLICATION) {
|
||||
continue;
|
||||
}
|
||||
// If the store matches some default mapping store name, ignore.
|
||||
if (array_key_exists($mapping['store'], $defaultstores) && !empty($defaultstores[$mapping['store']]['default'])) {
|
||||
continue;
|
||||
}
|
||||
// Arrived here, have found an application mode store not being the default mapped one (file),
|
||||
// that's the one we are using in the configuration for sure.
|
||||
$expected = 'cachestore_'.$mapping['store'];
|
||||
}
|
||||
}
|
||||
|
||||
return $expected;
|
||||
}
|
||||
|
||||
@@ -90,9 +114,9 @@ class core_cache_testcase extends advanced_testcase {
|
||||
}
|
||||
|
||||
$instance = cache_config::instance();
|
||||
$this->assertInstanceOf('cache_config_phpunittest', $instance);
|
||||
$this->assertInstanceOf('cache_config_testing', $instance);
|
||||
|
||||
$this->assertTrue(cache_config_phpunittest::config_file_exists());
|
||||
$this->assertTrue(cache_config_testing::config_file_exists());
|
||||
|
||||
$stores = $instance->get_all_stores();
|
||||
$this->assertCount(3, $stores);
|
||||
@@ -150,7 +174,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Tests for cache keys that would break on windows.
|
||||
*/
|
||||
public function test_windows_nasty_keys() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/windowskeytest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -171,7 +195,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
$this->assertInstanceOf('cache_application', $cache);
|
||||
$this->run_on_cache($cache);
|
||||
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/test_default_application_cache', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -206,7 +230,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Tests using a cache system when there are no stores available (who knows what the admin did to achieve this).
|
||||
*/
|
||||
public function test_on_cache_without_store() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/nostoretest1', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -435,7 +459,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Tests a definition using a data loader
|
||||
*/
|
||||
public function test_definition_data_loader() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/datasourcetest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -476,7 +500,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Tests a definition using an overridden loader
|
||||
*/
|
||||
public function test_definition_overridden_loader() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/overridetest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -501,8 +525,8 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Test the mappingsonly setting.
|
||||
*/
|
||||
public function test_definition_mappings_only() {
|
||||
/** @var cache_config_phpunittest $instance */
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
/** @var cache_config_testing $instance */
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/mappingsonly', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -530,7 +554,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Test a very basic definition.
|
||||
*/
|
||||
public function test_definition() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/test', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -548,7 +572,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Test a definition using the simple keys.
|
||||
*/
|
||||
public function test_definition_simplekeys() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/simplekeytest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -574,7 +598,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Test a negative TTL on an application cache.
|
||||
*/
|
||||
public function test_application_ttl_negative() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/ttltest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -623,7 +647,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Test a positive TTL on an application cache.
|
||||
*/
|
||||
public function test_application_ttl_positive() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/ttltest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -672,7 +696,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Test a negative TTL on an session cache.
|
||||
*/
|
||||
public function test_session_ttl_positive() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/ttltest', array(
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'component' => 'phpunit',
|
||||
@@ -721,7 +745,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Tests manual locking operations on an application cache
|
||||
*/
|
||||
public function test_application_manual_locking() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/lockingtest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -750,7 +774,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Tests application cache event invalidation
|
||||
*/
|
||||
public function test_application_event_invalidation() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/eventinvalidationtest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -785,7 +809,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Tests session cache event invalidation
|
||||
*/
|
||||
public function test_session_event_invalidation() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/test_session_event_invalidation', array(
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'component' => 'phpunit',
|
||||
@@ -821,7 +845,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Tests application cache definition invalidation
|
||||
*/
|
||||
public function test_application_definition_invalidation() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/definitioninvalidation', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -857,7 +881,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Tests session cache definition invalidation
|
||||
*/
|
||||
public function test_session_definition_invalidation() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/test_session_definition_invalidation', array(
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'component' => 'phpunit',
|
||||
@@ -901,7 +925,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
// We need to add data the to cache, invalidate it by event, manually force it back without MUC knowing to simulate a
|
||||
// disconnected/distributed setup (think load balanced server using local cache), instantiate the cache again and finally
|
||||
// check that it is not picked up.
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/eventinvalidationtest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -939,7 +963,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
|
||||
// Test 1: Rebuild without the event and test its there.
|
||||
cache_factory::reset();
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/eventinvalidationtest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -952,7 +976,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
|
||||
// Test 2: Rebuild and test the invalidation of the event via the invalidation cache.
|
||||
cache_factory::reset();
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/eventinvalidationtest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -971,7 +995,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Tests application cache event purge
|
||||
*/
|
||||
public function test_application_event_purge() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/eventpurgetest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -1022,7 +1046,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Tests session cache event purge
|
||||
*/
|
||||
public function test_session_event_purge() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/eventpurgetest', array(
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'component' => 'phpunit',
|
||||
@@ -1073,7 +1097,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Tests application cache definition purge
|
||||
*/
|
||||
public function test_application_definition_purge() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/definitionpurgetest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -1108,7 +1132,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
}
|
||||
$this->resetAfterTest();
|
||||
$CFG->altcacheconfigpath = $CFG->dataroot.'/cache/altcacheconfigpath';
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$this->assertInstanceOf('cache_config', $instance);
|
||||
}
|
||||
|
||||
@@ -1116,7 +1140,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Test disabling the cache stores.
|
||||
*/
|
||||
public function test_disable_stores() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/disabletest1', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -1245,14 +1269,14 @@ class core_cache_testcase extends advanced_testcase {
|
||||
|
||||
$factory = cache_factory::instance(true);
|
||||
$config = $factory->create_config_instance();
|
||||
$this->assertEquals('cache_config_phpunittest', get_class($config));
|
||||
$this->assertEquals('cache_config_testing', get_class($config));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that multiple application loaders work ok.
|
||||
*/
|
||||
public function test_multiple_application_loaders() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_file_store('phpunittest1');
|
||||
$instance->phpunit_add_file_store('phpunittest2');
|
||||
$instance->phpunit_add_definition('phpunit/multi_loader', array(
|
||||
@@ -1320,8 +1344,8 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Test that multiple application loaders work ok.
|
||||
*/
|
||||
public function test_multiple_session_loaders() {
|
||||
/* @var cache_config_phpunittest $instance */
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
/* @var cache_config_testing $instance */
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_session_store('phpunittest1');
|
||||
$instance->phpunit_add_session_store('phpunittest2');
|
||||
$instance->phpunit_add_definition('phpunit/multi_loader', array(
|
||||
@@ -1418,7 +1442,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
*/
|
||||
public function test_session_cache_switch_user_application_mapping() {
|
||||
$this->resetAfterTest(true);
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_file_store('testfilestore');
|
||||
$instance->phpunit_add_definition('phpunit/testappsession', array(
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
@@ -1453,7 +1477,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Test two session caches being used at once to confirm collisions don't occur.
|
||||
*/
|
||||
public function test_dual_session_caches() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/testsess1', array(
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'component' => 'phpunit',
|
||||
@@ -1518,7 +1542,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Test application locking.
|
||||
*/
|
||||
public function test_application_locking() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/test_application_locking', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -1544,7 +1568,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Test the static cache_helper method purge_stores_used_by_definition.
|
||||
*/
|
||||
public function test_purge_stores_used_by_definition() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/test_purge_stores_used_by_definition', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -1566,7 +1590,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Test purge routines.
|
||||
*/
|
||||
public function test_purge_routines() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/purge1', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -1613,7 +1637,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
* Test that the default stores all support searching.
|
||||
*/
|
||||
public function test_defaults_support_searching() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance = cache_config_testing::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/search1', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
@@ -1660,7 +1684,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
}
|
||||
|
||||
public function test_static_acceleration() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/accelerated', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
|
||||
Vendored
+2
-2
@@ -47,7 +47,7 @@ class core_cache_config_writer_testcase extends advanced_testcase {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
cache_factory::reset();
|
||||
cache_config_phpunittest::create_default_configuration();
|
||||
cache_config_testing::create_default_configuration();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,7 +261,7 @@ class core_cache_config_writer_testcase extends advanced_testcase {
|
||||
* Test setting some definition mappings.
|
||||
*/
|
||||
public function test_set_definition_mappings() {
|
||||
$config = cache_config_phpunittest::instance(true);
|
||||
$config = cache_config_testing::instance(true);
|
||||
$config->phpunit_add_definition('phpunit/testdefinition', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
|
||||
Vendored
+29
-11
@@ -31,12 +31,16 @@ defined('MOODLE_INTERNAL') || die();
|
||||
require_once($CFG->dirroot.'/cache/locallib.php');
|
||||
|
||||
/**
|
||||
* Override the default cache configuration for our own maniacle purposes.
|
||||
* Override the default cache configuration for our own maniacal purposes.
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* This class was originally named cache_config_phpunittest but was renamed in 2.9
|
||||
* because it is used for both unit tests and acceptance tests.
|
||||
*
|
||||
* @since 2.9
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_config_phpunittest extends cache_config_writer {
|
||||
class cache_config_testing extends cache_config_writer {
|
||||
|
||||
/**
|
||||
* Creates the default configuration and saves it.
|
||||
@@ -61,14 +65,13 @@ class cache_config_phpunittest extends cache_config_writer {
|
||||
$appdefine = defined('TEST_CACHE_USING_APPLICATION_STORE') ? TEST_CACHE_USING_APPLICATION_STORE : false;
|
||||
if ($appdefine !== false && preg_match('/^[a-zA-Z][a-zA-Z0-9_]+$/', $appdefine)) {
|
||||
$expectedstore = $appdefine;
|
||||
$expecteddefine = 'TEST_CACHESTORE_'.strtoupper($expectedstore).'_TESTSERVERS';
|
||||
$file = $CFG->dirroot.'/cache/stores/'.$appdefine.'/lib.php';
|
||||
$class = 'cachestore_'.$appdefine;
|
||||
if (file_exists($file)) {
|
||||
require_once($file);
|
||||
}
|
||||
if (defined($expecteddefine) && class_exists($class)) {
|
||||
/** @var cache_store $class */
|
||||
if (class_exists($class) && $class::ready_to_be_used_for_testing()) {
|
||||
/* @var cache_store $class */
|
||||
$writer->configstores['test_application'] = array(
|
||||
'use_test_store' => true,
|
||||
'name' => 'test_application',
|
||||
@@ -136,10 +139,10 @@ class cache_config_phpunittest extends cache_config_writer {
|
||||
|
||||
if (!empty($CFG->altcacheconfigpath)) {
|
||||
|
||||
if (defined('PHPUNIT_TEST') && PHPUNIT_TEST &&
|
||||
(!defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') || !TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH)) {
|
||||
// We're within a unit test, but TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH has not being defined or is
|
||||
// false, we want to use the default.
|
||||
// No need to check we are within a test here, this is the cache config class that gets used
|
||||
// only when one of those is true.
|
||||
if (!defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') || !TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH) {
|
||||
// TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH has not being defined or is false, we want to use the default.
|
||||
return $configpath;
|
||||
}
|
||||
|
||||
@@ -287,6 +290,21 @@ class cache_config_phpunittest extends cache_config_writer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a deprecated class. It has been renamed to cache_config_testing.
|
||||
*
|
||||
* This was deprecated in Moodle 2.9 but will be removed at the next major release
|
||||
* as it is only used during testing and its highly unlikely anyone has used this.
|
||||
*
|
||||
* @deprecated since 2.9
|
||||
* @copyright 2014 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_config_phpunittest extends cache_config_testing {
|
||||
// We can't do anything here to warn the user.
|
||||
// The cache can be utilised before sessions have even been started.
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy object for testing cacheable object interface and interaction
|
||||
*
|
||||
|
||||
Vendored
+13
-1
@@ -1,6 +1,18 @@
|
||||
This files describes API changes in /cache/stores/* - cache store plugins.
|
||||
Information provided here is intended especially for developers.
|
||||
|
||||
=== 2.8.2 ===
|
||||
* Cache data source aggregation functionality has been removed. This functionality was found to be broken and unused.
|
||||
It was decided that rather than fixing it it should be removed.
|
||||
As well as the processing code being removed the following API changes have been made.
|
||||
The following changes have come about because of it:
|
||||
- cache_definition::$datasourceaggregate is deprecated an unused.
|
||||
- cache_definition::load Argument 3 (final arg) is now unused.
|
||||
- cache_factory::create_cache_from_definition Argument 4 (final arg) is now unused.
|
||||
- cache::make Argument 4 (final arg) is now unused.
|
||||
* cache_config_phpunittest has been renamed to cache_config_testing
|
||||
* New method cache_store::ready_to_be_used_for_testing() that returns true|false if the store is suitable and ready for use as the primary store during unit and acceptance tests.
|
||||
|
||||
=== 2.7 ===
|
||||
* cache_store::is_ready is no longer abstract, calling cache_store::are_requirements_met by default.
|
||||
|
||||
@@ -22,4 +34,4 @@ Information provided here is intended especially for developers.
|
||||
* cleanup method renamed to instance_deleted.
|
||||
It is now called when the store is deleted as all comments suggested anyway.
|
||||
* instance_created method added.
|
||||
It is called when the store is created for the very first time.
|
||||
It is called when the store is created for the very first time.
|
||||
|
||||
@@ -173,13 +173,19 @@ class core_calendar_external extends external_api {
|
||||
|
||||
// Let us findout courses that we can return events from.
|
||||
if (!$hassystemcap) {
|
||||
$courses = enrol_get_my_courses();
|
||||
$courses = array_keys($courses);
|
||||
foreach ($params['events']['courseids'] as $id) {
|
||||
if (in_array($id, $courses)) {
|
||||
try {
|
||||
$context = context_course::instance($id);
|
||||
self::validate_context($context);
|
||||
$funcparam['courses'][] = $id;
|
||||
} else {
|
||||
$warnings[] = array('item' => $id, 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to access this course');
|
||||
} catch (Exception $e) {
|
||||
$warnings[] = array(
|
||||
'item' => 'course',
|
||||
'itemid' => $id,
|
||||
'warningcode' => 'nopermissions',
|
||||
'message' => 'No access rights in course context '.$e->getMessage().$e->getTraceAsString()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
+53
-1
@@ -1890,6 +1890,54 @@ function calendar_add_event_allowed($event) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert region timezone to php supported timezone
|
||||
*
|
||||
* @param string $tz value from ical file
|
||||
* @return string $tz php supported timezone
|
||||
*/
|
||||
function calendar_normalize_tz($tz) {
|
||||
switch ($tz) {
|
||||
case('CST'):
|
||||
case('Central Time'):
|
||||
case('Central Standard Time'):
|
||||
$tz = 'America/Chicago';
|
||||
break;
|
||||
case('CET'):
|
||||
case('Central European Time'):
|
||||
$tz = 'Europe/Berlin';
|
||||
break;
|
||||
case('EST'):
|
||||
case('Eastern Time'):
|
||||
case('Eastern Standard Time'):
|
||||
$tz = 'America/New_York';
|
||||
break;
|
||||
case('PST'):
|
||||
case('Pacific Time'):
|
||||
case('Pacific Standard Time'):
|
||||
$tz = 'America/Los_Angeles';
|
||||
break;
|
||||
case('China Time'):
|
||||
case('China Standard Time'):
|
||||
$tz = 'Asia/Beijing';
|
||||
break;
|
||||
case('IST'):
|
||||
case('India Time'):
|
||||
case('India Standard Time'):
|
||||
$tz = 'Asia/New_Delhi';
|
||||
break;
|
||||
case('JST');
|
||||
case('Japan Time'):
|
||||
case('Japan Standard Time'):
|
||||
$tz = 'Asia/Tokyo';
|
||||
break;
|
||||
case('Romance Standard Time'):
|
||||
$tz = 'Europe/Brussels';
|
||||
break;
|
||||
}
|
||||
return $tz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage calendar events
|
||||
*
|
||||
@@ -2919,12 +2967,14 @@ function calendar_add_icalendar_event($event, $courseid, $subscriptionid, $timez
|
||||
$defaulttz = date_default_timezone_get();
|
||||
$tz = isset($event->properties['DTSTART'][0]->parameters['TZID']) ? $event->properties['DTSTART'][0]->parameters['TZID'] :
|
||||
$timezone;
|
||||
$tz = calendar_normalize_tz($tz);
|
||||
$eventrecord->timestart = strtotime($event->properties['DTSTART'][0]->value . ' ' . $tz);
|
||||
if (empty($event->properties['DTEND'])) {
|
||||
$eventrecord->timeduration = 0; // no duration if no end time specified
|
||||
} else {
|
||||
$endtz = isset($event->properties['DTEND'][0]->parameters['TZID']) ? $event->properties['DTEND'][0]->parameters['TZID'] :
|
||||
$timezone;
|
||||
$endtz = calendar_normalize_tz($endtz);
|
||||
$eventrecord->timeduration = strtotime($event->properties['DTEND'][0]->value . ' ' . $endtz) - $eventrecord->timestart;
|
||||
}
|
||||
|
||||
@@ -3065,7 +3115,9 @@ function calendar_import_icalendar_events($ical, $courseid, $subscriptionid = nu
|
||||
$updatecount = 0;
|
||||
|
||||
// Large calendars take a while...
|
||||
core_php_time_limit::raise(300);
|
||||
if (!CLI_SCRIPT) {
|
||||
core_php_time_limit::raise(300);
|
||||
}
|
||||
|
||||
// Mark all events in a subscription with a zero timestamp.
|
||||
if (!empty($subscriptionid)) {
|
||||
|
||||
@@ -74,4 +74,63 @@ class core_calendar_ical_testcase extends advanced_testcase {
|
||||
$this->setExpectedException('coding_exception');
|
||||
calendar_update_subscription($subscription);
|
||||
}
|
||||
|
||||
public function test_calendar_add_subscription() {
|
||||
global $DB, $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/lib/bennu/bennu.inc.php');
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Test for Microsoft Outlook 2010.
|
||||
$subscription = new stdClass();
|
||||
$subscription->name = 'Microsoft Outlook 2010';
|
||||
$subscription->importfrom = CALENDAR_IMPORT_FROM_FILE;
|
||||
$subscription->eventtype = 'site';
|
||||
$id = calendar_add_subscription($subscription);
|
||||
|
||||
$calendar = file_get_contents($CFG->dirroot . '/lib/tests/fixtures/ms_outlook_2010.ics');
|
||||
$ical = new iCalendar();
|
||||
$ical->unserialize($calendar);
|
||||
$this->assertEquals($ical->parser_errors, array());
|
||||
|
||||
$sub = calendar_get_subscription($id);
|
||||
$result = calendar_import_icalendar_events($ical, $sub->courseid, $sub->id);
|
||||
$count = $DB->count_records('event', array('subscriptionid' => $sub->id));
|
||||
$this->assertEquals($count, 1);
|
||||
|
||||
// Test for OSX Yosemite.
|
||||
$subscription = new stdClass();
|
||||
$subscription->name = 'OSX Yosemite';
|
||||
$subscription->importfrom = CALENDAR_IMPORT_FROM_FILE;
|
||||
$subscription->eventtype = 'site';
|
||||
$id = calendar_add_subscription($subscription);
|
||||
|
||||
$calendar = file_get_contents($CFG->dirroot . '/lib/tests/fixtures/osx_yosemite.ics');
|
||||
$ical = new iCalendar();
|
||||
$ical->unserialize($calendar);
|
||||
$this->assertEquals($ical->parser_errors, array());
|
||||
|
||||
$sub = calendar_get_subscription($id);
|
||||
$result = calendar_import_icalendar_events($ical, $sub->courseid, $sub->id);
|
||||
$count = $DB->count_records('event', array('subscriptionid' => $sub->id));
|
||||
$this->assertEquals($count, 1);
|
||||
|
||||
// Test for Google Gmail.
|
||||
$subscription = new stdClass();
|
||||
$subscription->name = 'Google Gmail';
|
||||
$subscription->importfrom = CALENDAR_IMPORT_FROM_FILE;
|
||||
$subscription->eventtype = 'site';
|
||||
$id = calendar_add_subscription($subscription);
|
||||
|
||||
$calendar = file_get_contents($CFG->dirroot . '/lib/tests/fixtures/google_gmail.ics');
|
||||
$ical = new iCalendar();
|
||||
$ical->unserialize($calendar);
|
||||
$this->assertEquals($ical->parser_errors, array());
|
||||
|
||||
$sub = calendar_get_subscription($id);
|
||||
$result = calendar_import_icalendar_events($ical, $sub->courseid, $sub->id);
|
||||
$count = $DB->count_records('event', array('subscriptionid' => $sub->id));
|
||||
$this->assertEquals($count, 1);
|
||||
}
|
||||
}
|
||||
|
||||
+20
-14
@@ -64,7 +64,7 @@ M.core_comment = {
|
||||
var ta = Y.one('#dlg-content-'+this.client_id);
|
||||
var scope = this;
|
||||
var value = ta.get('value');
|
||||
if (value && value != M.str.moodle.addcomment) {
|
||||
if (value && value != M.util.get_string('addcomment', 'moodle')) {
|
||||
var params = {'content': value};
|
||||
this.request({
|
||||
action: 'add',
|
||||
@@ -81,9 +81,9 @@ M.core_comment = {
|
||||
var newcomment = Y.Node.create(result.html);
|
||||
container.appendChild(newcomment);
|
||||
var ids = result.ids;
|
||||
var linktext = Y.one('#comment-link-text-'+cid);
|
||||
if (linktext) {
|
||||
linktext.set('innerHTML', M.str.moodle.comments + ' ('+obj.count+')');
|
||||
var linkText = Y.one('#comment-link-text-' + cid);
|
||||
if (linkText) {
|
||||
linkText.set('innerHTML', M.util.get_string('commentscount', 'moodle', obj.count));
|
||||
}
|
||||
for(var i in ids) {
|
||||
var attributes = {
|
||||
@@ -178,7 +178,7 @@ M.core_comment = {
|
||||
val = val.replace('___name___', list[i].fullname);
|
||||
}
|
||||
if (list[i]['delete']||newcmt) {
|
||||
list[i].content = '<div class="comment-delete"><a href="#" id ="comment-delete-'+this.client_id+'-'+list[i].id+'" title="'+M.str.moodle.deletecomment+'"><img alt="" src="'+M.util.image_url('t/delete', 'core')+'" /></a></div>' + list[i].content;
|
||||
list[i].content = '<div class="comment-delete"><a href="#" id ="comment-delete-'+this.client_id+'-'+list[i].id+'" title="'+M.util.get_string('deletecomment', 'moodle')+'"><img alt="" src="'+M.util.image_url('t/delete', 'core')+'" /></a></div>' + list[i].content;
|
||||
}
|
||||
val = val.replace('___time___', list[i].time);
|
||||
val = val.replace('___picture___', list[i].avatar);
|
||||
@@ -201,9 +201,9 @@ M.core_comment = {
|
||||
scope: scope,
|
||||
params: params,
|
||||
callback: function(id, ret, args) {
|
||||
var linktext = Y.one('#comment-link-text-'+scope.client_id);
|
||||
if (ret.count && linktext) {
|
||||
linktext.set('innerHTML', M.str.moodle.comments + ' ('+ret.count+')');
|
||||
var linkText = Y.one('#comment-link-text-' + scope.client_id);
|
||||
if (ret.count && linkText) {
|
||||
linkText.set('innerHTML', M.util.get_string('commentscount', 'moodle', ret.count));
|
||||
}
|
||||
var container = Y.one('#comment-list-'+scope.client_id);
|
||||
var pagination = Y.one('#comment-pagination-'+scope.client_id);
|
||||
@@ -215,7 +215,7 @@ M.core_comment = {
|
||||
}
|
||||
if (ret.error == 'require_login') {
|
||||
var result = {};
|
||||
result.html = M.str.moodle.commentsrequirelogin;
|
||||
result.html = M.util.get_string('commentsrequirelogin', 'moodle');
|
||||
} else {
|
||||
var result = scope.render(ret.list);
|
||||
}
|
||||
@@ -231,10 +231,16 @@ M.core_comment = {
|
||||
},
|
||||
|
||||
dodelete: function(id) { // note: delete is a reserved word in javascript, chrome and safary do not like it at all here!
|
||||
var scope = this;
|
||||
var params = {'commentid': id};
|
||||
var scope = this,
|
||||
cid = scope.client_id,
|
||||
params = {'commentid': id};
|
||||
function remove_dom(type, anim, cmt) {
|
||||
cmt.remove();
|
||||
var linkText = Y.one('#comment-link-text-' + cid),
|
||||
comments = Y.all('#comment-list-' + cid + ' li');
|
||||
if (linkText && comments) {
|
||||
linkText.set('innerHTML', M.util.get_string('commentscount', 'moodle', comments.size()));
|
||||
}
|
||||
}
|
||||
this.request({
|
||||
action: 'delete',
|
||||
@@ -374,13 +380,13 @@ M.core_comment = {
|
||||
return false;
|
||||
}
|
||||
if (focus) {
|
||||
if (t.get('value') == M.str.moodle.addcomment) {
|
||||
if (t.get('value') == M.util.get_string('addcomment', 'moodle')) {
|
||||
t.set('value', '');
|
||||
t.setStyle('color', 'black');
|
||||
}
|
||||
}else{
|
||||
if (t.get('value') == '') {
|
||||
t.set('value', M.str.moodle.addcomment);
|
||||
t.set('value', M.util.get_string('addcomment', 'moodle'));
|
||||
t.setStyle('color','grey');
|
||||
t.set('rows', 2);
|
||||
}
|
||||
@@ -425,7 +431,7 @@ M.core_comment = {
|
||||
return;
|
||||
}
|
||||
var args = {};
|
||||
args.message = M.str.admin.confirmdeletecomments;
|
||||
args.message = M.util.get_string('confirmdeletecomments', 'admin');
|
||||
args.callback = function() {
|
||||
var url = M.cfg.wwwroot + '/comment/index.php';
|
||||
|
||||
|
||||
+37
-5
@@ -71,6 +71,12 @@ class comment {
|
||||
/** @var int The number of comments associated with this comments params */
|
||||
protected $totalcommentcount = null;
|
||||
|
||||
/**
|
||||
* Set to true to remove the col attribute from the textarea making it full width.
|
||||
* @var bool
|
||||
*/
|
||||
protected $fullwidth = false;
|
||||
|
||||
/** @var bool Use non-javascript UI */
|
||||
private static $nonjs = false;
|
||||
/** @var int comment itemid used in non-javascript UI */
|
||||
@@ -240,10 +246,15 @@ class comment {
|
||||
self::$comment_page = optional_param('comment_page', '', PARAM_INT);
|
||||
self::$comment_area = optional_param('comment_area', '', PARAM_AREA);
|
||||
|
||||
$page->requires->string_for_js('addcomment', 'moodle');
|
||||
$page->requires->string_for_js('deletecomment', 'moodle');
|
||||
$page->requires->string_for_js('comments', 'moodle');
|
||||
$page->requires->string_for_js('commentsrequirelogin', 'moodle');
|
||||
$page->requires->strings_for_js(array(
|
||||
'addcomment',
|
||||
'comments',
|
||||
'commentscount',
|
||||
'commentsrequirelogin',
|
||||
'deletecomment',
|
||||
),
|
||||
'moodle'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -459,9 +470,20 @@ class comment {
|
||||
|
||||
if ($this->can_post()) {
|
||||
// print posting textarea
|
||||
$textareaattrs = array(
|
||||
'name' => 'content',
|
||||
'rows' => 2,
|
||||
'id' => 'dlg-content-'.$this->cid
|
||||
);
|
||||
if (!$this->fullwidth) {
|
||||
$textareaattrs['cols'] = '20';
|
||||
} else {
|
||||
$textareaattrs['class'] = 'fullwidth';
|
||||
}
|
||||
|
||||
$html .= html_writer::start_tag('div', array('class' => 'comment-area'));
|
||||
$html .= html_writer::start_tag('div', array('class' => 'db'));
|
||||
$html .= html_writer::tag('textarea', '', array('name' => 'content', 'rows' => 2, 'cols' => 20, 'id' => 'dlg-content-'.$this->cid));
|
||||
$html .= html_writer::tag('textarea', '', $textareaattrs);
|
||||
$html .= html_writer::end_tag('div'); // .db
|
||||
|
||||
$html .= html_writer::start_tag('div', array('class' => 'fd', 'id' => 'comment-action-'.$this->cid));
|
||||
@@ -945,6 +967,16 @@ class comment {
|
||||
public function get_commentarea() {
|
||||
return $this->commentarea;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the comments textarea fullwidth.
|
||||
*
|
||||
* @since 2.8.1 + 2.7.4
|
||||
* @param bool $fullwidth
|
||||
*/
|
||||
public function set_fullwidth($fullwidth = true) {
|
||||
$this->fullwidth = (bool)$fullwidth;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -123,6 +123,9 @@ class completion_criteria_role extends completion_criteria {
|
||||
public function get_title() {
|
||||
global $DB;
|
||||
$role = $DB->get_record('role', array('id' => $this->role));
|
||||
if (!$role) {
|
||||
return '['.get_string('roleidnotfound', 'completion', $this->role).']';
|
||||
}
|
||||
return role_get_name($role, context_course::instance($this->course));
|
||||
}
|
||||
|
||||
|
||||
@@ -105,4 +105,20 @@ class behat_completion extends behat_base {
|
||||
return $steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles completion tracking for course being in the course page.
|
||||
*
|
||||
* @When /^completion tracking is "(?P<completion_status_string>Enabled|Disabled)" in current course$/
|
||||
* @param string $completionstatus The status, enabled or disabled.
|
||||
*/
|
||||
public function completion_is_toggled_in_course($completionstatus) {
|
||||
|
||||
$toggle = strtolower($completionstatus) == 'enabled' ? get_string('yes') : get_string('no');
|
||||
|
||||
return array(
|
||||
new Given('I follow "'.get_string('editsettings').'"'),
|
||||
new Given('I set the field "'.get_string('enablecompletion', 'completion').'" to "'.$toggle.'"'),
|
||||
new Given('I press "'.get_string('savechanges').'"')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
@core @core_completion
|
||||
Feature: Allow teachers to manually mark users as complete when configured
|
||||
In order for teachers to mark students as complete
|
||||
As a teacher
|
||||
I need to be able to use the completion report mark complete functionality
|
||||
|
||||
Scenario: Mark a student as complete using the completion report
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Completion course | CC1 | 0 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Student | First | student1@example.com |
|
||||
| teacher1 | Teacher | First | teacher1@example.com |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | CC1 | student |
|
||||
| teacher1 | CC1 | editingteacher |
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
| Enable completion tracking | 1 |
|
||||
And I am on homepage
|
||||
And I follow "Completion course"
|
||||
And completion tracking is "Enabled" in current course
|
||||
And I follow "Course completion"
|
||||
And I set the field "Teacher" to "1"
|
||||
And I press "Save changes"
|
||||
And I turn editing mode on
|
||||
And I add the "Course completion status" block
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Completion course"
|
||||
And I should see "Status: Not yet started"
|
||||
And I log out
|
||||
When I log in as "teacher1"
|
||||
And I follow "Completion course"
|
||||
And I follow "View course report"
|
||||
And I should see "Student First"
|
||||
And I follow "Click to mark user complete"
|
||||
And I trigger cron
|
||||
And I am on homepage
|
||||
And I log out
|
||||
Then I log in as "student1"
|
||||
And I follow "Completion course"
|
||||
And I should see "Status: Complete"
|
||||
+1
-1
@@ -8,6 +8,6 @@
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "3.7.*",
|
||||
"phpunit/dbUnit": "1.2.*",
|
||||
"moodlehq/behat-extension": "1.28.4"
|
||||
"moodlehq/behat-extension": "1.28.6"
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -635,13 +635,14 @@ M.course_dndupload = {
|
||||
bodyContent: content,
|
||||
width: '350px',
|
||||
modal: true,
|
||||
visible: true,
|
||||
visible: false,
|
||||
render: true,
|
||||
align: {
|
||||
node: null,
|
||||
points: [Y.WidgetPositionAlign.CC, Y.WidgetPositionAlign.CC]
|
||||
}
|
||||
});
|
||||
panel.show();
|
||||
// When the panel is hidden - destroy it and then check for other pending uploads
|
||||
panel.after("visibleChange", function(e) {
|
||||
if (!panel.get('visible')) {
|
||||
|
||||
+11
-6
@@ -483,12 +483,17 @@ function update_moduleinfo($cm, $moduleinfo, $course, $mform = null) {
|
||||
}
|
||||
|
||||
$completion = new completion_info($course);
|
||||
if ($completion->is_enabled() && !empty($moduleinfo->completionunlocked)) {
|
||||
// Update completion settings.
|
||||
$cm->completion = $moduleinfo->completion;
|
||||
$cm->completiongradeitemnumber = $moduleinfo->completiongradeitemnumber;
|
||||
$cm->completionview = $moduleinfo->completionview;
|
||||
$cm->completionexpected = $moduleinfo->completionexpected;
|
||||
if ($completion->is_enabled()) {
|
||||
// Completion settings that would affect users who have already completed
|
||||
// the activity may be locked; if so, these should not be updated.
|
||||
if (!empty($moduleinfo->completionunlocked)) {
|
||||
$cm->completion = $moduleinfo->completion;
|
||||
$cm->completiongradeitemnumber = $moduleinfo->completiongradeitemnumber;
|
||||
$cm->completionview = $moduleinfo->completionview;
|
||||
}
|
||||
// The expected date does not affect users who have completed the activity,
|
||||
// so it is safe to update it regardless of the lock status.
|
||||
$cm->completionexpected = $moduleinfo->completionexpected;
|
||||
}
|
||||
if (!empty($CFG->enableavailability)) {
|
||||
// This code is used both when submitting the form, which uses a long
|
||||
|
||||
+1
-1
@@ -116,7 +116,7 @@ if (empty($pending)) {
|
||||
$row[] = format_string($course->shortname);
|
||||
$row[] = format_string($course->fullname);
|
||||
$row[] = fullname($course->get_requester());
|
||||
$row[] = $course->summary;
|
||||
$row[] = format_text($course->summary, $course->summaryformat);
|
||||
$row[] = $category->get_formatted_name();
|
||||
$row[] = format_string($course->reason);
|
||||
$row[] = $OUTPUT->single_button(new moodle_url($baseurl, array('approve' => $course->id, 'sesskey' => sesskey())), get_string('approve'), 'get') .
|
||||
|
||||
@@ -65,7 +65,9 @@ class course_reset_form extends moodleform {
|
||||
$mform->addElement('header', 'gradebookheader', get_string('gradebook', 'grades'));
|
||||
|
||||
$mform->addElement('checkbox', 'reset_gradebook_items', get_string('removeallcourseitems', 'grades'));
|
||||
$mform->addHelpButton('reset_gradebook_items', 'removeallcourseitems', 'grades');
|
||||
$mform->addElement('checkbox', 'reset_gradebook_grades', get_string('removeallcoursegrades', 'grades'));
|
||||
$mform->addHelpButton('reset_gradebook_grades', 'removeallcoursegrades', 'grades');
|
||||
$mform->disabledIf('reset_gradebook_grades', 'reset_gradebook_items', 'checked');
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
@core @core_course
|
||||
Feature: Edit completion settings of an activity
|
||||
In order to edit completion settings without accidentally breaking user data
|
||||
As a teacher
|
||||
I need to edit the activity and use the unlock button if required
|
||||
|
||||
Background:
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | enablecompletion |
|
||||
| Course 1 | C1 | 1 |
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
| Enable completion tracking | 1 |
|
||||
And I am on homepage
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add a "Page" to section "1" and I fill the form with:
|
||||
| Name | TestPage |
|
||||
| Description | x |
|
||||
| Page content | x |
|
||||
| Completion tracking | 2 |
|
||||
| Require view | 1 |
|
||||
And I follow "Course 1"
|
||||
|
||||
Scenario: Completion is not locked when the activity has not yet been viewed
|
||||
Given I click on "Edit settings" "link" in the "TestPage" activity
|
||||
When I expand all fieldsets
|
||||
Then I should see "Completion tracking"
|
||||
And I should not see "Completion options locked"
|
||||
|
||||
Scenario: Completion is locked after the activity has been viewed
|
||||
Given I follow "TestPage"
|
||||
When I follow "Edit settings"
|
||||
And I expand all fieldsets
|
||||
Then I should see "Completion options locked"
|
||||
|
||||
@javascript
|
||||
Scenario: Pressing the unlock button allows the user to edit completion settings
|
||||
Given I follow "TestPage"
|
||||
When I follow "Edit settings"
|
||||
And I expand all fieldsets
|
||||
And I press "Unlock completion options"
|
||||
Then I should see "Completion options unlocked"
|
||||
And I set the field "Completion tracking" to "Students can manually mark the activity as completed"
|
||||
And I press "Save and display"
|
||||
And I follow "Edit settings"
|
||||
And I expand all fieldsets
|
||||
Then the field "Completion tracking" matches value "Students can manually mark the activity as completed"
|
||||
|
||||
@javascript
|
||||
Scenario: Even when completion is locked, the user can still set the date
|
||||
Given I follow "TestPage"
|
||||
And I follow "Edit settings"
|
||||
And I expand all fieldsets
|
||||
When I click on "id_completionexpected_enabled" "checkbox"
|
||||
And I set the field "id_completionexpected_year" to "2013"
|
||||
And I press "Save and display"
|
||||
And I follow "Edit settings"
|
||||
And I expand all fieldsets
|
||||
Then the field "id_completionexpected_year" matches value "2013"
|
||||
@@ -36,14 +36,15 @@ Feature: We can change the visibility of categories in the management interface.
|
||||
|
||||
And I log in as "admin"
|
||||
And I go to the courses management page
|
||||
And I start watching to see if a new page loads
|
||||
And I should see the "Course categories and courses" management page
|
||||
And I should see "Cat 1" in the "#category-listing ul.ml" "css_element"
|
||||
And category in management listing should be visible "CAT1"
|
||||
And I toggle visibility of category "CAT1" in management listing
|
||||
# AJAX updated.
|
||||
And a new page should not have loaded since I started watching
|
||||
And category in management listing should be dimmed "CAT1"
|
||||
And I toggle visibility of category "CAT1" in management listing
|
||||
# AJAX updated.
|
||||
And a new page should not have loaded since I started watching
|
||||
And category in management listing should be visible "CAT1"
|
||||
|
||||
# Tests hiding and then showing a subcategory.
|
||||
@@ -91,20 +92,21 @@ Feature: We can change the visibility of categories in the management interface.
|
||||
|
||||
And I log in as "admin"
|
||||
And I go to the courses management page
|
||||
And I start watching to see if a new page loads
|
||||
And I should see the "Course categories and courses" management page
|
||||
And I should see "Cat 1" in the "#category-listing ul.ml" "css_element"
|
||||
And I should not see "Cat 2" in the "#category-listing ul.ml" "css_element"
|
||||
And category in management listing should be visible "CAT1"
|
||||
And I click to expand category "CAT1" in the management interface
|
||||
# AJAX loads sub category.
|
||||
And a new page should not have loaded since I started watching
|
||||
And category in management listing should be visible "CAT1"
|
||||
And category in management listing should be visible "CAT2"
|
||||
And I toggle visibility of category "CAT2" in management listing
|
||||
# AJAX hides the subcategory.
|
||||
And a new page should not have loaded since I started watching
|
||||
And category in management listing should be visible "CAT1"
|
||||
And category in management listing should be dimmed "CAT2"
|
||||
And I toggle visibility of category "CAT2" in management listing
|
||||
# AJAX reveals the subcategory.
|
||||
And a new page should not have loaded since I started watching
|
||||
And category in management listing should be visible "CAT1"
|
||||
And category in management listing should be visible "CAT2"
|
||||
|
||||
@@ -199,9 +201,11 @@ Feature: We can change the visibility of categories in the management interface.
|
||||
|
||||
And I log in as "admin"
|
||||
And I go to the courses management page
|
||||
And I start watching to see if a new page loads
|
||||
And I should see the "Course categories and courses" management page
|
||||
And I click on category "Cat 1" in the management interface
|
||||
# Redirect.
|
||||
And a new page should have loaded since I started watching
|
||||
And I start watching to see if a new page loads
|
||||
And I should see the "Course categories and courses" management page
|
||||
And I should see "Cat 1" in the "#category-listing ul.ml" "css_element"
|
||||
And I should see "Cat 2" in the "#category-listing ul.ml" "css_element"
|
||||
@@ -218,13 +222,13 @@ Feature: We can change the visibility of categories in the management interface.
|
||||
And course in management listing should be visible "C2"
|
||||
And course in management listing should be visible "C3"
|
||||
And I toggle visibility of course "C2" in management listing
|
||||
# AJAX action - no redirect.
|
||||
And a new page should not have loaded since I started watching
|
||||
And I should see "Cat 3" in the "#category-listing ul.ml" "css_element"
|
||||
And course in management listing should be visible "C1"
|
||||
And course in management listing should be dimmed "C2"
|
||||
And course in management listing should be visible "C3"
|
||||
And I toggle visibility of category "CAT3" in management listing
|
||||
# AJAX action - no redirect.
|
||||
And a new page should not have loaded since I started watching
|
||||
And category in management listing should be visible "CAT1"
|
||||
And category in management listing should be visible "CAT2"
|
||||
And category in management listing should be dimmed "CAT3"
|
||||
@@ -233,7 +237,7 @@ Feature: We can change the visibility of categories in the management interface.
|
||||
And course in management listing should be dimmed "C2"
|
||||
And course in management listing should be visible "C3"
|
||||
And I toggle visibility of category "CAT1" in management listing
|
||||
# AJAX action - no redirect.
|
||||
And a new page should not have loaded since I started watching
|
||||
And category in management listing should be dimmed "CAT1"
|
||||
And category in management listing should be visible "CAT2"
|
||||
And category in management listing should be dimmed "CAT3"
|
||||
@@ -242,7 +246,7 @@ Feature: We can change the visibility of categories in the management interface.
|
||||
And course in management listing should be dimmed "C2"
|
||||
And course in management listing should be dimmed "C3"
|
||||
And I toggle visibility of category "CAT1" in management listing
|
||||
# AJAX action - no redirect.
|
||||
And a new page should not have loaded since I started watching
|
||||
And category in management listing should be visible "CAT1"
|
||||
And category in management listing should be visible "CAT2"
|
||||
And category in management listing should be dimmed "CAT3"
|
||||
@@ -264,46 +268,50 @@ Feature: We can change the visibility of categories in the management interface.
|
||||
|
||||
And I log in as "admin"
|
||||
And I go to the courses management page
|
||||
And I start watching to see if a new page loads
|
||||
And I should see the "Course categories and courses" management page
|
||||
And I click on category "Cat 1" in the management interface
|
||||
# Redirect
|
||||
And a new page should have loaded since I started watching
|
||||
And I start watching to see if a new page loads
|
||||
And I should see the "Course categories and courses" management page
|
||||
And I click on category "Cat 2" in the management interface
|
||||
# Redirect
|
||||
And a new page should have loaded since I started watching
|
||||
And I start watching to see if a new page loads
|
||||
And I should see the "Course categories and courses" management page
|
||||
And I click on category "Cat 3" in the management interface
|
||||
# Redirect
|
||||
And a new page should have loaded since I started watching
|
||||
And I start watching to see if a new page loads
|
||||
And I should see the "Course categories and courses" management page
|
||||
And category in management listing should be visible "CAT1"
|
||||
And category in management listing should be visible "CAT2"
|
||||
And category in management listing should be visible "CAT3"
|
||||
And course in management listing should be visible "C1"
|
||||
And I toggle visibility of category "CAT1" in management listing
|
||||
# AJAX action - no redirect.
|
||||
And a new page should not have loaded since I started watching
|
||||
And category in management listing should be dimmed "CAT1"
|
||||
And category in management listing should be dimmed "CAT2"
|
||||
And category in management listing should be dimmed "CAT3"
|
||||
And course in management listing should be dimmed "C1"
|
||||
And I toggle visibility of category "CAT1" in management listing
|
||||
# AJAX action - no redirect.
|
||||
And a new page should not have loaded since I started watching
|
||||
And category in management listing should be visible "CAT1"
|
||||
And category in management listing should be visible "CAT2"
|
||||
And category in management listing should be visible "CAT3"
|
||||
And course in management listing should be visible "C1"
|
||||
And I toggle visibility of course "C1" in management listing
|
||||
# AJAX action - no redirect.
|
||||
And a new page should not have loaded since I started watching
|
||||
And category in management listing should be visible "CAT1"
|
||||
And category in management listing should be visible "CAT2"
|
||||
And category in management listing should be visible "CAT3"
|
||||
And course in management listing should be dimmed "C1"
|
||||
And I toggle visibility of category "CAT1" in management listing
|
||||
# AJAX action - no redirect.
|
||||
And a new page should not have loaded since I started watching
|
||||
And category in management listing should be dimmed "CAT1"
|
||||
And category in management listing should be dimmed "CAT2"
|
||||
And category in management listing should be dimmed "CAT3"
|
||||
And course in management listing should be dimmed "C1"
|
||||
And I toggle visibility of category "CAT1" in management listing
|
||||
# AJAX action - no redirect.
|
||||
And a new page should not have loaded since I started watching
|
||||
And category in management listing should be visible "CAT1"
|
||||
And category in management listing should be visible "CAT2"
|
||||
And category in management listing should be visible "CAT3"
|
||||
|
||||
@@ -97,12 +97,15 @@ Feature: Test we can resort categories in the management interface.
|
||||
|
||||
And I log in as "admin"
|
||||
And I go to the courses management page
|
||||
And I start watching to see if a new page loads
|
||||
And I should see the "Course categories and courses" management page
|
||||
And I click on "Master cat" category in the management category listing
|
||||
# Redirect.
|
||||
And a new page should have loaded since I started watching
|
||||
And I start watching to see if a new page loads
|
||||
And I should see the "Course categories and courses" management page
|
||||
And I click on <sortby> action for "Master cat" in management category listing
|
||||
# Redirect.
|
||||
And a new page should have loaded since I started watching
|
||||
And I start watching to see if a new page loads
|
||||
And I should see the "Course categories and courses" management page
|
||||
And I should see category listing <cat1> before <cat2>
|
||||
And I should see category listing <cat2> before <cat3>
|
||||
@@ -186,22 +189,24 @@ Feature: Test we can resort categories in the management interface.
|
||||
|
||||
And I log in as "admin"
|
||||
And I go to the courses management page
|
||||
And I start watching to see if a new page loads
|
||||
And I should see the "Course categories and courses" management page
|
||||
And I click on category "Cat 1" in the management interface
|
||||
# Redirect.
|
||||
And a new page should have loaded since I started watching
|
||||
And I start watching to see if a new page loads
|
||||
And I should see the "Course categories and courses" management page
|
||||
And I should see category listing "Cat 1" before "Cat 1a"
|
||||
And I should see category listing "Cat 1a" before "Cat 1b"
|
||||
And I should see category listing "Cat 1b" before "Cat 1c"
|
||||
And I should see category listing "Cat 1c" before "Cat 2"
|
||||
And I click to move category "CATA" down one
|
||||
# AJAX request. No redirect.We should a 1, 1b, 1a, 1c, 2.
|
||||
And a new page should not have loaded since I started watching
|
||||
And I should see category listing "Cat 1" before "Cat 1b"
|
||||
And I should see category listing "Cat 1b" before "Cat 1a"
|
||||
And I should see category listing "Cat 1a" before "Cat 1c"
|
||||
And I should see category listing "Cat 1c" before "Cat 2"
|
||||
And I click to move category "CATC" up one
|
||||
# AJAX request. No redirect. We should a 1, 1b, 1c, 1a, 2.
|
||||
And a new page should not have loaded since I started watching
|
||||
And I should see category listing "Cat 1" before "Cat 1b"
|
||||
And I should see category listing "Cat 1b" before "Cat 1c"
|
||||
And I should see category listing "Cat 1c" before "Cat 1a"
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
@core @core_course
|
||||
Feature: Role assignments can be made at the category level
|
||||
In order to grant a user different capabilities
|
||||
As a user
|
||||
I can assign roles in categories
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname |
|
||||
| manager | Manager | Manager |
|
||||
And the following "categories" exist:
|
||||
| name | category | idnumber |
|
||||
| Cat 1 | 0 | CAT1 |
|
||||
And the following "role assigns" exist:
|
||||
| user | role | contextlevel | reference |
|
||||
| manager | manager | Category | CAT1 |
|
||||
And I log in as "admin"
|
||||
|
||||
|
||||
@javascript
|
||||
Scenario: A user with a category role can assign roles
|
||||
Given I define the allowed role assignments for the "Manager" role as:
|
||||
| Teacher | Assignable |
|
||||
And I log out
|
||||
And I log in as "manager"
|
||||
And I follow "Courses"
|
||||
When I follow "Cat 1"
|
||||
Then I should see "Assign roles"
|
||||
|
||||
@javascript
|
||||
Scenario: A user with a category role cannot assign roles if there are no roles to assign
|
||||
Given I define the allowed role assignments for the "Manager" role as:
|
||||
| Manager | Not assignable |
|
||||
| Course creator | Not assignable |
|
||||
| Teacher | Not assignable |
|
||||
| Non-editing teacher | Not assignable |
|
||||
| Student | Not assignable |
|
||||
And I log out
|
||||
And I log in as "manager"
|
||||
And I follow "Courses"
|
||||
When I follow "Cat 1"
|
||||
Then I should not see "Assign roles"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user