Compare commits
722 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aca9cdc61f | |||
| bbaf0525f4 | |||
| 7a37b0a5a4 | |||
| 2dcd7f8a55 | |||
| 7edb20cadd | |||
| 3d2783690d | |||
| 3681c247f5 | |||
| 7013fe50b5 | |||
| d3c2514a2c | |||
| 808954253e | |||
| be5d94c454 | |||
| d75d4949ef | |||
| 88180f1e0c | |||
| b534e3ff7b | |||
| c7d887dae0 | |||
| d2df0daf5b | |||
| 9c7f64b79d | |||
| 2cff96a309 | |||
| c01ffc64eb | |||
| e3f586e627 | |||
| 6f41a2c318 | |||
| 786fe59949 | |||
| 53c902867f | |||
| 7467757700 | |||
| ee5065676d | |||
| 04919a422b | |||
| 2b459ffe02 | |||
| da70ec0cca | |||
| 6ce7c17be9 | |||
| f058de06a3 | |||
| f4f0f30630 | |||
| 2f982389d4 | |||
| 41790d2e48 | |||
| 52b50073fc | |||
| 3598b0e3bd | |||
| f2391ae1c1 | |||
| f2eeb4ede6 | |||
| e3602a5a0a | |||
| 513e8044c3 | |||
| 6776687817 | |||
| f558157719 | |||
| e07f6c0528 | |||
| ba532b0010 | |||
| 9dfe3e0be1 | |||
| f27e3d0bde | |||
| f1ad334f3a | |||
| 8ee12efa22 | |||
| 1fe370973c | |||
| d871d35b90 | |||
| 066b56539e | |||
| bdc0d9f924 | |||
| 785c9e3f70 | |||
| b45c238223 | |||
| a5f11a79a2 | |||
| 22b39b4c8c | |||
| de284c4cd4 | |||
| fc5853c205 | |||
| 110828ee5c | |||
| 97a9e8c3c4 | |||
| da1eeea0ff | |||
| 1b87b66946 | |||
| 7db66a5fe2 | |||
| 8c04f87af5 | |||
| 6a4d4f2ba4 | |||
| 845266a33d | |||
| a9ec123f52 | |||
| 1cef9981fb | |||
| 81cbe8e8a4 | |||
| c428bb3c0b | |||
| 75b395ee62 | |||
| 9637705b0c | |||
| bae234d9ff | |||
| a27fbd640d | |||
| 83cf77b622 | |||
| 88b95638fb | |||
| dc855f283b | |||
| a0c850628c | |||
| 0edc5fe898 | |||
| ef65abdcbd | |||
| 931f732a8c | |||
| 318ab14384 | |||
| c5691ceee3 | |||
| 910ceebd70 | |||
| 38317c9050 | |||
| bd956b6ce2 | |||
| b19d180a86 | |||
| 93b943b322 | |||
| 8e3b262bb7 | |||
| f0b9026133 | |||
| b779dc805e | |||
| e74867d06c | |||
| 41feb40e7e | |||
| 7696a8d9e3 | |||
| c44b6e79fa | |||
| 81d5b794d0 | |||
| 8137f98b1c | |||
| 9103f6ca8a | |||
| e6973df683 | |||
| 7d7ca07870 | |||
| 8398fbe8b6 | |||
| 7c362df750 | |||
| 550b956e23 | |||
| 59dbf60906 | |||
| f6f7a788ac | |||
| b6c0d8fc2a | |||
| b7bbe9fd94 | |||
| f6da281a83 | |||
| f72830b4f0 | |||
| ecd1bce400 | |||
| 6c0e979eda | |||
| 3ff27109ec | |||
| f06f4603b8 | |||
| 7bed262b8f | |||
| e9ba7022fc | |||
| 60aab30175 | |||
| 85e9ee2175 | |||
| 10426ccf9d | |||
| babe95c9dc | |||
| 9a15bdc97f | |||
| 86f0bb4591 | |||
| b0d7351cff | |||
| ad5761237a | |||
| d98544a729 | |||
| 1dd71c502a | |||
| fae9236939 | |||
| 36a7b9f234 | |||
| 878d69b794 | |||
| 2032491730 | |||
| 7b2fc2b81e | |||
| c8fe4d8b88 | |||
| 567c0bb51d | |||
| fe8a827cb0 | |||
| e25e165231 | |||
| fdcf7fb3f6 | |||
| 88bd118665 | |||
| b061fba2c4 | |||
| 7f5f07b68e | |||
| 6843744660 | |||
| fe82348810 | |||
| cfefd25d6c | |||
| 956166b766 | |||
| c692bb567f | |||
| 70a7b23df5 | |||
| 42c5d3aa9d | |||
| f0a15feedb | |||
| 2078761831 | |||
| 6fb9c39256 | |||
| 8103058154 | |||
| f9000970ca | |||
| 2412171944 | |||
| ea12e51d4d | |||
| 40b1f7f86d | |||
| e052fe614f | |||
| 52c730014e | |||
| 787727c745 | |||
| 5e99464c96 | |||
| 46ecac536c | |||
| b4e05280c1 | |||
| 572838664d | |||
| 2090c294bc | |||
| a9c4c8da11 | |||
| 499c8d894d | |||
| 16ab239fb4 | |||
| 4940aa5152 | |||
| 3e130de203 | |||
| f41a173317 | |||
| dc7420d411 | |||
| 96e40b6cfc | |||
| a1eb47211d | |||
| f5f0074aee | |||
| ea8f903568 | |||
| 51a43f5fa7 | |||
| 2edcf6861a | |||
| bd5779cfb5 | |||
| 5a13113b07 | |||
| 6ac052dbc4 | |||
| c8c5e35bb1 | |||
| 28964ed251 | |||
| e362c66d49 | |||
| 977861a47b | |||
| b8067e0b01 | |||
| cf877600b7 | |||
| d1660a6aae | |||
| e0b391da98 | |||
| 687aad78bd | |||
| 21144b8419 | |||
| 0372a0accb | |||
| fea4000d5e | |||
| 7105cb7684 | |||
| d868aafa8e | |||
| 54bb01439e | |||
| 0b42937599 | |||
| fab3e4fa3d | |||
| 2393486db4 | |||
| 853fbda447 | |||
| 007edbdecb | |||
| c614487d2d | |||
| 58643c8b11 | |||
| 9cdf50cb79 | |||
| 03bfea8134 | |||
| 9962df9eb3 | |||
| 6f845655e5 | |||
| 1ff9dccd8d | |||
| 5fd13b5970 | |||
| f79a3236ea | |||
| 25fd679aaf | |||
| 93cca9afbe | |||
| 26663756fb | |||
| e404606458 | |||
| 68b925e274 | |||
| 0a8190569f | |||
| d95fb5245b | |||
| fcea6de794 | |||
| e5b89df732 | |||
| 67ea841f41 | |||
| a64872eb59 | |||
| 5d20a3ed14 | |||
| 39aacd1dfe | |||
| bc312c648a | |||
| b545f830d0 | |||
| 5325b4efe1 | |||
| c6d38b89dc | |||
| 73e0775d26 | |||
| e4eb50ae15 | |||
| 34e60f0dbf | |||
| 5f2a1f0c36 | |||
| 1357976049 | |||
| 9f7af133fd | |||
| 0faa1175fe | |||
| f3fe4f52e1 | |||
| 45a405014e | |||
| b77e45119c | |||
| 9a3855aa00 | |||
| f06abbd040 | |||
| 9e1375fac0 | |||
| 62be5f6154 | |||
| 5db085f0a7 | |||
| 734fb0f015 | |||
| 0fd90e2044 | |||
| ab4afd711a | |||
| 4fc794b7ef | |||
| f883e31728 | |||
| c2e08a97c1 | |||
| cd7cd469ae | |||
| 43d1a8695c | |||
| 499e619868 | |||
| 914ce2602e | |||
| 43663a70af | |||
| c072f462c6 | |||
| 99b4c8bb10 | |||
| 6e1f0e9527 | |||
| bd83aa0cd6 | |||
| c7a89d2009 | |||
| 548c5fa21e | |||
| 106fa63926 | |||
| 5176a91648 | |||
| 540a38d586 | |||
| 47f81ef27f | |||
| d4fa90b0e9 | |||
| 033429a51c | |||
| 3c8e25328c | |||
| bd08b92fcb | |||
| 13add2b85c | |||
| e42fa42955 | |||
| bbbaa37cc5 | |||
| 7c26a6bd77 | |||
| ebbf9851c8 | |||
| 65cd459455 | |||
| df91963a70 | |||
| eb58b204ff | |||
| 11106c1bcc | |||
| 6e76ce1689 | |||
| bbe6b470aa | |||
| 6d0649e537 | |||
| b36b735a3b | |||
| 1df212a569 | |||
| 843fcc5fac | |||
| 4d3595e7af | |||
| c68568541f | |||
| dde166ba07 | |||
| 542536418d | |||
| 603ba08d27 | |||
| 0dcab26cd9 | |||
| 26c501f37e | |||
| 545174182a | |||
| 5166e06e08 | |||
| e7e9d4c0b5 | |||
| 2786be9622 | |||
| 27442e8c01 | |||
| 5e2cd4637e | |||
| 2588195fc0 | |||
| 315214fd80 | |||
| 04f5ebb886 | |||
| 08ff55dad7 | |||
| 5c93b9a5ce | |||
| 4f14f2f491 | |||
| 24db354737 | |||
| c0e9e7e0a0 | |||
| 900f974216 | |||
| 61f2bbbf36 | |||
| e5bec10f56 | |||
| 9005f18486 | |||
| 29df554433 | |||
| b31860af40 | |||
| 605cba073c | |||
| 46573be04b | |||
| 6adcb47532 | |||
| 0fc3aba77e | |||
| 29c2971468 | |||
| 1536b56796 | |||
| 26713f79f4 | |||
| 1769ede142 | |||
| e411b59f80 | |||
| 21b902996a | |||
| 9c68807f6c | |||
| c06dc57666 | |||
| e707c69cce | |||
| d39fbc3e06 | |||
| 1d2368318a | |||
| 7dfa47b2dd | |||
| 0c61a163ae | |||
| 0af674082c | |||
| 927a6221aa | |||
| e63627f50e | |||
| 77be0785fd | |||
| 9427e333c7 | |||
| 506fd9545c | |||
| a7ce01cc70 | |||
| de9af122db | |||
| 9b04032e45 | |||
| e42f41b81a | |||
| 5b1b83101e | |||
| 50e53bb6ea | |||
| e025fd2123 | |||
| 4b7b979d39 | |||
| e2180dbc89 | |||
| 7b4ecf6fe2 | |||
| 51134125fc | |||
| 4268f10674 | |||
| 6cbdb4c31c | |||
| 2bbd04781c | |||
| 454d532acb | |||
| b30c0b6b68 | |||
| afb659ff15 | |||
| a7457acc52 | |||
| 34ed4e0e19 | |||
| c7c84089f1 | |||
| 3b9df47dfe | |||
| 79f0d69bca | |||
| 89cd3cfc02 | |||
| b57860bfbb | |||
| 740917fa43 | |||
| 9c5c5e2ee6 | |||
| e88bef20f5 | |||
| 6785483b81 | |||
| 0923ed6bb8 | |||
| 42ee071ab5 | |||
| a22951044f | |||
| bd34549316 | |||
| 3bef945fbe | |||
| 4bd3c5766e | |||
| c6fa627a23 | |||
| 79d8b6f1bd | |||
| f5850997b4 | |||
| df2b12bf1b | |||
| e8d61c6f11 | |||
| 4a1f3cf807 | |||
| c075a898e1 | |||
| f181e3cf70 | |||
| 6ef412801f | |||
| baf42679ab | |||
| 2c0857ba05 | |||
| 781f643d95 | |||
| 520a528943 | |||
| 7e95809a37 | |||
| a3309be840 | |||
| 637b3ff3de | |||
| 22983bd4da | |||
| 3422a9148b | |||
| 231a80c010 | |||
| a6c36884b3 | |||
| 81292696c8 | |||
| 89e5f43109 | |||
| f6ae580fbb | |||
| d8928b4535 | |||
| 23ba40cf63 | |||
| 09d7121146 | |||
| db370772bd | |||
| 47abeb18f6 | |||
| 8890feae27 | |||
| e1fffdbc27 | |||
| f654011e0d | |||
| 9de798de71 | |||
| 4d6d096336 | |||
| 65704ab9b3 | |||
| f3f513ec7b | |||
| f14510d944 | |||
| 4d19341a0a | |||
| 6df5fb575c | |||
| 1eee389afe | |||
| c2a65ab688 | |||
| e02ebdc5fe | |||
| b702e5b44e | |||
| b77027c57d | |||
| 5acfcf8777 | |||
| 201812e592 | |||
| 87562dbe24 | |||
| e5a5962d00 | |||
| e9569a7fa3 | |||
| 76e50ea9e7 | |||
| 8b196aa53e | |||
| f0be1f90da | |||
| 42f9eb4c56 | |||
| d3c4f13490 | |||
| e8e2c512b8 | |||
| 31d8260331 | |||
| 6fd68f6967 | |||
| 11f3ea4f94 | |||
| 07e95f49fc | |||
| 0f7ed737fc | |||
| f821c0a5e2 | |||
| 2321c81d52 | |||
| 94f61491de | |||
| 913493048b | |||
| 8d82a20f4b | |||
| 1ea218b63e | |||
| 75728ebe8d | |||
| af6a891144 | |||
| 2eda090ebe | |||
| a239fdd2a0 | |||
| 5f368c5dd7 | |||
| 37e25a19a8 | |||
| 7a27817f98 | |||
| 6b00083d7f | |||
| 5dc2aeda9a | |||
| 75cb3afa71 | |||
| 4b98e2986c | |||
| d77585c672 | |||
| dd82501911 | |||
| acdf91203c | |||
| 3f48012eb5 | |||
| 31867bb1b4 | |||
| 97baaf9a53 | |||
| becf6318c2 | |||
| c7806282a8 | |||
| 2f8db6a31e | |||
| c26566f21b | |||
| 0ae9094a96 | |||
| 1a1cc84af1 | |||
| 10fed49ab2 | |||
| e57beee8dc | |||
| 2be51b7633 | |||
| 48d02c4437 | |||
| b72615601b | |||
| ac5f2bc552 | |||
| f569989db3 | |||
| af84c619a0 | |||
| ce51586b24 | |||
| b9866e9e71 | |||
| 45a4ebb697 | |||
| 9574aee37c | |||
| 229ffe02dc | |||
| 52f391ceab | |||
| f494350d40 | |||
| ef79f05067 | |||
| 3fc319c0c4 | |||
| 57d9979b43 | |||
| 3cd85bbfae | |||
| 354d0f5e84 | |||
| 42306af15e | |||
| 9ffd2349a2 | |||
| 5efec4d5a3 | |||
| 00a142859b | |||
| 17b7ca5b2b | |||
| e5edbe8ccd | |||
| 258db34bfc | |||
| c3c88e8673 | |||
| be12bb8ab8 | |||
| 8da9781a25 | |||
| 6f3be9d5ce | |||
| 045a69fd3e | |||
| 7bfa58873a | |||
| 889a985b62 | |||
| 5f90019098 | |||
| c5ed804680 | |||
| 72536bd5a8 | |||
| 94ae0b7f7b | |||
| 5d8822ff11 | |||
| ea8ce0c362 | |||
| afbb1b7fb5 | |||
| cd099d041f | |||
| 6e0b2cde30 | |||
| 2c60b80795 | |||
| 6e0c45eab6 | |||
| 71a3196ddf | |||
| 9ec7dbc419 | |||
| b147cea65f | |||
| 0c9a5310f9 | |||
| 67f8fc9419 | |||
| aec0f344de | |||
| baad350a71 | |||
| 3a9570a37a | |||
| 3203027f54 | |||
| f96f84cb19 | |||
| f285d9203f | |||
| 6766dabe82 | |||
| d56e2cd3eb | |||
| 9b7f4e9e8e | |||
| 7b3608625e | |||
| 0a76cd2ca1 | |||
| 36706f5b98 | |||
| ff2b56b538 | |||
| b6a8a31088 | |||
| 3fe3805d79 | |||
| 3ce9798faf | |||
| e0a41273a6 | |||
| 238b87a2fb | |||
| 7ea1dd1580 | |||
| 1a13098682 | |||
| ed961702c6 | |||
| 17da61847f | |||
| 6805a9e11b | |||
| fe785db8db | |||
| b6500c619a | |||
| 62fbaeecbb | |||
| 80e10c2278 | |||
| dcbcc94a51 | |||
| 0ce3aa0562 | |||
| 47dc780e98 | |||
| 471a306b44 | |||
| e52a7a4fe0 | |||
| 5e1a2d2a42 | |||
| 20c506c970 | |||
| c8e6ceb4c4 | |||
| 967451e8ed | |||
| 6098e65822 | |||
| fb1fcfb4f3 | |||
| 39e019735f | |||
| b95cb5e0e5 | |||
| 44b6b2318f | |||
| c89650cd42 | |||
| 3d062e2cdf | |||
| f9465c0974 | |||
| ced9b94a04 | |||
| 55bf30c23c | |||
| c91507f8d5 | |||
| c8d7affbb5 | |||
| 188892a7a8 | |||
| 12ec6228a5 | |||
| b7265c5a4f | |||
| bdff8c070f | |||
| 3b8f30b128 | |||
| f76f70f1b3 | |||
| 7b14fef527 | |||
| cf350bc4f4 | |||
| 99105dabb5 | |||
| 9634c940d4 | |||
| 330f27b3fd | |||
| 48f3c5744d | |||
| 8b913c588a | |||
| 3bf7fdb1e8 | |||
| 6a85db36f5 | |||
| ac4c7623fe | |||
| d0f5f3ad11 | |||
| 8a63956d8d | |||
| 957f81aaa6 | |||
| 9a910e7e8c | |||
| 2135c7e9ed | |||
| 87cafedcc0 | |||
| 5911ac9525 | |||
| 5a6e167a0b | |||
| 771f6902df | |||
| 1626dc7c1f | |||
| 4eb15e3225 | |||
| 1d9bd31e42 | |||
| 4d2bd9a054 | |||
| 34502102b4 | |||
| 95cd5edfab | |||
| c36e94ad98 | |||
| 8225414388 | |||
| 71f3a6c9c0 | |||
| c1ee0772ff | |||
| f5b9fddac4 | |||
| ea76a7cde3 | |||
| 8e99e8c014 | |||
| 131e69e29b | |||
| 9c6cc265da | |||
| 0767770d95 | |||
| 79fcb7446a | |||
| 91f22e1486 | |||
| b2a8d8af06 | |||
| b34f0b369f | |||
| 8163446fee | |||
| a331f6b0d8 | |||
| b3094c4751 | |||
| 2d47619503 | |||
| 1494620bfb | |||
| f1fd291473 | |||
| b20d56c357 | |||
| 9e49655f16 | |||
| 805a4017ab | |||
| 0e69daef28 | |||
| c704f0da1a | |||
| f15c70ab7f | |||
| 26ef0c5c79 | |||
| 7f5df63d6e | |||
| ae1cb40c5e | |||
| 609d5f1826 | |||
| 70b465a75c | |||
| 341af0a779 | |||
| 1e2f25c79c | |||
| 95004c72af | |||
| da83f4d2fc | |||
| 5799fb8640 | |||
| e1c3a4d24f | |||
| c20f9c6678 | |||
| 556ca7fa16 | |||
| 7dfbe69352 | |||
| bc091a045b | |||
| 257ef7c065 | |||
| d8b7b023a6 | |||
| 622a713fa1 | |||
| 7b48139c7a | |||
| 5ef761f4d8 | |||
| 4f865489f0 | |||
| 475a266e31 | |||
| d77412d35f | |||
| 5fee906a30 | |||
| a04076a1f2 | |||
| d0ed5742d0 | |||
| 2ba930d7bb | |||
| 650197e4dc | |||
| 53b48f0116 | |||
| 21f77735fc | |||
| 049838f998 | |||
| 6df1f24a6b | |||
| 1f47d41b03 | |||
| 903ef33978 | |||
| 7d2477a859 | |||
| 054a23264c | |||
| 9e50e8c386 | |||
| 1a52d63ea9 | |||
| 78d40d3e6d | |||
| afaeae1796 | |||
| f518505d71 | |||
| b5c0146072 | |||
| d5b78c7c81 | |||
| 99e0439396 | |||
| 9660b5f471 | |||
| 742671e7c9 | |||
| 81e812c697 | |||
| b984606f45 | |||
| 072a971908 | |||
| 829e7ae8a3 | |||
| df222f7338 | |||
| 8a0eb277db | |||
| 836747b10a | |||
| b638f51bd0 | |||
| 371f4c2e72 | |||
| 787e897899 | |||
| c341626927 | |||
| 86ef5ce6be | |||
| eee6c61ea6 | |||
| 79441b56ba | |||
| c4b9228d18 | |||
| 571f5aba3e | |||
| 8a954a1571 | |||
| 4e9072d20b | |||
| f49ff4b736 | |||
| 52c14af205 | |||
| 087e52eccf | |||
| 26c76fcc0b | |||
| 544a47fc07 | |||
| dfbbb39a25 | |||
| c52766af64 | |||
| 4af0bb82e1 | |||
| c2a9f46692 | |||
| 3398ecbfc2 | |||
| 3ce28cce2e | |||
| 54cbf4279a | |||
| e611797d37 | |||
| db27b57aa5 | |||
| 8d270f56e2 | |||
| 1e25d9306d | |||
| b3b8d045b7 | |||
| ab1c2037fc | |||
| 29aafc0ec7 | |||
| 03ed7684d4 | |||
| 8d1bf87c0e | |||
| ec4e2838cd | |||
| d3bfb8ceac | |||
| 10ce06e159 | |||
| 23cf2f6db3 | |||
| d3a22bc5a8 | |||
| 18937c1577 | |||
| 93c8f9347e | |||
| 26c09bc165 | |||
| 35ce557a91 | |||
| bdccf17907 | |||
| 91557e03ee | |||
| 5b5424a8ee | |||
| 3a5de34b03 | |||
| 6608a30b15 | |||
| bb21b0ad57 | |||
| 8e2349f196 | |||
| c4f051e2e9 | |||
| 691868bdaa | |||
| bc1643135c | |||
| 1910eaf251 | |||
| e3d53b99aa | |||
| d05d5cd4c7 | |||
| 1790647ff9 | |||
| fe51a68590 | |||
| 62ca5c4b91 | |||
| 33952ce027 | |||
| 893ed748bc | |||
| 13c9addc7d | |||
| 0d1c739aeb | |||
| 3ef9d54387 | |||
| 29c62b300c | |||
| b7afb51132 | |||
| d346c55175 |
+6
-5
@@ -45,12 +45,9 @@ matrix:
|
||||
fast_finish: true
|
||||
|
||||
include:
|
||||
# Run grunt/npm install on lowest supported npm version
|
||||
- php: 7
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='0.10'
|
||||
# Run grunt/npm install on highest version ('node' is an alias for the latest node.js version.)
|
||||
- php: 7
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='node'
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='lts/carbon'
|
||||
|
||||
exclude:
|
||||
# MySQL - it's just too slow.
|
||||
@@ -137,11 +134,13 @@ before_script:
|
||||
sed -i \
|
||||
-e "s%= 'pgsql'%= 'mysqli'%" \
|
||||
-e "s%= 'username'%= 'travis'%" \
|
||||
-e "s%=> 'utf8mb4_unicode_ci'%=> 'utf8mb4_bin'%" \
|
||||
config.php;
|
||||
|
||||
mysql -u root -e 'SET GLOBAL innodb_file_format=barracuda;' ;
|
||||
mysql -u root -e 'SET GLOBAL innodb_file_per_table=ON;' ;
|
||||
mysql -e 'CREATE DATABASE travis_ci_test DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_bin;' ;
|
||||
mysql -u root -e 'SET GLOBAL innodb_large_prefix=ON;' ;
|
||||
mysql -e 'CREATE DATABASE travis_ci_test DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_bin;' ;
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -240,7 +239,9 @@ script:
|
||||
grunt ;
|
||||
# Add all files to the git index and then run diff --cached to see all changes.
|
||||
# This ensures that we get the status of all files, including new files.
|
||||
# We ignore npm-shrinkwrap.json to make the tasks immune to npm changes.
|
||||
git add . ;
|
||||
git reset -- npm-shrinkwrap.json ;
|
||||
git diff --cached --exit-code ;
|
||||
fi
|
||||
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ if ($PAGE->user_allowed_editing()) {
|
||||
$savebutton = false;
|
||||
$outputhtml = '';
|
||||
foreach ($settingspage->children as $childpage) {
|
||||
if ($childpage->is_hidden()) {
|
||||
if ($childpage->is_hidden() || !$childpage->check_access()) {
|
||||
continue;
|
||||
}
|
||||
if ($childpage instanceof admin_externalpage) {
|
||||
|
||||
@@ -64,6 +64,71 @@ if (!empty($options['collation'])) {
|
||||
cli_error("Error: collation '$collation' is not available on this server!");
|
||||
}
|
||||
|
||||
$collationinfo = explode('_', $collation);
|
||||
$charset = reset($collationinfo);
|
||||
|
||||
$engine = strtolower($DB->get_dbengine());
|
||||
|
||||
// Do checks for utf8mb4.
|
||||
if (strpos($collation, 'utf8mb4') === 0) {
|
||||
// Do we have the right engine?
|
||||
if ($engine !== 'innodb' && $engine !== 'xtradb') {
|
||||
cli_error("Error: '$collation' requires InnoDB or XtraDB set as the engine.");
|
||||
}
|
||||
// Are we using Barracuda?
|
||||
if ($DB->get_row_format() != 'Barracuda') {
|
||||
// Try setting it here.
|
||||
try {
|
||||
$DB->execute("SET GLOBAL innodb_file_format=Barracuda");
|
||||
} catch (dml_exception $e) {
|
||||
cli_error("Error: '$collation' requires the file format to be set to Barracuda.
|
||||
An attempt was made to change the format, but it failed. Please try doing this manually.");
|
||||
}
|
||||
echo "GLOBAL SETTING: innodb_file_format changed to Barracuda\n";
|
||||
}
|
||||
// Is one file per table being used?
|
||||
if (!$DB->is_file_per_table_enabled()) {
|
||||
try {
|
||||
$DB->execute("SET GLOBAL innodb_file_per_table=1");
|
||||
} catch (dml_exception $e) {
|
||||
cli_error("Error: '$collation' requires the setting 'innodb_file_per_table' be set to 'ON'.
|
||||
An attempt was made to change the format, but it failed. Please try doing this manually.");
|
||||
}
|
||||
echo "GLOBAL SETTING: innodb_file_per_table changed to 1\n";
|
||||
}
|
||||
// Is large prefix set?
|
||||
if (!$DB->is_large_prefix_enabled()) {
|
||||
try {
|
||||
$DB->execute("SET GLOBAL innodb_large_prefix=1");
|
||||
} catch (dml_exception $e) {
|
||||
cli_error("Error: '$collation' requires the setting 'innodb_large_prefix' be set to 'ON'.
|
||||
An attempt was made to change the format, but it failed. Please try doing this manually.");
|
||||
}
|
||||
echo "GLOBAL SETTING: innodb_large_prefix changed to 1\n";
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "SHOW VARIABLES LIKE 'collation_database'";
|
||||
if (!$dbcollation = $DB->get_record_sql($sql)) {
|
||||
cli_error("Error: Could not access collation information on the database.");
|
||||
}
|
||||
$sql = "SHOW VARIABLES LIKE 'character_set_database'";
|
||||
if (!$dbcharset = $DB->get_record_sql($sql)) {
|
||||
cli_error("Error: Could not access character set information on the database.");
|
||||
}
|
||||
if ($dbcollation->value !== $collation || $dbcharset->value !== $charset) {
|
||||
// Try to convert the DB.
|
||||
echo "Converting database to '$collation' for $CFG->wwwroot:\n";
|
||||
$sql = "ALTER DATABASE $CFG->dbname DEFAULT CHARACTER SET $charset DEFAULT COLLATE = $collation";
|
||||
try {
|
||||
$DB->change_database_structure($sql);
|
||||
} catch (exception $e) {
|
||||
cli_error("Error: Tried to alter the database with no success. Please try manually changing the database
|
||||
to the new collation and character set and then run this script again.");
|
||||
}
|
||||
echo "DATABASE CONVERTED\n";
|
||||
}
|
||||
|
||||
echo "Converting tables and columns to '$collation' for $CFG->wwwroot:\n";
|
||||
$prefix = $DB->get_prefix();
|
||||
$prefix = str_replace('_', '\\_', $prefix);
|
||||
@@ -80,7 +145,7 @@ if (!empty($options['collation'])) {
|
||||
$skipped++;
|
||||
|
||||
} else {
|
||||
$DB->change_database_structure("ALTER TABLE $table->name DEFAULT COLLATE = $collation");
|
||||
$DB->change_database_structure("ALTER TABLE $table->name DEFAULT CHARACTER SET $charset DEFAULT COLLATE = $collation");
|
||||
echo "CONVERTED\n";
|
||||
$converted++;
|
||||
}
|
||||
@@ -96,18 +161,32 @@ if (!empty($options['collation'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for utf8mb4 collation.
|
||||
$rowformat = $DB->get_row_format_sql($engine, $collation);
|
||||
|
||||
if ($column->type === 'tinytext' or $column->type === 'mediumtext' or $column->type === 'text' or $column->type === 'longtext') {
|
||||
$notnull = ($column->null === 'NO') ? 'NOT NULL' : 'NULL';
|
||||
$default = (!is_null($column->default) and $column->default !== '') ? "DEFAULT '$column->default'" : '';
|
||||
// primary, unique and inc are not supported for texts
|
||||
$sql = "ALTER TABLE $table->name MODIFY COLUMN $column->field $column->type COLLATE $collation $notnull $default";
|
||||
$sql = "ALTER TABLE $table->name
|
||||
MODIFY COLUMN $column->field $column->type
|
||||
CHARACTER SET $charset
|
||||
COLLATE $collation $notnull $default";
|
||||
$DB->change_database_structure($sql);
|
||||
|
||||
} else if (strpos($column->type, 'varchar') === 0) {
|
||||
$notnull = ($column->null === 'NO') ? 'NOT NULL' : 'NULL';
|
||||
$default = !is_null($column->default) ? "DEFAULT '$column->default'" : '';
|
||||
// primary, unique and inc are not supported for texts
|
||||
$sql = "ALTER TABLE $table->name MODIFY COLUMN $column->field $column->type COLLATE $collation $notnull $default";
|
||||
|
||||
if ($rowformat != '') {
|
||||
$sql = "ALTER TABLE $table->name $rowformat";
|
||||
$DB->change_database_structure($sql);
|
||||
}
|
||||
|
||||
$sql = "ALTER TABLE $table->name
|
||||
MODIFY COLUMN $column->field $column->type
|
||||
CHARACTER SET $charset
|
||||
COLLATE $collation $notnull $default";
|
||||
$DB->change_database_structure($sql);
|
||||
} else {
|
||||
echo "ERROR (unknown column type: $column->type)\n";
|
||||
@@ -180,7 +259,9 @@ function mysql_get_collations() {
|
||||
global $DB;
|
||||
|
||||
$collations = array();
|
||||
$sql = "SHOW COLLATION WHERE Collation LIKE 'utf8\_%' AND Charset = 'utf8'";
|
||||
$sql = "SHOW COLLATION
|
||||
WHERE Collation LIKE 'utf8\_%' AND Charset = 'utf8'
|
||||
OR Collation LIKE 'utf8mb4\_%' AND Charset = 'utf8mb4'";
|
||||
$rs = $DB->get_recordset_sql($sql);
|
||||
foreach ($rs as $collation) {
|
||||
$collations[$collation->collation] = $collation->collation;
|
||||
|
||||
@@ -47,6 +47,7 @@ switch ($action) {
|
||||
// remove from enabled list
|
||||
$key = array_search($editor, $active_editors);
|
||||
unset($active_editors[$key]);
|
||||
add_to_config_log('editor_visibility', '1', '0', $editor);
|
||||
break;
|
||||
|
||||
case 'enable':
|
||||
@@ -54,6 +55,7 @@ switch ($action) {
|
||||
if (!in_array($editor, $active_editors)) {
|
||||
$active_editors[] = $editor;
|
||||
$active_editors = array_unique($active_editors);
|
||||
add_to_config_log('editor_visibility', '0', '1', $editor);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -66,6 +68,7 @@ switch ($action) {
|
||||
$fsave = $active_editors[$key];
|
||||
$active_editors[$key] = $active_editors[$key + 1];
|
||||
$active_editors[$key + 1] = $fsave;
|
||||
add_to_config_log('editor_position', $key, $key + 1, $editor);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -79,6 +82,7 @@ switch ($action) {
|
||||
$fsave = $active_editors[$key];
|
||||
$active_editors[$key] = $active_editors[$key - 1];
|
||||
$active_editors[$key - 1] = $fsave;
|
||||
add_to_config_log('editor_position', $key, $key - 1, $editor);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1723,6 +1723,21 @@
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.2" requires="2.7">
|
||||
@@ -1739,6 +1754,7 @@
|
||||
<VENDOR name="oracle" version="10.2" />
|
||||
</DATABASE>
|
||||
<PHP version="5.6.5" level="required">
|
||||
<RESTRICT function="restrict_php_version_72" message="unsupportedphpversion72" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
@@ -1873,6 +1889,914 @@
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.3" requires="2.7">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="5.5.31" />
|
||||
<VENDOR name="mysql" version="5.5.31" />
|
||||
<VENDOR name="postgres" version="9.3" />
|
||||
<VENDOR name="mssql" version="10.0" />
|
||||
<VENDOR name="oracle" version="10.2" />
|
||||
</DATABASE>
|
||||
<PHP version="5.6.5" level="required">
|
||||
<RESTRICT function="restrict_php_version_72" message="unsupportedphpversion72" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="mbstringrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="intlrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.4" requires="3.0">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="5.5.31" />
|
||||
<VENDOR name="mysql" version="5.5.31" />
|
||||
<VENDOR name="postgres" version="9.3" />
|
||||
<VENDOR name="mssql" version="10.0" />
|
||||
<VENDOR name="oracle" version="10.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.0.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_73" message="unsupportedphpversion73" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="mbstringrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.5" requires="3.1">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="5.5.31" />
|
||||
<VENDOR name="mysql" version="5.5.31" />
|
||||
<VENDOR name="postgres" version="9.3" />
|
||||
<VENDOR name="mssql" version="10.0" />
|
||||
<VENDOR name="oracle" version="10.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.0.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_73" message="unsupportedphpversion73" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="mbstringrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.6" requires="3.1">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="5.5.31" />
|
||||
<VENDOR name="mysql" version="5.6" />
|
||||
<VENDOR name="postgres" version="9.4" />
|
||||
<VENDOR name="mssql" version="10.0" />
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.0.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="mbstringrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.7" requires="3.2">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="5.5.31" />
|
||||
<VENDOR name="mysql" version="5.6" />
|
||||
<VENDOR name="postgres" version="9.4" />
|
||||
<VENDOR name="mssql" version="10.0" />
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.1.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="mbstringrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
+8
-1
@@ -852,10 +852,17 @@ $cachewarnings = cache_helper::warnings();
|
||||
// Check if there are events 1 API handlers.
|
||||
$eventshandlers = $DB->get_records_sql('SELECT DISTINCT component FROM {events_handlers}');
|
||||
|
||||
// Check if a directory with development libraries exists.
|
||||
if (is_dir($CFG->dirroot.'/vendor') || is_dir($CFG->dirroot.'/node_modules')) {
|
||||
$devlibdir = true;
|
||||
} else {
|
||||
$devlibdir = false;
|
||||
}
|
||||
|
||||
admin_externalpage_setup('adminnotifications');
|
||||
|
||||
$output = $PAGE->get_renderer('core', 'admin');
|
||||
|
||||
echo $output->admin_notifications_page($maturity, $insecuredataroot, $errorsdisplayed, $cronoverdue, $dbproblems,
|
||||
$maintenancemode, $availableupdates, $availableupdatesfetch, $buggyiconvnomb,
|
||||
$registered, $cachewarnings, $eventshandlers);
|
||||
$registered, $cachewarnings, $eventshandlers, null, $devlibdir);
|
||||
|
||||
@@ -421,12 +421,12 @@ class site_registration_form extends moodleform {
|
||||
$mform->addElement('checkbox', 'badges', '',
|
||||
" " . get_string('badgesnumber', 'hub', $badges));
|
||||
$mform->setDefault('badges', $badgesnumber != -1);
|
||||
$mform->setType('resources', PARAM_INT);
|
||||
$mform->setType('badges', PARAM_INT);
|
||||
|
||||
$mform->addElement('checkbox', 'issuedbadges', '',
|
||||
" " . get_string('issuedbadgesnumber', 'hub', $issuedbadges));
|
||||
$mform->setDefault('issuedbadges', $issuedbadgesnumber != -1);
|
||||
$mform->setType('resources', PARAM_INT);
|
||||
$mform->setType('issuedbadges', PARAM_INT);
|
||||
|
||||
$mform->addElement('checkbox', 'participantnumberaverage', '',
|
||||
" " . get_string('participantnumberaverage', 'hub', $participantnumberaverage));
|
||||
|
||||
+24
-2
@@ -276,12 +276,15 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
* @param int|null $availableupdatesfetch timestamp of the most recent updates fetch or null (unknown)
|
||||
* @param string[] $cachewarnings An array containing warnings from the Cache API.
|
||||
* @param array $eventshandlers Events 1 API handlers.
|
||||
* @param bool $ignored Forward-compatible placeholder for the themedesignermode.
|
||||
* @param bool $devlibdir Warn about development libs directory presence.
|
||||
*
|
||||
* @return string HTML to output.
|
||||
*/
|
||||
public function admin_notifications_page($maturity, $insecuredataroot, $errorsdisplayed,
|
||||
$cronoverdue, $dbproblems, $maintenancemode, $availableupdates, $availableupdatesfetch,
|
||||
$buggyiconvnomb, $registered, array $cachewarnings = array(), $eventshandlers = 0) {
|
||||
$buggyiconvnomb, $registered, array $cachewarnings = array(), $eventshandlers = 0,
|
||||
$ignored = false, $devlibdir = false) {
|
||||
global $CFG;
|
||||
$output = '';
|
||||
|
||||
@@ -290,6 +293,7 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
$output .= $this->legacy_log_store_writing_error();
|
||||
$output .= empty($CFG->disableupdatenotifications) ? $this->available_updates($availableupdates, $availableupdatesfetch) : '';
|
||||
$output .= $this->insecure_dataroot_warning($insecuredataroot);
|
||||
$output .= $this->development_libs_directories_warning($devlibdir);
|
||||
$output .= $this->display_errors_warning($errorsdisplayed);
|
||||
$output .= $this->buggy_iconv_warning($buggyiconvnomb);
|
||||
$output .= $this->cron_overdue_warning($cronoverdue);
|
||||
@@ -519,6 +523,24 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a warning that a directory with development libs is present.
|
||||
*
|
||||
* @param bool $devlibdir True if the warning should be displayed.
|
||||
* @return string
|
||||
*/
|
||||
protected function development_libs_directories_warning($devlibdir) {
|
||||
|
||||
if ($devlibdir) {
|
||||
$moreinfo = new moodle_url('/report/security/index.php');
|
||||
$warning = get_string('devlibdirpresent', 'core_admin', ['moreinfourl' => $moreinfo->out()]);
|
||||
return $this->warning($warning, 'error');
|
||||
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an appropriate message if dataroot is insecure.
|
||||
* @param bool $errorsdisplayed
|
||||
@@ -1598,7 +1620,7 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
$row = new html_table_row();
|
||||
$row->attributes['class'] = 'type-' . $plugin->type . ' name-' . $plugin->type . '_' . $plugin->name;
|
||||
|
||||
if ($this->page->theme->resolve_image_location('icon', $plugin->type . '_' . $plugin->name)) {
|
||||
if ($this->page->theme->resolve_image_location('icon', $plugin->type . '_' . $plugin->name, null)) {
|
||||
$icon = $this->output->pix_icon('icon', '', $plugin->type . '_' . $plugin->name, array('class' => 'icon pluginicon'));
|
||||
} else {
|
||||
$icon = $this->output->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
|
||||
|
||||
@@ -32,7 +32,9 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
|
||||
$ADMIN->add('development', $temp);
|
||||
|
||||
// "Profiling" settingpage (conditionally if the 'xhprof' extension is available only).
|
||||
$xhprofenabled = extension_loaded('xhprof') || extension_loaded('tideways');
|
||||
$xhprofenabled = extension_loaded('tideways_xhprof');
|
||||
$xhprofenabled = $xhprofenabled || extension_loaded('tideways');
|
||||
$xhprofenabled = $xhprofenabled || extension_loaded('xhprof');
|
||||
$temp = new admin_settingpage('profiling', new lang_string('profiling', 'admin'), 'moodle/site:config', !$xhprofenabled);
|
||||
// Main profiling switch.
|
||||
$temp->add(new admin_setting_configcheckbox('profilingenabled', new lang_string('profilingenabled', 'admin'), new lang_string('profilingenabled_help', 'admin'), false));
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
@tool @tool_behat
|
||||
Feature: Backported behat step definitions
|
||||
In order to provide feature file compatibility between multiple Moodle versions
|
||||
As a developer
|
||||
I need to be able to use backported steps from newer Moodle versiosn
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | format |
|
||||
| Course 1 | C101 | topics |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C101 | student |
|
||||
| teacher1 | C101 | editingteacher |
|
||||
|
||||
Scenario: I am on the course homepage
|
||||
When I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
Then I should see "Topic 1"
|
||||
|
||||
@javascript
|
||||
Scenario: I am on the course homepage with JS
|
||||
When I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
Then I should see "Topic 1"
|
||||
|
||||
Scenario: I am on the course homepage with editing
|
||||
When I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
Then I should see "Topic 1"
|
||||
And I should see "Add an activity or resource"
|
||||
|
||||
@javascript
|
||||
Scenario: I am on the course homepage with editing and JS
|
||||
When I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
Then I should see "Topic 1"
|
||||
And I should see "Add an activity or resource"
|
||||
@@ -7,8 +7,7 @@ Feature: Forms manipulation
|
||||
@javascript
|
||||
Scenario: Basic forms manipulation
|
||||
Given I log in as "admin"
|
||||
And I follow "Preferences" in the user menu
|
||||
And I follow "Edit profile"
|
||||
And I open my profile in edit mode
|
||||
When I set the field "First name" to "Field value"
|
||||
And I set the field "Select a country" to "Japan"
|
||||
And I set the field "Unmask" to "1"
|
||||
|
||||
@@ -10,8 +10,7 @@ Feature: Transform steps arguments
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And I log in as "admin"
|
||||
And I follow "Preferences" in the user menu
|
||||
And I follow "Edit profile"
|
||||
And I open my profile in edit mode
|
||||
|
||||
Scenario: Use nasty strings on steps arguments
|
||||
When I set the field "Surname" to "$NASTYSTRING1"
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/str","core/templates","tool_lp/competency_rule"],function(a,b,c,d){var e=function(){d.apply(this,arguments)};return e.prototype=Object.create(d.prototype),e.prototype._container=null,e.prototype._templateLoaded=!1,e.prototype.getConfig=function(){return JSON.stringify({base:{points:this._getRequiredPoints()},competencies:this._getCompetenciesConfig()})},e.prototype._getCompetenciesConfig=function(){var b=[];return this._container.find("[data-competency]").each(function(){var c=a(this),d=c.data("competency"),e=parseInt(c.find('[name="points"]').val(),10),f=c.find('[name="required"]').prop("checked");b.push({id:d,points:e,required:f?1:0})}),b},e.prototype._getRequiredPoints=function(){return parseInt(this._container.find('[name="requiredpoints"]').val()||1,10)},e.prototype.getType=function(){return"core_competency\\competency_rule_points"},e.prototype.injectTemplate=function(b){var d,e=this,f=this._tree.getChildren(this._competency.id),g={base:{points:2},competencies:[]};if(this._templateLoaded=!1,e._competency.ruletype==e.getType())try{g=JSON.parse(e._competency.ruleconfig)}catch(h){}return d={requiredpoints:g&&g.base?g.base.points:2,competency:e._competency,children:[]},a.each(f,function(b,c){var e={id:c.id,shortname:c.shortname,required:!1,points:0};g&&a.each(g.competencies,function(a,b){b.id==e.id&&(e.required=b.required?!0:!1,e.points=b.points)}),d.children.push(e)}),c.render("tool_lp/competency_rule_points",d).then(function(a){e._container=b,b.html(a),b.find("input").change(function(){e._triggerChange()}),e._templateLoaded=!0,e._triggerChange()})},e.prototype.isValid=function(){if(!this._templateLoaded)return!1;var b=this._getRequiredPoints(),c=0,d=!0;return a.each(this._getCompetenciesConfig(),function(a,b){b.points<0&&(d=!1),c+=b.points}),d=d&&c>=b},e});
|
||||
define(["jquery","core/str","core/templates","tool_lp/competency_rule"],function(a,b,c,d){var e=function(){d.apply(this,arguments)};return e.prototype=Object.create(d.prototype),e.prototype._container=null,e.prototype._templateLoaded=!1,e.prototype.getConfig=function(){return JSON.stringify({base:{points:this._getRequiredPoints()},competencies:this._getCompetenciesConfig()})},e.prototype._getCompetenciesConfig=function(){var b=[];return this._container.find("[data-competency]").each(function(){var c=a(this),d=c.data("competency"),e=parseInt(c.find('[name="points"]').val(),10),f=c.find('[name="required"]').prop("checked");b.push({id:d,points:e,required:f?1:0})}),b},e.prototype._getRequiredPoints=function(){return parseInt(this._container.find('[name="requiredpoints"]').val()||1,10)},e.prototype.getType=function(){return"core_competency\\competency_rule_points"},e.prototype.injectTemplate=function(b){var d,e=this,f=this._tree.getChildren(this._competency.id),g={base:{points:2},competencies:[]};if(this._templateLoaded=!1,e._competency.ruletype==e.getType())try{g=JSON.parse(e._competency.ruleconfig)}catch(h){}return d={requiredpoints:g&&g.base?g.base.points:2,competency:e._competency,children:[]},a.each(f,function(b,c){var e={id:c.id,shortname:c.shortname,required:!1,points:0};g&&a.each(g.competencies,function(a,b){b.id==e.id&&(e.required=!!b.required,e.points=b.points)}),d.children.push(e)}),c.render("tool_lp/competency_rule_points",d).then(function(a){e._container=b,b.html(a),b.find("input").change(function(){e._triggerChange()}),e._templateLoaded=!0,e._triggerChange()})},e.prototype.isValid=function(){if(!this._templateLoaded)return!1;var b=this._getRequiredPoints(),c=0,d=!0;return a.each(this._getCompetenciesConfig(),function(a,b){b.points<0&&(d=!1),c+=b.points}),d=d&&c>=b},e});
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
define(["jquery","core/notification","core/ajax","core/templates","core/str","tool_lp/tree","tool_lp/competencypicker"],function(a,b,c,d,e,f,g){var h=function(a,b,c){g.prototype.constructor.apply(this,[1,!1,"self",c]),this._userId=a,this._plans=[],b&&(this._planId=b,this._singlePlan=!0)};return h.prototype=Object.create(g.prototype),h.prototype._plans=null,h.prototype._planId=null,h.prototype._singlePlan=!1,h.prototype._userId=null,h.prototype._afterRender=function(){var b=this;g.prototype._afterRender.apply(b,arguments),b._singlePlan||b._find('[data-action="chooseplan"]').change(function(c){b._planId=a(c.target).val(),b._loadCompetencies().then(b._refresh.bind(b))}.bind(b))},h.prototype._fetchCompetencies=function(a,d){var e=this;return c.call([{methodname:"core_competency_list_plan_competencies",args:{id:a}}])[0].done(function(a){var b,c,f=[];for(b=0;b<a.length;b++)c=a[b].competency,c.shortname.toLowerCase().indexOf(d.toLowerCase())<0||(c.children=[],c.haschildren=0,f.push(c));e._competencies=f}).fail(b.exception)},h.prototype._getPlan=function(b){var c;return a.each(this._plans,function(a,d){return d.id==b?(c=d,!1):void 0}),c},h.prototype._loadCompetencies=function(){return this._fetchCompetencies(this._planId,this._searchText)},h.prototype._loadPlans=function(){var d,e=this;return e._plans.length>0?a.when():(d=e._singlePlan?c.call([{methodname:"core_competency_read_plan",args:{id:this._planId}}])[0].then(function(a){return[a]}):c.call([{methodname:"core_competency_list_user_plans",args:{userid:e._userId}}])[0],d.done(function(a){e._plans=a}).fail(b.exception))},h.prototype._preRender=function(){var b=this;return b._loadPlans().then(function(){return!b._planId&&b._plans.length>0&&(b._planId=b._plans[0].id),b._planId?b._loadCompetencies():(b._plans=[],a.when())}.bind(b))},h.prototype._render=function(){var b=this;return b._preRender().then(function(){b._singlePlan||a.each(b._plans,function(a,c){c.id==b._planId?c.selected=!0:c.selected=!1});var c={competencies:b._competencies,plan:b._getPlan(b._planId),plans:b._plans,search:b._searchText,singlePlan:b._singlePlan};return d.render("tool_lp/competency_picker_user_plans",c)}.bind(b))},h});
|
||||
define(["jquery","core/notification","core/ajax","core/templates","core/str","tool_lp/tree","tool_lp/competencypicker"],function(a,b,c,d,e,f,g){var h=function(a,b,c){g.prototype.constructor.apply(this,[1,!1,"self",c]),this._userId=a,this._plans=[],b&&(this._planId=b,this._singlePlan=!0)};return h.prototype=Object.create(g.prototype),h.prototype._plans=null,h.prototype._planId=null,h.prototype._singlePlan=!1,h.prototype._userId=null,h.prototype._afterRender=function(){var b=this;g.prototype._afterRender.apply(b,arguments),b._singlePlan||b._find('[data-action="chooseplan"]').change(function(c){b._planId=a(c.target).val(),b._loadCompetencies().then(b._refresh.bind(b))}.bind(b))},h.prototype._fetchCompetencies=function(a,d){var e=this;return c.call([{methodname:"core_competency_list_plan_competencies",args:{id:a}}])[0].done(function(a){var b,c,f=[];for(b=0;b<a.length;b++)c=a[b].competency,c.shortname.toLowerCase().indexOf(d.toLowerCase())<0||(c.children=[],c.haschildren=0,f.push(c));e._competencies=f}).fail(b.exception)},h.prototype._getPlan=function(b){var c;return a.each(this._plans,function(a,d){if(d.id==b)return c=d,!1}),c},h.prototype._loadCompetencies=function(){return this._fetchCompetencies(this._planId,this._searchText)},h.prototype._loadPlans=function(){var d,e=this;return e._plans.length>0?a.when():(d=e._singlePlan?c.call([{methodname:"core_competency_read_plan",args:{id:this._planId}}])[0].then(function(a){return[a]}):c.call([{methodname:"core_competency_list_user_plans",args:{userid:e._userId}}])[0],d.done(function(a){e._plans=a}).fail(b.exception))},h.prototype._preRender=function(){var b=this;return b._loadPlans().then(function(){return!b._planId&&b._plans.length>0&&(b._planId=b._plans[0].id),b._planId?b._loadCompetencies():(b._plans=[],a.when())}.bind(b))},h.prototype._render=function(){var b=this;return b._preRender().then(function(){b._singlePlan||a.each(b._plans,function(a,c){c.id==b._planId?c.selected=!0:c.selected=!1});var c={competencies:b._competencies,plan:b._getPlan(b._planId),plans:b._plans,search:b._searchText,singlePlan:b._singlePlan};return d.render("tool_lp/competency_picker_user_plans",c)}.bind(b))},h});
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/notification","core/templates","tool_lp/dialogue","tool_lp/competency_outcomes","core/str"],function(a,b,c,d,e,f){var g=function(b,c){this._eventNode=a("<div></div>"),this._tree=b,this._rulesModules=c,this._setUp()};return g.prototype._competency=null,g.prototype._eventNode=null,g.prototype._outcomesOption=null,g.prototype._popup=null,g.prototype._ready=null,g.prototype._rules=null,g.prototype._rulesModules=null,g.prototype._tree=null,g.prototype._afterChange=function(){this._isValid()?this._find('[data-action="save"]').prop("disabled",!1):this._find('[data-action="save"]').prop("disabled",!0)},g.prototype._afterRuleConfigChange=function(a,b){b==this._getRule()&&this._afterChange()},g.prototype._afterRender=function(){var a=this;a._find('[name="outcome"]').on("change",function(){a._switchedOutcome()}).trigger("change"),a._find('[name="rule"]').on("change",function(){a._switchedRule()}).trigger("change"),a._find('[data-action="save"]').on("click",function(){a._trigger("save",a._getConfig()),a.close()}),a._find('[data-action="cancel"]').on("click",function(){a.close()})},g.prototype.canBeConfigured=function(){var b=!1;return a.each(this._rules,function(a,c){return c.canConfig()?(b=!0,!1):void 0}),b},g.prototype.close=function(){this._popup.close(),this._popup=null},g.prototype.display=function(){var a=this;if(a._competency)return a._render().then(function(b){return f.get_string("competencyrule","tool_lp").then(function(c){a._popup=new d(c,b,a._afterRender.bind(a))})}).fail(b.exception)},g.prototype._find=function(b){return a(this._popup.getContent()).find(b)},g.prototype._getApplicableOutcomesOptions=function(){var b=this,c=[];return a.each(b._outcomesOption,function(a,d){c.push({code:d.code,name:d.name,selected:d.code==b._competency.ruleoutcome?!0:!1})}),c},g.prototype._getApplicableRulesOptions=function(){var b=this,c=[];return a.each(b._rules,function(a,d){d.canConfig()&&c.push({name:b._getRuleName(d.getType()),type:d.getType(),selected:d.getType()==b._competency.ruletype?!0:!1})}),c},g.prototype._getConfig=function(){var a=this._getRule();return{ruletype:a?a.getType():null,ruleconfig:a?a.getConfig():null,ruleoutcome:this._getOutcome()}},g.prototype._getOutcome=function(){return this._find('[name="outcome"]').val()},g.prototype._getRule=function(){var b,c=this._find('[name="rule"]').val();return a.each(this._rules,function(a,d){return d.getType()==c?(b=d,!1):void 0}),b},g.prototype._getRuleName=function(b){var c,d=this;return a.each(d._rulesModules,function(a,d){return d.type==b?(c=d.name,!1):void 0}),c},g.prototype._initOutcomes=function(){var a=this;return e.getAll().then(function(b){a._outcomesOption=b})},g.prototype._initRules=function(){var b=this,c=[];return a.each(b._rules,function(d,e){var f=e.init().then(function(){e.setTargetCompetency(b._competency),e.on("change",b._afterRuleConfigChange.bind(b))},function(){return b._rules.splice(d,1),a.when()});c.push(f)}),a.when.apply(a.when,c)},g.prototype._isValid=function(){var a=this._getOutcome(),b=this._getRule();return a==e.NONE?!0:b?b.isValid():!1},g.prototype.on=function(a,b){this._eventNode.on(a,b)},g.prototype._preRender=function(){return this.ready()},g.prototype.ready=function(){return this._ready.promise()},g.prototype._render=function(){var a=this;return this._preRender().then(function(){var b;a.canBeConfigured()?(b={},b.outcomes=a._getApplicableOutcomesOptions(),b.rules=a._getApplicableRulesOptions()):b=!1;var d={competencyshortname:a._competency.shortname,config:b};return c.render("tool_lp/competency_rule_config",d)})},g.prototype.setTargetCompetencyId=function(b){var c=this;c._competency=c._tree.getCompetency(b),a.each(c._rules,function(a,b){b.setTargetCompetency(c._competency)})},g.prototype._setUp=function(){var b=this,c=[],d=[];b._ready=a.Deferred(),b._rules=[],a.each(b._rulesModules,function(a,b){d.push(b.amd)}),require(d,function(){a.each(arguments,function(a,c){var d=new c(b._tree);b._rules.push(d)}),c.push(b._initRules()),c.push(b._initOutcomes()),a.when.apply(a.when,c).always(function(){b._ready.resolve()})})},g.prototype._switchedOutcome=function(){var a=this,b=a._getOutcome();return b==e.NONE?(a._find('[data-region="rule-type"]').hide().find('[name="rule"]').val(-1),a._find('[data-region="rule-config"]').empty().hide(),void a._afterChange()):(a._find('[data-region="rule-type"]').show(),a._find('[data-region="rule-config"]').show(),void a._afterChange())},g.prototype._switchedRule=function(){var a=this,b=a._find('[data-region="rule-config"]'),c=a._getRule();return c?void c.injectTemplate(b).then(function(){b.show()},function(){b.empty().hide()}).always(function(){a._afterChange()}):(b.empty().hide(),void a._afterChange())},g.prototype._trigger=function(a,b){this._eventNode.trigger(a,[b])},g});
|
||||
define(["jquery","core/notification","core/templates","tool_lp/dialogue","tool_lp/competency_outcomes","core/str"],function(a,b,c,d,e,f){var g=function(b,c){this._eventNode=a("<div></div>"),this._tree=b,this._rulesModules=c,this._setUp()};return g.prototype._competency=null,g.prototype._eventNode=null,g.prototype._outcomesOption=null,g.prototype._popup=null,g.prototype._ready=null,g.prototype._rules=null,g.prototype._rulesModules=null,g.prototype._tree=null,g.prototype._afterChange=function(){this._isValid()?this._find('[data-action="save"]').prop("disabled",!1):this._find('[data-action="save"]').prop("disabled",!0)},g.prototype._afterRuleConfigChange=function(a,b){b==this._getRule()&&this._afterChange()},g.prototype._afterRender=function(){var a=this;a._find('[name="outcome"]').on("change",function(){a._switchedOutcome()}).trigger("change"),a._find('[name="rule"]').on("change",function(){a._switchedRule()}).trigger("change"),a._find('[data-action="save"]').on("click",function(){a._trigger("save",a._getConfig()),a.close()}),a._find('[data-action="cancel"]').on("click",function(){a.close()})},g.prototype.canBeConfigured=function(){var b=!1;return a.each(this._rules,function(a,c){if(c.canConfig())return b=!0,!1}),b},g.prototype.close=function(){this._popup.close(),this._popup=null},g.prototype.display=function(){var a=this;if(a._competency)return a._render().then(function(b){return f.get_string("competencyrule","tool_lp").then(function(c){a._popup=new d(c,b,a._afterRender.bind(a))})}).fail(b.exception)},g.prototype._find=function(b){return a(this._popup.getContent()).find(b)},g.prototype._getApplicableOutcomesOptions=function(){var b=this,c=[];return a.each(b._outcomesOption,function(a,d){c.push({code:d.code,name:d.name,selected:d.code==b._competency.ruleoutcome})}),c},g.prototype._getApplicableRulesOptions=function(){var b=this,c=[];return a.each(b._rules,function(a,d){d.canConfig()&&c.push({name:b._getRuleName(d.getType()),type:d.getType(),selected:d.getType()==b._competency.ruletype})}),c},g.prototype._getConfig=function(){var a=this._getRule();return{ruletype:a?a.getType():null,ruleconfig:a?a.getConfig():null,ruleoutcome:this._getOutcome()}},g.prototype._getOutcome=function(){return this._find('[name="outcome"]').val()},g.prototype._getRule=function(){var b,c=this._find('[name="rule"]').val();return a.each(this._rules,function(a,d){if(d.getType()==c)return b=d,!1}),b},g.prototype._getRuleName=function(b){var c,d=this;return a.each(d._rulesModules,function(a,d){if(d.type==b)return c=d.name,!1}),c},g.prototype._initOutcomes=function(){var a=this;return e.getAll().then(function(b){a._outcomesOption=b})},g.prototype._initRules=function(){var b=this,c=[];return a.each(b._rules,function(d,e){var f=e.init().then(function(){e.setTargetCompetency(b._competency),e.on("change",b._afterRuleConfigChange.bind(b))},function(){return b._rules.splice(d,1),a.when()});c.push(f)}),a.when.apply(a.when,c)},g.prototype._isValid=function(){var a=this._getOutcome(),b=this._getRule();return a==e.NONE||!!b&&b.isValid()},g.prototype.on=function(a,b){this._eventNode.on(a,b)},g.prototype._preRender=function(){return this.ready()},g.prototype.ready=function(){return this._ready.promise()},g.prototype._render=function(){var a=this;return this._preRender().then(function(){var b;a.canBeConfigured()?(b={},b.outcomes=a._getApplicableOutcomesOptions(),b.rules=a._getApplicableRulesOptions()):b=!1;var d={competencyshortname:a._competency.shortname,config:b};return c.render("tool_lp/competency_rule_config",d)})},g.prototype.setTargetCompetencyId=function(b){var c=this;c._competency=c._tree.getCompetency(b),a.each(c._rules,function(a,b){b.setTargetCompetency(c._competency)})},g.prototype._setUp=function(){var b=this,c=[],d=[];b._ready=a.Deferred(),b._rules=[],a.each(b._rulesModules,function(a,b){d.push(b.amd)}),require(d,function(){a.each(arguments,function(a,c){var d=new c(b._tree);b._rules.push(d)}),c.push(b._initRules()),c.push(b._initOutcomes()),a.when.apply(a.when,c).always(function(){b._ready.resolve()})})},g.prototype._switchedOutcome=function(){var a=this,b=a._getOutcome();return b==e.NONE?(a._find('[data-region="rule-type"]').hide().find('[name="rule"]').val(-1),a._find('[data-region="rule-config"]').empty().hide(),void a._afterChange()):(a._find('[data-region="rule-type"]').show(),a._find('[data-region="rule-config"]').show(),void a._afterChange())},g.prototype._switchedRule=function(){var a=this,b=a._find('[data-region="rule-config"]'),c=a._getRule();return c?void c.injectTemplate(b).then(function(){b.show()},function(){b.empty().hide()}).always(function(){a._afterChange()}):(b.empty().hide(),void a._afterChange())},g.prototype._trigger=function(a,b){this._eventNode.trigger(a,[b])},g});
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["core/ajax","core/notification","core/templates","tool_lp/tree","tool_lp/competency_outcomes","jquery"],function(a,b,c,d,e,f){var g={},h=0,i="",j="",k="",l=!1,m=function(a,b){var c=0,d=!1;for(a.haschildren=!1,a.children=[],c=0;c<b.length;c++)d=b[c],d.parentid==a.id&&(a.haschildren=!0,a.children.push(d),m(d,b))},n=function(b){var e=f.Deferred();return c.render("tool_lp/loading",{}).done(function(n,o){c.replaceNodeContents(f(j),n,o);var p=a.call([{methodname:"core_competency_search_competencies",args:{searchtext:b,competencyframeworkid:h}}]);p[0].done(function(a){g={};var b=0;for(b=0;b<a.length;b++)g[a[b].id]=a[b];var h=[],n=!1;for(b=0;b<a.length;b++)n=a[b],0===parseInt(n.parentid,10)&&(h.push(n),m(n,a));var o={shortname:i,canmanage:l,competencies:h};c.render("tool_lp/competencies_tree_root",o).done(function(a,b){c.replaceNodeContents(f(j),f(a).html(),b);var h=new d(j,!1);if(k){var i=f(j).find("[data-id="+k+"]");i.length&&(h.selectItem(i),h.updateFocus(i))}e.resolve(g)}).fail(e.reject)}).fail(e.reject)}),e.promise()},o=function(a,b){var c=b.selected;k=c.attr("data-id")};return{init:function(a,c,d,e,f){h=a,i=c,l=f,j=e,n(d).fail(b.exception),this.on("selectionchanged",o)},on:function(a,b){f(j).on(a,b)},getChildren:function(a){var b=[];return f.each(g,function(c,d){d.parentid==a&&b.push(d)}),b},getCompetencyFrameworkId:function(){return h},getCompetency:function(a){return g[a]},getCompetencyLevel:function(a){var b=this.getCompetency(a),c=b.path.replace(/^\/|\/$/g,"").split("/").length;return c},hasChildren:function(a){return this.getChildren(a).length>0},hasRule:function(a){var b=this.getCompetency(a);return b?b.ruleoutcome!=e.OUTCOME_NONE&&b.ruletype:!1},reloadCompetencies:function(){return n("").fail(b.exception)},listCompetencies:function(){return g}}});
|
||||
define(["core/ajax","core/notification","core/templates","tool_lp/tree","tool_lp/competency_outcomes","jquery"],function(a,b,c,d,e,f){var g={},h=0,i="",j="",k="",l=!1,m=function(a,b){var c=0,d=!1;for(a.haschildren=!1,a.children=[],c=0;c<b.length;c++)d=b[c],d.parentid==a.id&&(a.haschildren=!0,a.children.push(d),m(d,b))},n=function(b){var e=f.Deferred();return c.render("tool_lp/loading",{}).done(function(n,o){c.replaceNodeContents(f(j),n,o);var p=a.call([{methodname:"core_competency_search_competencies",args:{searchtext:b,competencyframeworkid:h}}]);p[0].done(function(a){g={};var b=0;for(b=0;b<a.length;b++)g[a[b].id]=a[b];var h=[],n=!1;for(b=0;b<a.length;b++)n=a[b],0===parseInt(n.parentid,10)&&(h.push(n),m(n,a));var o={shortname:i,canmanage:l,competencies:h};c.render("tool_lp/competencies_tree_root",o).done(function(a,b){c.replaceNodeContents(f(j),f(a).html(),b);var h=new d(j,(!1));if(k){var i=f(j).find("[data-id="+k+"]");i.length&&(h.selectItem(i),h.updateFocus(i))}e.resolve(g)}).fail(e.reject)}).fail(e.reject)}),e.promise()},o=function(a,b){var c=b.selected;k=c.attr("data-id")};return{init:function(a,c,d,e,f){h=a,i=c,l=f,j=e,n(d).fail(b.exception),this.on("selectionchanged",o)},on:function(a,b){f(j).on(a,b)},getChildren:function(a){var b=[];return f.each(g,function(c,d){d.parentid==a&&b.push(d)}),b},getCompetencyFrameworkId:function(){return h},getCompetency:function(a){return g[a]},getCompetencyLevel:function(a){var b=this.getCompetency(a),c=b.path.replace(/^\/|\/$/g,"").split("/").length;return c},hasChildren:function(a){return this.getChildren(a).length>0},hasRule:function(a){var b=this.getCompetency(a);return!!b&&(b.ruleoutcome!=e.OUTCOME_NONE&&b.ruletype)},reloadCompetencies:function(){return n("").fail(b.exception)},listCompetencies:function(){return g}}});
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/ajax","core/str","tool_lp/competencypicker","core/templates","core/notification"],function(a,b,c,d,e,f){var g=function(a,b,c,d,e){this.buttonSelector=a,this.inputHiddenSelector=b,this.staticElementSelector=c,this.frameworkId=d,this.pageContextId=e,this.registerEvents()};return g.prototype.buttonSelector=null,g.prototype.inputHiddenSelector=null,g.prototype.staticElementSelector=null,g.prototype.frameworkId=null,g.prototype.pageContextId=null,g.prototype.setParent=function(d){var e=this;0!==d.competencyId?b.call([{methodname:"core_competency_read_competency",args:{id:d.competencyId}}])[0].done(function(b){a(e.staticElementSelector).html(b.shortname),a(e.inputHiddenSelector).val(b.id)}).fail(f.exception):c.get_string("competencyframeworkroot","tool_lp").then(function(b){a(e.staticElementSelector).html(b),a(e.inputHiddenSelector).val(d.competencyId)}).fail(f.exception)},g.prototype.registerEvents=function(){var b=this;a(b.buttonSelector).on("click",function(a){a.preventDefault();var c=new d(b.pageContextId,b.frameworkId,"self",!1);c._render=function(){var a=this;return a._preRender().then(function(){var b={competencies:a._competencies,framework:a._getFramework(a._frameworkId),frameworks:a._frameworks,search:a._searchText,singleFramework:a._singleFramework};return e.render("tool_lp/competency_picker_competencyform",b)}.bind(a))},c.on("save",function(a,c){b.setParent(c)}.bind(b)),c.display()})},{init:function(a,b,c,d,e){new g(a,b,c,d,e)}}});
|
||||
define(["jquery","core/ajax","core/str","tool_lp/competencypicker","core/templates","core/notification"],function(a,b,c,d,e,f){var g=function(a,b,c,d,e){this.buttonSelector=a,this.inputHiddenSelector=b,this.staticElementSelector=c,this.frameworkId=d,this.pageContextId=e,this.registerEvents()};return g.prototype.buttonSelector=null,g.prototype.inputHiddenSelector=null,g.prototype.staticElementSelector=null,g.prototype.frameworkId=null,g.prototype.pageContextId=null,g.prototype.setParent=function(d){var e=this;0!==d.competencyId?b.call([{methodname:"core_competency_read_competency",args:{id:d.competencyId}}])[0].done(function(b){a(e.staticElementSelector).html(b.shortname),a(e.inputHiddenSelector).val(b.id)}).fail(f.exception):c.get_string("competencyframeworkroot","tool_lp").then(function(b){a(e.staticElementSelector).html(b),a(e.inputHiddenSelector).val(d.competencyId)}).fail(f.exception)},g.prototype.registerEvents=function(){var b=this;a(b.buttonSelector).on("click",function(a){a.preventDefault();var c=new d(b.pageContextId,b.frameworkId,"self",(!1));c._render=function(){var a=this;return a._preRender().then(function(){var b={competencies:a._competencies,framework:a._getFramework(a._frameworkId),frameworks:a._frameworks,search:a._searchText,singleFramework:a._singleFramework};return e.render("tool_lp/competency_picker_competencyform",b)}.bind(a))},c.on("save",function(a,c){b.setParent(c)}.bind(b)),c.display()})},{init:function(a,b,c,d,e){new g(a,b,c,d,e)}}});
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/notification","core/ajax","core/templates"],function(a,b,c,d){var e=function(a,b,c,d,e,f){this._rootElement=a,this._competencyId=b,this._userId=c,this._planId=d,this._courseId=e,this._valid=!0,this._displayuser="undefined"!=typeof f?f:!1,this._planId?(this._methodName="tool_lp_data_for_user_competency_summary_in_plan",this._args={competencyid:this._competencyId,planid:this._planId},this._templateName="tool_lp/user_competency_summary_in_plan"):this._courseId?(this._methodName="tool_lp_data_for_user_competency_summary_in_course",this._args={userid:this._userId,competencyid:this._competencyId,courseid:this._courseId},this._templateName="tool_lp/user_competency_summary_in_course"):(this._methodName="tool_lp_data_for_user_competency_summary",this._args={userid:this._userId,competencyid:this._competencyId},this._templateName="tool_lp/user_competency_summary")};return e.prototype.reload=function(){var a=this,e=[];this._valid&&(e=c.call([{methodname:this._methodName,args:this._args}]),e[0].done(function(c){a._displayuser&&(c.displayuser=!0),d.render(a._templateName,c).done(function(b,c){d.replaceNode(a._rootElement,b,c)}).fail(b.exception)}).fail(b.exception))},e.prototype._rootElement=null,e.prototype._courseId=null,e.prototype._valid=null,e.prototype._planId=null,e.prototype._competencyId=null,e.prototype._userId=null,e.prototype._methodName=null,e.prototype._args=null,e.prototype._templateName=null,e.prototype._displayuser=!1,e});
|
||||
define(["jquery","core/notification","core/ajax","core/templates"],function(a,b,c,d){var e=function(a,b,c,d,e,f){this._rootElement=a,this._competencyId=b,this._userId=c,this._planId=d,this._courseId=e,this._valid=!0,this._displayuser="undefined"!=typeof f&&f,this._planId?(this._methodName="tool_lp_data_for_user_competency_summary_in_plan",this._args={competencyid:this._competencyId,planid:this._planId},this._templateName="tool_lp/user_competency_summary_in_plan"):this._courseId?(this._methodName="tool_lp_data_for_user_competency_summary_in_course",this._args={userid:this._userId,competencyid:this._competencyId,courseid:this._courseId},this._templateName="tool_lp/user_competency_summary_in_course"):(this._methodName="tool_lp_data_for_user_competency_summary",this._args={userid:this._userId,competencyid:this._competencyId},this._templateName="tool_lp/user_competency_summary")};return e.prototype.reload=function(){var a=this,e=[];this._valid&&(e=c.call([{methodname:this._methodName,args:this._args}]),e[0].done(function(c){a._displayuser&&(c.displayuser=!0),d.render(a._templateName,c).done(function(b,c){d.replaceNode(a._rootElement,b,c)}).fail(b.exception)}).fail(b.exception))},e.prototype._rootElement=null,e.prototype._courseId=null,e.prototype._valid=null,e.prototype._planId=null,e.prototype._competencyId=null,e.prototype._userId=null,e.prototype._methodName=null,e.prototype._args=null,e.prototype._templateName=null,e.prototype._displayuser=!1,e});
|
||||
@@ -1 +1 @@
|
||||
define(["jquery","core/notification","core/str","core/ajax","core/templates","tool_lp/dialogue"],function(a,b,c,d,e,f){var g=function(b,c,d){this._regionSelector=b,this._userCompetencySelector=c,this._planId=d,a(this._regionSelector).on("click",this._userCompetencySelector,this._handleClick.bind(this))};return g.prototype._handleClick=function(c){c.preventDefault();var e=a(c.target).closest("tr"),f=a(e).data("competencyid"),g=a(e).data("userid"),h=this._planId,i=d.call([{methodname:"tool_lp_data_for_user_competency_summary_in_plan",args:{competencyid:f,planid:h},done:this._contextLoaded.bind(this),fail:b.exception}]);i[0].then(function(a){var c="core_competency_user_competency_viewed_in_plan";a.plan.iscompleted&&(c="core_competency_user_competency_plan_viewed"),d.call([{methodname:c,args:{competencyid:f,userid:g,planid:h},fail:b.exception}])})},g.prototype._contextLoaded=function(a){var d=this;e.render("tool_lp/user_competency_summary_in_plan",a).done(function(a,g){c.get_string("usercompetencysummary","report_competency").done(function(b){new f(b,a,e.runTemplateJS.bind(e,g),d._refresh.bind(d),!0)}).fail(b.exception)}).fail(b.exception)},g.prototype._refresh=function(){var a=this._planId;d.call([{methodname:"tool_lp_data_for_plan_page",args:{planid:a},done:this._pageContextLoaded.bind(this),fail:b.exception}])},g.prototype._pageContextLoaded=function(a){var c=this;e.render("tool_lp/plan_page",a).done(function(a,b){e.replaceNode(c._regionSelector,a,b)}).fail(b.exception)},g.prototype._regionSelector=null,g.prototype._userCompetencySelector=null,g.prototype._planId=null,g});
|
||||
define(["jquery","core/notification","core/str","core/ajax","core/templates","tool_lp/dialogue"],function(a,b,c,d,e,f){var g=function(b,c,d){this._regionSelector=b,this._userCompetencySelector=c,this._planId=d,a(this._regionSelector).on("click",this._userCompetencySelector,this._handleClick.bind(this))};return g.prototype._handleClick=function(c){c.preventDefault();var e=a(c.target).closest("tr"),f=a(e).data("competencyid"),g=a(e).data("userid"),h=this._planId,i=d.call([{methodname:"tool_lp_data_for_user_competency_summary_in_plan",args:{competencyid:f,planid:h},done:this._contextLoaded.bind(this),fail:b.exception}]);i[0].then(function(a){var c="core_competency_user_competency_viewed_in_plan";a.plan.iscompleted&&(c="core_competency_user_competency_plan_viewed"),d.call([{methodname:c,args:{competencyid:f,userid:g,planid:h},fail:b.exception}])})},g.prototype._contextLoaded=function(a){var d=this;e.render("tool_lp/user_competency_summary_in_plan",a).done(function(a,g){c.get_string("usercompetencysummary","report_competency").done(function(b){new f(b,a,e.runTemplateJS.bind(e,g),d._refresh.bind(d),(!0))}).fail(b.exception)}).fail(b.exception)},g.prototype._refresh=function(){var a=this._planId;d.call([{methodname:"tool_lp_data_for_plan_page",args:{planid:a},done:this._pageContextLoaded.bind(this),fail:b.exception}])},g.prototype._pageContextLoaded=function(a){var c=this;e.render("tool_lp/plan_page",a).done(function(a,b){e.replaceNode(c._regionSelector,a,b)}).fail(b.exception)},g.prototype._regionSelector=null,g.prototype._userCompetencySelector=null,g.prototype._planId=null,g});
|
||||
@@ -99,7 +99,7 @@ class competency extends persistent {
|
||||
// Description.
|
||||
$mform->addElement('editor', 'description',
|
||||
get_string('description', 'tool_lp'), array('rows' => 4));
|
||||
$mform->setType('description', PARAM_RAW);
|
||||
$mform->setType('description', PARAM_CLEANHTML);
|
||||
// ID number.
|
||||
$mform->addElement('text', 'idnumber', get_string('idnumber', 'tool_lp'), 'maxlength="100"');
|
||||
$mform->setType('idnumber', PARAM_RAW);
|
||||
|
||||
@@ -62,7 +62,7 @@ class competency_framework extends persistent {
|
||||
// Description.
|
||||
$mform->addElement('editor', 'description',
|
||||
get_string('description', 'tool_lp'), array('rows' => 4));
|
||||
$mform->setType('description', PARAM_RAW);
|
||||
$mform->setType('description', PARAM_CLEANHTML);
|
||||
// ID number.
|
||||
$mform->addElement('text', 'idnumber', get_string('idnumber', 'tool_lp'), 'maxlength="100"');
|
||||
$mform->setType('idnumber', PARAM_RAW);
|
||||
|
||||
@@ -184,8 +184,14 @@ abstract class persistent extends moodleform {
|
||||
$data = $this->get_persistent()->to_record();
|
||||
$class = static::$persistentclass;
|
||||
$properties = $class::get_formatted_properties();
|
||||
$allproperties = $class::properties_definition();
|
||||
|
||||
foreach ($data as $field => $value) {
|
||||
// Clean data if it is to be displayed in a form.
|
||||
if (isset($allproperties[$field]['type'])) {
|
||||
$data->$field = clean_param($data->$field, $allproperties[$field]['type']);
|
||||
}
|
||||
|
||||
// Convert formatted properties.
|
||||
if (isset($properties[$field])) {
|
||||
$data->$field = array(
|
||||
|
||||
@@ -59,7 +59,7 @@ class plan extends persistent {
|
||||
$mform->addRule('name', get_string('maximumchars', '', 100), 'maxlength', 100, 'client');
|
||||
// Description.
|
||||
$mform->addElement('editor', 'description', get_string('plandescription', 'tool_lp'), array('rows' => 4));
|
||||
$mform->setType('description', PARAM_RAW);
|
||||
$mform->setType('description', PARAM_CLEANHTML);
|
||||
|
||||
$mform->addElement('date_time_selector', 'duedate', get_string('duedate', 'tool_lp'), array('optional' => true));
|
||||
$mform->addHelpButton('duedate', 'duedate', 'tool_lp');
|
||||
|
||||
@@ -58,7 +58,7 @@ class template extends persistent {
|
||||
// Description.
|
||||
$mform->addElement('editor', 'description',
|
||||
get_string('description', 'tool_lp'), array('rows' => 4));
|
||||
$mform->setType('description', PARAM_RAW);
|
||||
$mform->setType('description', PARAM_CLEANHTML);
|
||||
$mform->addElement('selectyesno', 'visible',
|
||||
get_string('visible', 'tool_lp'));
|
||||
$mform->addElement('date_time_selector',
|
||||
|
||||
@@ -54,7 +54,7 @@ class user_evidence extends persistent {
|
||||
$mform->addRule('name', get_string('maximumchars', '', 100), 'maxlength', 100, 'client');
|
||||
// Description.
|
||||
$mform->addElement('editor', 'description', get_string('userevidencedescription', 'tool_lp'), array('rows' => 10));
|
||||
$mform->setType('description', PARAM_RAW);
|
||||
$mform->setType('description', PARAM_CLEANHTML);
|
||||
|
||||
$mform->addElement('url', 'url', get_string('userevidenceurl', 'tool_lp'), array(), array('usefilepicker' => false));
|
||||
$mform->setType('url', PARAM_RAW_TRIMMED); // Can not use PARAM_URL, it silently converts bad URLs to ''.
|
||||
|
||||
@@ -92,7 +92,7 @@ require(['core/str'], function(Str) {
|
||||
{ key: 'comments', component: 'moodle' },
|
||||
{ key: 'commentscount', component: 'moodle' },
|
||||
{ key: 'commentsrequirelogin', component: 'moodle' },
|
||||
{ key: 'deletecomment', component: 'moodle' },
|
||||
{ key: 'deletecommentbyon', component: 'moodle' },
|
||||
]).then(function() {
|
||||
// Kick off when strings are loaded.
|
||||
Y.use('core_comment', function(Y) {
|
||||
|
||||
@@ -45,6 +45,13 @@ class api {
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
|
||||
// Check if we can return this from cache.
|
||||
$cache = \cache::make('tool_mobile', 'plugininfo');
|
||||
$pluginsinfo = $cache->get('mobileplugins');
|
||||
if ($pluginsinfo !== false) {
|
||||
return (array)$pluginsinfo;
|
||||
}
|
||||
|
||||
$pluginsinfo = [];
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
|
||||
@@ -56,7 +63,7 @@ class api {
|
||||
$component = $plugintype . '_' . $plugin;
|
||||
$version = get_component_version($component);
|
||||
|
||||
require_once("$path/db/mobile.php");
|
||||
require("$path/db/mobile.php");
|
||||
foreach ($addons as $addonname => $addoninfo) {
|
||||
$plugininfo = array(
|
||||
'component' => $component,
|
||||
@@ -69,7 +76,7 @@ class api {
|
||||
);
|
||||
|
||||
// All the mobile packages must be under the plugin mobile directory.
|
||||
$package = $path . DIRECTORY_SEPARATOR . 'mobile' . DIRECTORY_SEPARATOR . $addonname . '.zip';
|
||||
$package = $path . '/mobile/' . $addonname . '.zip';
|
||||
if (file_exists($package)) {
|
||||
$plugininfo['fileurl'] = $CFG->wwwroot . '' . str_replace($CFG->dirroot, '', $package);
|
||||
$plugininfo['filehash'] = sha1_file($package);
|
||||
@@ -79,6 +86,9 @@ class api {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cache->set('mobileplugins', $pluginsinfo);
|
||||
|
||||
return $pluginsinfo;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Mobile cache definitions.
|
||||
*
|
||||
* @package tool_mobile
|
||||
* @copyright 2017 Skylar Kelty <S.Kelty@kent.ac.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$definitions = array(
|
||||
'plugininfo' => array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'simplekeys' => true,
|
||||
'staticacceleration' => true,
|
||||
'staticaccelerationsize' => 1
|
||||
)
|
||||
);
|
||||
@@ -23,3 +23,4 @@
|
||||
*/
|
||||
|
||||
$string['pluginname'] = 'Moodle Mobile tools';
|
||||
$string['cachedef_plugininfo'] = 'This stores the list of plugins with mobile addons';
|
||||
|
||||
@@ -53,10 +53,13 @@ class eventobservers {
|
||||
* @param \core\event\course_deleted $event The course deleted event.
|
||||
*/
|
||||
public static function course_deleted(\core\event\course_deleted $event) {
|
||||
// Delete rules defined inside this course and associated subscriptions.
|
||||
$rules = rule_manager::get_rules_by_courseid($event->courseid, 0, 0, false);
|
||||
foreach ($rules as $rule) {
|
||||
rule_manager::delete_rule($rule->id, $event->get_context());
|
||||
}
|
||||
// Delete remaining subscriptions inside this course (from site-wide rules).
|
||||
subscription_manager::remove_all_subscriptions_in_course($event->get_context());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -176,8 +176,12 @@ class subscription {
|
||||
if (empty($courseid)) {
|
||||
return get_string('site');
|
||||
} else {
|
||||
$course = get_course($courseid);
|
||||
return format_string($course->fullname, true, array('context' => $context));
|
||||
try {
|
||||
$course = get_course($courseid);
|
||||
return format_string($course->fullname, true, array('context' => $context));
|
||||
} catch (\dml_exception $e) {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,9 +118,10 @@ class subscription_manager {
|
||||
|
||||
// If successful trigger a subscription_deleted event.
|
||||
if ($success) {
|
||||
if (!empty($subscription->courseid)) {
|
||||
if (!empty($subscription->courseid) &&
|
||||
($coursecontext = \context_course::instance($subscription->courseid, IGNORE_MISSING))) {
|
||||
$courseid = $subscription->courseid;
|
||||
$context = \context_course::instance($subscription->courseid);
|
||||
$context = $coursecontext;
|
||||
} else {
|
||||
$courseid = 0;
|
||||
$context = \context_system::instance();
|
||||
@@ -222,6 +223,31 @@ class subscription_manager {
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all subscriptions in a course.
|
||||
*
|
||||
* This is called after a course was deleted, context no longer exists but we kept the object
|
||||
*
|
||||
* @param \context_course $coursecontext the context of the course
|
||||
*/
|
||||
public static function remove_all_subscriptions_in_course($coursecontext) {
|
||||
global $DB;
|
||||
|
||||
// Store all the subscriptions we have to delete.
|
||||
if ($subscriptions = $DB->get_records('tool_monitor_subscriptions', ['courseid' => $coursecontext->instanceid])) {
|
||||
// Delete subscriptions in bulk.
|
||||
$DB->delete_records('tool_monitor_subscriptions', ['courseid' => $coursecontext->instanceid]);
|
||||
|
||||
// Trigger events one by one.
|
||||
foreach ($subscriptions as $subscription) {
|
||||
$params = ['objectid' => $subscription->id, 'context' => $coursecontext];
|
||||
$event = \tool_monitor\event\subscription_deleted::create($params);
|
||||
$event->add_record_snapshot('tool_monitor_subscriptions', $subscription);
|
||||
$event->trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a subscription instance for an given subscription id.
|
||||
*
|
||||
|
||||
@@ -77,5 +77,22 @@ function xmldb_tool_monitor_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2016052305, 'tool', 'monitor');
|
||||
}
|
||||
|
||||
if ($oldversion < 2016052306) {
|
||||
|
||||
// Delete "orphaned" subscriptions.
|
||||
$sql = "SELECT DISTINCT s.courseid
|
||||
FROM {tool_monitor_subscriptions} s
|
||||
LEFT OUTER JOIN {course} c ON c.id = s.courseid
|
||||
WHERE s.courseid <> 0 and c.id IS NULL";
|
||||
$deletedcourses = $DB->get_field_sql($sql);
|
||||
if ($deletedcourses) {
|
||||
list($sql, $params) = $DB->get_in_or_equal($deletedcourses);
|
||||
$DB->execute("DELETE FROM {tool_monitor_subscriptions} WHERE courseid " . $sql, $params);
|
||||
}
|
||||
|
||||
// Monitor savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2016052306, 'tool', 'monitor');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ if (empty($courseid)) {
|
||||
// check system level capability.
|
||||
if (!has_capability('tool/monitor:subscribe', $context)) {
|
||||
// If not system level then check to see if they have access to any course level rules.
|
||||
if (tool_monitor_get_user_courses()){
|
||||
if (tool_monitor_can_subscribe()) {
|
||||
// Make them choose a course.
|
||||
$choose = true;
|
||||
} else {
|
||||
|
||||
@@ -75,12 +75,20 @@ function tool_monitor_extend_navigation_frontpage($navigation, $course, $context
|
||||
* @param context $coursecontext The context of the course
|
||||
*/
|
||||
function tool_monitor_extend_navigation_user_settings($navigation, $user, $usercontext, $course, $coursecontext) {
|
||||
global $USER, $SITE;
|
||||
global $USER, $PAGE;
|
||||
|
||||
// Don't bother doing needless calculations unless we are on the relevant pages.
|
||||
$onpreferencepage = $PAGE->url->compare(new moodle_url('/user/preferences.php'), URL_MATCH_BASE);
|
||||
$onmonitorpage = $PAGE->url->compare(new moodle_url('/admin/tool/monitor/index.php'), URL_MATCH_BASE);
|
||||
if (!$onpreferencepage && !$onmonitorpage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Don't show the setting if the event monitor isn't turned on. No access to other peoples subscriptions.
|
||||
if (get_config('tool_monitor', 'enablemonitor') && $USER->id == $user->id) {
|
||||
// Now let's check to see if the user has any courses / site rules that they can subscribe to.
|
||||
if ($courses = tool_monitor_get_user_courses()) {
|
||||
// We skip doing a check here if we are on the event monitor page as the check is done internally on that page.
|
||||
if ($onmonitorpage || tool_monitor_can_subscribe()) {
|
||||
$url = new moodle_url('/admin/tool/monitor/index.php');
|
||||
$subsnode = navigation_node::create(get_string('managesubscriptions', 'tool_monitor'), $url,
|
||||
navigation_node::TYPE_SETTING, null, 'monitor', new pix_icon('i/settings', ''));
|
||||
@@ -92,6 +100,19 @@ function tool_monitor_extend_navigation_user_settings($navigation, $user, $userc
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user has the capacity to subscribe to an event monitor anywhere.
|
||||
*
|
||||
* @return bool True if a capability in a course is found. False otherwise.
|
||||
*/
|
||||
function tool_monitor_can_subscribe() {
|
||||
if (has_capability('tool/monitor:subscribe', context_system::instance())) {
|
||||
return true;
|
||||
}
|
||||
$courses = get_user_capability_course('tool/monitor:subscribe', null, true, '', '', 1);
|
||||
return empty($courses) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of courses and also include 'Site' for site wide rules.
|
||||
*
|
||||
@@ -103,8 +124,11 @@ function tool_monitor_get_user_courses() {
|
||||
if (has_capability('tool/monitor:subscribe', context_system::instance())) {
|
||||
$options[0] = get_string('site');
|
||||
}
|
||||
if ($courses = get_user_capability_course('tool/monitor:subscribe', null, true, 'fullname, visible', $orderby)) {
|
||||
|
||||
$fields = 'fullname, visible, ctxid, ctxpath, ctxdepth, ctxlevel, ctxinstance';
|
||||
if ($courses = get_user_capability_course('tool/monitor:subscribe', null, true, $fields, $orderby)) {
|
||||
foreach ($courses as $course) {
|
||||
context_helper::preload_from_record($course);
|
||||
$coursectx = context_course::instance($course->id);
|
||||
if ($course->visible || has_capability('moodle/course:viewhiddencourses', $coursectx)) {
|
||||
$options[$course->id] = format_string($course->fullname, true, array('context' => $coursectx));
|
||||
|
||||
@@ -26,6 +26,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$plugin->version = 2016052305; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2016052306; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2016051900; // Requires this Moodle version.
|
||||
$plugin->component = 'tool_monitor'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -25,8 +25,12 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
// profiling tool, added to development
|
||||
if ((extension_loaded('xhprof') || extension_loaded('tideways')) && (!empty($CFG->profilingenabled) || !empty($CFG->earlyprofilingenabled))) {
|
||||
// Profiling tool, added to development.
|
||||
$hasextension = extension_loaded('tideways_xhprof');
|
||||
$hasextension = $hasextension || extension_loaded('tideways');
|
||||
$hasextension = $hasextension || extension_loaded('xhprof');
|
||||
$isenabled = !empty($CFG->profilingenabled) || !empty($CFG->earlyprofilingenabled);
|
||||
if ($hasextension && $isenabled) {
|
||||
$ADMIN->add('development', new admin_externalpage('toolprofiling', get_string('pluginname', 'tool_profiling'),
|
||||
"$CFG->wwwroot/$CFG->admin/tool/profiling/index.php", 'moodle/site:config'));
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/ajax","core/log","core/notification","core/templates","core/config","core/str"],function(a,b,c,d,e,f,g){var h=function(a,b){if(!a)return!1;var c="@template "+b,d=0,e=[];if(e=a.match(/{{!([\s\S]*?)}}/g),null!==e)for(d=0;d<e.length;d++){var f=e[d],g=f.indexOf(c);if(-1!==g){var h=g+c.length+1;return f=f.substr(h,f.length-2-h)}}return!1},i=function(b,f,i){g.get_string("templateselected","tool_templatelibrary",b).done(function(b){a('[data-region="displaytemplateheader"]').text(b)}).fail(d.exception);var j=h(f,b);j===!1&&(j=h(i,b)),j&&(f=j),a('[data-region="displaytemplatesource"]').text(f);var k=f.match(/Example context \(json\):([\s\S]*)/),l=!1;if(k){var m=k[1].trim();try{l=a.parseJSON(m)}catch(n){c.debug("Could not parse json example context for template."),c.debug(n)}}l?e.render(b,l).done(function(b,c){e.replaceNodeContents(a('[data-region="displaytemplateexample"]'),b,c)}).fail(d.exception):g.get_string("templatehasnoexample","tool_templatelibrary").done(function(b){a('[data-region="displaytemplateexample"]').text(b)}).fail(d.exception)},j=function(c){var e=c.split("/"),g=e.shift(),h=e.shift(),j=b.call([{methodname:"core_output_load_template",args:{component:g,template:h,themename:f.theme}},{methodname:"tool_templatelibrary_load_canonical_template",args:{component:g,template:h}}],!0,!1);a.when.apply(a,j).done(function(a,b){i(c,a,b)}).fail(d.exception)};return a('[data-region="list-templates"]').on("click","[data-templatename]",function(b){var c=a(this).data("templatename");b.preventDefault(),j(c)}),{}});
|
||||
define(["jquery","core/ajax","core/log","core/notification","core/templates","core/config","core/str"],function(a,b,c,d,e,f,g){var h=function(a,b){if(!a)return!1;var c="@template "+b,d=0,e=[];if(e=a.match(/{{!([\s\S]*?)}}/g),null!==e)for(d=0;d<e.length;d++){var f=e[d],g=f.indexOf(c);if(g!==-1){var h=g+c.length+1;return f=f.substr(h,f.length-2-h)}}return!1},i=function(b,f,i){g.get_string("templateselected","tool_templatelibrary",b).done(function(b){a('[data-region="displaytemplateheader"]').text(b)}).fail(d.exception);var j=h(f,b);j===!1&&(j=h(i,b)),j&&(f=j),a('[data-region="displaytemplatesource"]').text(f);var k=f.match(/Example context \(json\):([\s\S]*)/),l=!1;if(k){var m=k[1].trim();try{l=a.parseJSON(m)}catch(n){c.debug("Could not parse json example context for template."),c.debug(n)}}l?e.render(b,l).done(function(b,c){e.replaceNodeContents(a('[data-region="displaytemplateexample"]'),b,c)}).fail(d.exception):g.get_string("templatehasnoexample","tool_templatelibrary").done(function(b){a('[data-region="displaytemplateexample"]').text(b)}).fail(d.exception)},j=function(c){var e=c.split("/"),g=e.shift(),h=e.shift(),j=b.call([{methodname:"core_output_load_template",args:{component:g,template:h,themename:f.theme}},{methodname:"tool_templatelibrary_load_canonical_template",args:{component:g,template:h}}],!0,!1);a.when.apply(a,j).done(function(a,b){i(c,a,b)}).fail(d.exception)};return a('[data-region="list-templates"]').on("click","[data-templatename]",function(b){var c=a(this).data("templatename");b.preventDefault(),j(c)}),{}});
|
||||
@@ -49,7 +49,7 @@ It\'s highly recommended to be running the latest (+ version) available of your
|
||||
This functionality doesn\'t perform any action against the DB (just reads from it), so can be safely executed at any moment.';
|
||||
$string['confirmcheckforeignkeys'] = 'This functionality will search for potential violations of the foreign keys defined in the install.xml definitions. (Moodle does not currently generate actual foreign key constraints in the database, which is why invalid data may be present.)
|
||||
|
||||
It\'s highly recommended to be running the latest (+ version) available of your Moodle release before executing the search of missing indexes.
|
||||
It\'s highly recommended to be running the latest (+ version) available of your Moodle release before executing the search for potential violations of the foreign keys.
|
||||
|
||||
This functionality doesn\'t perform any action against the DB (just reads from it), so can be safely executed at any moment.';
|
||||
$string['confirmcheckindexes'] = 'This functionality will search for potential missing indexes in your Moodle server, generating (but not executing!) automatically the needed SQL statements to keep everything updated.
|
||||
|
||||
+12
-12
@@ -3187,6 +3187,18 @@ class CAS_Client
|
||||
false/*$no_response*/, true/*$bad_response*/, $text_response
|
||||
);
|
||||
$result = false;
|
||||
} else if ( $tree_response->getElementsByTagName("authenticationFailure")->length != 0) {
|
||||
// authentication failed, extract the error code and message and throw exception
|
||||
$auth_fail_list = $tree_response
|
||||
->getElementsByTagName("authenticationFailure");
|
||||
throw new CAS_AuthenticationException(
|
||||
$this, 'Ticket not validated', $validate_url,
|
||||
false/*$no_response*/, false/*$bad_response*/,
|
||||
$text_response,
|
||||
$auth_fail_list->item(0)->getAttribute('code')/*$err_code*/,
|
||||
trim($auth_fail_list->item(0)->nodeValue)/*$err_msg*/
|
||||
);
|
||||
$result = false;
|
||||
} else if ($tree_response->getElementsByTagName("authenticationSuccess")->length != 0) {
|
||||
// authentication succeded, extract the user name
|
||||
$success_elements = $tree_response
|
||||
@@ -3227,18 +3239,6 @@ class CAS_Client
|
||||
$result = true;
|
||||
}
|
||||
}
|
||||
} else if ( $tree_response->getElementsByTagName("authenticationFailure")->length != 0) {
|
||||
// authentication succeded, extract the error code and message
|
||||
$auth_fail_list = $tree_response
|
||||
->getElementsByTagName("authenticationFailure");
|
||||
throw new CAS_AuthenticationException(
|
||||
$this, 'Ticket not validated', $validate_url,
|
||||
false/*$no_response*/, false/*$bad_response*/,
|
||||
$text_response,
|
||||
$auth_fail_list->item(0)->getAttribute('code')/*$err_code*/,
|
||||
trim($auth_fail_list->item(0)->nodeValue)/*$err_msg*/
|
||||
);
|
||||
$result = false;
|
||||
} else {
|
||||
throw new CAS_AuthenticationException(
|
||||
$this, 'Ticket not validated', $validate_url,
|
||||
|
||||
@@ -2,3 +2,4 @@ Description of phpCAS 1.3.4 library import
|
||||
|
||||
* downloaded from http://downloads.jasig.org/cas-clients/php/current/
|
||||
|
||||
* MDL-59456 phpCAS library has been patched because of an authentication bypass security vulnerability.
|
||||
|
||||
@@ -154,6 +154,7 @@ class auth_plugin_cas extends auth_plugin_ldap {
|
||||
$frm = new stdClass();
|
||||
$frm->username = phpCAS::getUser();
|
||||
$frm->password = 'passwdCas';
|
||||
$frm->logintoken = \core\session\manager::get_login_token();
|
||||
|
||||
// Redirect to a course if multi-auth is activated, authCAS is set to CAS and the courseid is specified.
|
||||
if ($this->config->multiauth && !empty($courseid)) {
|
||||
@@ -167,6 +168,7 @@ class auth_plugin_cas extends auth_plugin_ldap {
|
||||
$frm = new stdClass();
|
||||
$frm->username = 'guest';
|
||||
$frm->password = 'guest';
|
||||
$frm->logintoken = \core\session\manager::get_login_token();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+24
-2
@@ -99,9 +99,31 @@ if (!isset($config->removeuser)) {
|
||||
$yesno = array( get_string('no'), get_string('yes') );
|
||||
|
||||
$disabled = '';
|
||||
if (!ldap_paged_results_supported($config->ldap_version)) {
|
||||
$pagedresultssupported = false;
|
||||
if ($config->host_url !== '') {
|
||||
/**
|
||||
* We try to connect each and every time we open the config, because we want to set the Page
|
||||
* Size setting as enabled or disabled depending on the configured LDAP server supporting
|
||||
* pagination or not, and to notify the user about it. If the user changed the LDAP server (or
|
||||
* the LDAP protocol version) last time, it might happen that paged results are no longer
|
||||
* available and we want to show that to the user the next time she goes to the settings page.
|
||||
*/
|
||||
try {
|
||||
$ldapconn = $this->ldap_connect();
|
||||
$pagedresultssupported = ldap_paged_results_supported($config->ldap_version, $ldapconn);
|
||||
} catch (Exception $e) {
|
||||
// If we couldn't connect and get the supported options, we can only assume we don't support paged results.
|
||||
$pagedresultssupported = false;
|
||||
}
|
||||
}
|
||||
/* Make sure we only disable the paged result size setting and show the notification about it if
|
||||
* there is a configured server that we tried to contact. Othersiwe, if someone's LDAP server does
|
||||
* support paged results, they won't be able to turn it on the first time they set it up (because
|
||||
* the field will be disabled).
|
||||
*/
|
||||
if (($config->host_url !== '') && (!$pagedresultssupported)) {
|
||||
$disabled = ' disabled="disabled"';
|
||||
echo $OUTPUT->notification(get_string('pagedresultsnotsupp', 'auth_ldap'));
|
||||
echo $OUTPUT->notification(get_string('pagedresultsnotsupp', 'auth_ldap'), \core\output\notification::NOTIFY_INFO);
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
+4
-3
@@ -695,7 +695,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
array_push($contexts, $this->config->create_context);
|
||||
}
|
||||
|
||||
$ldap_pagedresults = ldap_paged_results_supported($this->config->ldap_version);
|
||||
$ldap_pagedresults = ldap_paged_results_supported($this->config->ldap_version, $ldapconnection);
|
||||
$ldap_cookie = '';
|
||||
foreach ($contexts as $context) {
|
||||
$context = trim($context);
|
||||
@@ -1540,7 +1540,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
}
|
||||
|
||||
$ldap_cookie = '';
|
||||
$ldap_pagedresults = ldap_paged_results_supported($this->config->ldap_version);
|
||||
$ldap_pagedresults = ldap_paged_results_supported($this->config->ldap_version, $ldapconnection);
|
||||
foreach ($contexts as $context) {
|
||||
$context = trim($context);
|
||||
if (empty($context)) {
|
||||
@@ -1762,7 +1762,8 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
|
||||
// Here we want to trigger the whole authentication machinery
|
||||
// to make sure no step is bypassed...
|
||||
$user = authenticate_user_login($username, $key);
|
||||
$reason = null;
|
||||
$user = authenticate_user_login($username, $key, false, $reason, false);
|
||||
if ($user) {
|
||||
complete_user_login($user);
|
||||
|
||||
|
||||
+24
-2
@@ -117,9 +117,31 @@ $fastpathoptions = array(AUTH_NTLM_FASTPATH_YESFORM => get_string('auth_ntlmsso_
|
||||
AUTH_NTLM_FASTPATH_ATTEMPT => get_string('auth_ntlmsso_ie_fastpath_attempt', 'auth_ldap'));
|
||||
|
||||
$disabled = '';
|
||||
if (!ldap_paged_results_supported($config->ldap_version)) {
|
||||
$pagedresultssupported = false;
|
||||
if ($config->host_url !== '') {
|
||||
/**
|
||||
* We try to connect each and every time we open the config, because we want to set the Page
|
||||
* Size setting as enabled or disabled depending on the configured LDAP server supporting
|
||||
* pagination or not, and to notify the user about it. If the user changed the LDAP server (or
|
||||
* the LDAP protocol version) last time, it might happen that paged results are no longer
|
||||
* available and we want to show that to the user the next time she goes to the settings page.
|
||||
*/
|
||||
try {
|
||||
$ldapconn = $this->ldap_connect();
|
||||
$pagedresultssupported = ldap_paged_results_supported($config->ldap_version, $ldapconn);
|
||||
} catch (Exception $e) {
|
||||
// If we couldn't connect and get the supported options, we can only assume we don't support paged results.
|
||||
$pagedresultssupported = false;
|
||||
}
|
||||
}
|
||||
/* Make sure we only disable the paged result size setting and show the notification about it if
|
||||
* there is a configured server that we tried to contact. Othersiwe, if someone's LDAP server does
|
||||
* support paged results, they won't be able to turn it on the first time they set it up (because
|
||||
* the field will be disabled).
|
||||
*/
|
||||
if (($config->host_url !== '') && (!$pagedresultssupported)) {
|
||||
$disabled = ' disabled="disabled"';
|
||||
echo $OUTPUT->notification(get_string('pagedresultsnotsupp', 'auth_ldap'));
|
||||
echo $OUTPUT->notification(get_string('pagedresultsnotsupp', 'auth_ldap'), \core\output\notification::NOTIFY_INFO);
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -132,7 +132,7 @@ $string['ntlmsso_attempting'] = 'Attempting Single Sign On via NTLM...';
|
||||
$string['ntlmsso_failed'] = 'Auto-login failed, try the normal login page...';
|
||||
$string['ntlmsso_isdisabled'] = 'NTLM SSO is disabled.';
|
||||
$string['ntlmsso_unknowntype'] = 'Unknown ntlmsso type!';
|
||||
$string['pagedresultsnotsupp'] = 'LDAP paged results not supported (either your PHP version lacks support or you have configured Moodle to use LDAP protocol version 2)';
|
||||
$string['pagedresultsnotsupp'] = 'LDAP paged results not supported (either your PHP version lacks support, you have configured Moodle to use LDAP protocol version 2 or Moodle cannot contact your LDAP server to see if paged support is available.)';
|
||||
$string['pagesize'] = 'Make sure this value is smaller than your LDAP server result set size limit (the maximum number of entries that can be returned in a single query)';
|
||||
$string['pagesize_key'] = 'Page Size';
|
||||
$string['pluginname'] = 'LDAP server';
|
||||
|
||||
@@ -49,9 +49,10 @@
|
||||
$frm->password = generate_password(8);
|
||||
|
||||
/// Check if the user has actually submitted login data to us
|
||||
$reason = null;
|
||||
|
||||
if ($shibbolethauth->user_login($frm->username, $frm->password)
|
||||
&& $user = authenticate_user_login($frm->username, $frm->password)) {
|
||||
&& $user = authenticate_user_login($frm->username, $frm->password, false, $reason, false)) {
|
||||
complete_user_login($user);
|
||||
|
||||
if (user_not_fully_set_up($USER, true)) {
|
||||
|
||||
@@ -54,6 +54,7 @@ if ($show_instructions) {
|
||||
</div>
|
||||
<form action="../../login/index.php" method="post" id="guestlogin">
|
||||
<div class="guestform">
|
||||
<input type="hidden" name="logintoken" value="<?php echo s(\core\session\manager::get_login_token()); ?>" />
|
||||
<input type="hidden" name="username" value="guest" />
|
||||
<input type="hidden" name="password" value="guest" />
|
||||
<input type="submit" value="<?php print_string("loginguest") ?>" />
|
||||
|
||||
@@ -69,7 +69,19 @@ $PAGE->https_required();
|
||||
$PAGE->set_heading($site->fullname);
|
||||
|
||||
echo $OUTPUT->header();
|
||||
include("index_form.html");
|
||||
|
||||
if (isloggedin() and !isguestuser()) {
|
||||
// Prevent logging when already logged in, we do not want them to relogin by accident because sesskey would be changed.
|
||||
echo $OUTPUT->box_start();
|
||||
$params = array('sesskey' => sesskey(), 'loginpage' => 1);
|
||||
$logout = new single_button(new moodle_url('/login/logout.php', $params), get_string('logout'), 'post');
|
||||
$continue = new single_button(new moodle_url('/'), get_string('cancel'), 'get');
|
||||
echo $OUTPUT->confirm(get_string('alreadyloggedin', 'error', fullname($USER)), $logout, $continue);
|
||||
echo $OUTPUT->box_end();
|
||||
} else {
|
||||
include("index_form.html");
|
||||
}
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
|
||||
|
||||
@@ -26,13 +26,14 @@ if (!is_enabled_auth('shibboleth')) {
|
||||
$inputstream = file_get_contents("php://input");
|
||||
if ($action == 'logout' && !empty($redirect)) {
|
||||
|
||||
if ($USER->auth == 'shibboleth') {
|
||||
// Logout out user from application.
|
||||
if (isloggedin($USER) && $USER->auth == 'shibboleth') {
|
||||
// Logout user from application.
|
||||
require_logout();
|
||||
// Finally, send user to the return URL.
|
||||
redirect($redirect);
|
||||
}
|
||||
|
||||
// Finally, send user to the return URL.
|
||||
redirect($redirect);
|
||||
|
||||
} else if (!empty($inputstream)) {
|
||||
|
||||
// Back channel logout.
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
This files describes API changes in /auth/* - plugins,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 3.1.15 ===
|
||||
|
||||
* Login forms generated from Moodle must include a login token to protect automated logins. See \core\session\manager::get_login_token().
|
||||
|
||||
=== 3.0 ===
|
||||
|
||||
* login_signup_form::signup_captcha_enabled() now calls is_captcha_enabled() from the current auth plugin instead of from auth_email
|
||||
|
||||
@@ -2666,7 +2666,7 @@ class restore_calendarevents_structure_step extends restore_structure_step {
|
||||
'courseid' => $this->get_courseid(),
|
||||
'groupid' => $data->groupid,
|
||||
'userid' => $data->userid,
|
||||
'repeatid' => $data->repeatid,
|
||||
'repeatid' => $this->get_mappingid('event', $data->repeatid),
|
||||
'modulename' => $data->modulename,
|
||||
'eventtype' => $data->eventtype,
|
||||
'timestart' => $this->apply_date_offset($data->timestart),
|
||||
@@ -2684,18 +2684,27 @@ class restore_calendarevents_structure_step extends restore_structure_step {
|
||||
FROM {event}
|
||||
WHERE " . $DB->sql_compare_text('name', 255) . " = " . $DB->sql_compare_text('?', 255) . "
|
||||
AND courseid = ?
|
||||
AND repeatid = ?
|
||||
AND modulename = ?
|
||||
AND timestart = ?
|
||||
AND timeduration = ?
|
||||
AND " . $DB->sql_compare_text('description', 255) . " = " . $DB->sql_compare_text('?', 255);
|
||||
$arg = array ($params['name'], $params['courseid'], $params['repeatid'], $params['modulename'], $params['timestart'], $params['timeduration'], $params['description']);
|
||||
$arg = array ($params['name'], $params['courseid'], $params['modulename'], $params['timestart'], $params['timeduration'], $params['description']);
|
||||
$result = $DB->record_exists_sql($sql, $arg);
|
||||
if (empty($result)) {
|
||||
$newitemid = $DB->insert_record('event', $params);
|
||||
$this->set_mapping('event', $oldid, $newitemid);
|
||||
$this->set_mapping('event_description', $oldid, $newitemid, $restorefiles);
|
||||
}
|
||||
// With repeating events, each event has the repeatid pointed at the first occurrence.
|
||||
// Since the repeatid will be empty when the first occurrence is restored,
|
||||
// Get the repeatid from the second occurrence of the repeating event and use that to update the first occurrence.
|
||||
// Then keep a list of repeatids so we only perform this update once.
|
||||
static $repeatids = array();
|
||||
if (!empty($params['repeatid']) && !in_array($params['repeatid'], $repeatids)) {
|
||||
// This entry is repeated so the repeatid field must be set.
|
||||
$DB->set_field('event', 'repeatid', $params['repeatid'], array('id' => $params['repeatid']));
|
||||
$repeatids[] = $params['repeatid'];
|
||||
}
|
||||
|
||||
}
|
||||
protected function after_execute() {
|
||||
|
||||
+42
-28
@@ -46,47 +46,61 @@ class edit_backpack_form extends moodleform {
|
||||
$mform->addElement('header', 'backpackheader', get_string('backpackconnection', 'badges'));
|
||||
$mform->addHelpButton('backpackheader', 'backpackconnection', 'badges');
|
||||
$mform->addElement('static', 'url', get_string('url'), BADGE_BACKPACKURL);
|
||||
$status = html_writer::tag('span', get_string('notconnected', 'badges'),
|
||||
array('class' => 'notconnected', 'id' => 'connection-status'));
|
||||
$mform->addElement('static', 'status', get_string('status'), $status);
|
||||
|
||||
$nojs = html_writer::tag('noscript', get_string('error:personaneedsjs', 'badges'),
|
||||
array('class' => 'notconnected'));
|
||||
$personadiv = $OUTPUT->container($nojs, null, 'persona-container');
|
||||
|
||||
$mform->addElement('static', 'persona', '', $personadiv);
|
||||
$mform->addHelpButton('persona', 'personaconnection', 'badges');
|
||||
|
||||
$PAGE->requires->js(new moodle_url('https://login.persona.org/include.js'));
|
||||
$PAGE->requires->js('/badges/backpack.js');
|
||||
$PAGE->requires->js_init_call('badges_init_persona_login_button', null, false);
|
||||
$PAGE->requires->strings_for_js(array('error:backpackloginfailed', 'signinwithyouremail',
|
||||
'error:noassertion', 'error:connectionunknownreason', 'error:badjson', 'connecting',
|
||||
'notconnected'), 'badges');
|
||||
|
||||
$mform->addElement('hidden', 'userid', $USER->id);
|
||||
$mform->setType('userid', PARAM_INT);
|
||||
|
||||
$mform->addElement('hidden', 'backpackurl', BADGE_BACKPACKURL);
|
||||
$mform->setType('backpackurl', PARAM_URL);
|
||||
|
||||
if (isset($this->_customdata['email'])) {
|
||||
// Email will be passed in when we're in the process of verifying the user's email address,
|
||||
// so set the connection status, lock the email field, and provide options to resend the verification
|
||||
// email or cancel the verification process entirely and start over.
|
||||
$status = html_writer::tag('span', get_string('backpackemailverificationpending', 'badges'),
|
||||
array('class' => 'notconnected', 'id' => 'connection-status'));
|
||||
$mform->addElement('static', 'status', get_string('status'), $status);
|
||||
$mform->addElement('hidden', 'email', $this->_customdata['email']);
|
||||
$mform->setType('email', PARAM_RAW_TRIMMED);
|
||||
$mform->hardFreeze(['email']);
|
||||
$status = html_writer::tag('span', $this->_customdata['email'], []);
|
||||
$mform->addElement('static', 'emailverify', get_string('email'), $status);
|
||||
$buttonarray = [];
|
||||
$buttonarray[] = &$mform->createElement('submit', 'submitbutton',
|
||||
get_string('backpackconnectionresendemail', 'badges'));
|
||||
$buttonarray[] = &$mform->createElement('submit', 'revertbutton',
|
||||
get_string('backpackconnectioncancelattempt', 'badges'));
|
||||
$mform->addGroup($buttonarray, 'buttonar', '', [''], false);
|
||||
$mform->closeHeaderBefore('buttonar');
|
||||
} else {
|
||||
// Email isn't present, so provide an input element to get it and a button to start the verification process.
|
||||
$status = html_writer::tag('span', get_string('notconnected', 'badges'),
|
||||
array('class' => 'notconnected', 'id' => 'connection-status'));
|
||||
$mform->addElement('static', 'status', get_string('status'), $status);
|
||||
$mform->addElement('text', 'email', get_string('email'), 'maxlength="100" size="30"');
|
||||
$mform->addHelpButton('email', 'backpackemail', 'badges');
|
||||
$mform->addRule('email', get_string('required'), 'required', null, 'client');
|
||||
$mform->setType('email', PARAM_RAW_TRIMMED);
|
||||
$this->add_action_buttons(false, get_string('backpackconnectionconnect', 'badges'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates form data
|
||||
*/
|
||||
public function validation($data, $files) {
|
||||
global $DB;
|
||||
$errors = parent::validation($data, $files);
|
||||
|
||||
$check = new stdClass();
|
||||
$check->backpackurl = $data['backpackurl'];
|
||||
$check->email = $data['email'];
|
||||
// We don't need to verify the email address if we're clearing a pending email verification attempt.
|
||||
if (!isset($data['revertbutton'])) {
|
||||
$check = new stdClass();
|
||||
$check->backpackurl = BADGE_BACKPACKURL;
|
||||
$check->email = $data['email'];
|
||||
|
||||
$bp = new OpenBadgesBackpackHandler($check);
|
||||
$request = $bp->curl_request('user');
|
||||
if (isset($request->status) && $request->status == 'missing') {
|
||||
$errors['email'] = get_string('error:nosuchuser', 'badges');
|
||||
$bp = new OpenBadgesBackpackHandler($check);
|
||||
$request = $bp->curl_request('user');
|
||||
if (isset($request->status) && $request->status == 'missing') {
|
||||
$errors['email'] = get_string('error:nosuchuser', 'badges');
|
||||
} else if (!isset($request->status) || $request->status !== 'okay') {
|
||||
$errors['email'] = get_string('backpackconnectionunexpectedresult', 'badges');
|
||||
}
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Endpoint for the verification email link.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage badges
|
||||
* @copyright 2016 Jake Dallimore <jrhdallimore@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
require_once(__DIR__ . '/../config.php');
|
||||
require_once($CFG->libdir . '/badgeslib.php');
|
||||
require_once(__DIR__ . '/lib/backpacklib.php');
|
||||
|
||||
$data = optional_param('data', '', PARAM_RAW);
|
||||
require_login();
|
||||
$PAGE->set_url('/badges/openbackpackemailverify.php');
|
||||
$PAGE->set_context(context_user::instance($USER->id));
|
||||
$redirect = '/badges/mybackpack.php';
|
||||
|
||||
// Confirm the secret and create the backpack connection.
|
||||
$storedsecret = get_user_preferences('badges_email_verify_secret');
|
||||
if (!is_null($storedsecret)) {
|
||||
if ($data === $storedsecret) {
|
||||
$storedemail = get_user_preferences('badges_email_verify_address');
|
||||
|
||||
$data = new stdClass();
|
||||
$data->backpackurl = BADGE_BACKPACKURL;
|
||||
$data->email = $storedemail;
|
||||
$bp = new OpenBadgesBackpackHandler($data);
|
||||
|
||||
// Make sure we have all the required information before trying to save the connection.
|
||||
$backpackuser = $bp->curl_request('user');
|
||||
if (isset($backpackuser->status) && $backpackuser->status === 'okay' && isset($backpackuser->userId)) {
|
||||
$backpackuid = $backpackuser->userId;
|
||||
} else {
|
||||
redirect(new moodle_url($redirect), get_string('backpackconnectionunexpectedresult', 'badges'),
|
||||
null, \core\output\notification::NOTIFY_ERROR);
|
||||
}
|
||||
|
||||
$obj = new stdClass();
|
||||
$obj->userid = $USER->id;
|
||||
$obj->email = $data->email;
|
||||
$obj->backpackurl = $data->backpackurl;
|
||||
$obj->backpackuid = $backpackuid;
|
||||
$obj->autosync = 0;
|
||||
$obj->password = '';
|
||||
$DB->insert_record('badge_backpack', $obj);
|
||||
|
||||
// Remove the verification vars and redirect to the mypackpack page.
|
||||
unset_user_preference('badges_email_verify_secret');
|
||||
unset_user_preference('badges_email_verify_address');
|
||||
redirect(new moodle_url($redirect), get_string('backpackemailverifysuccess', 'badges'),
|
||||
null, \core\output\notification::NOTIFY_SUCCESS);
|
||||
} else {
|
||||
// Stored secret doesn't match the supplied secret. Take user back to the mybackpack page and present a warning message.
|
||||
redirect(new moodle_url($redirect), get_string('backpackemailverifytokenmismatch', 'badges'),
|
||||
null, \core\output\notification::NOTIFY_ERROR);
|
||||
}
|
||||
} else {
|
||||
// Stored secret is null. Either the email address has already been verified, or there is no record of a verification attempt
|
||||
// for the current user. Either way, just redirect to the mybackpack page.
|
||||
redirect(new moodle_url($redirect));
|
||||
}
|
||||
@@ -117,3 +117,45 @@ class OpenBadgesBackpackHandler {
|
||||
return $this->backpack;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and send a verification email to the email address supplied.
|
||||
*
|
||||
* Since we're not sending this email to a user, email_to_user can't be used
|
||||
* but this function borrows largely the code from that process.
|
||||
*
|
||||
* @param string $email the email address to send the verification email to.
|
||||
* @return true if the email was sent successfully, false otherwise.
|
||||
*/
|
||||
function send_verification_email($email) {
|
||||
global $DB, $USER;
|
||||
|
||||
// Store a user secret (badges_email_verify_secret) and the address (badges_email_verify_address) as users prefs.
|
||||
// The address will be used by edit_backpack_form for display during verification and to facilitate the resending
|
||||
// of verification emails to said address.
|
||||
$secret = random_string(15);
|
||||
set_user_preference('badges_email_verify_secret', $secret);
|
||||
set_user_preference('badges_email_verify_address', $email);
|
||||
|
||||
// To, from.
|
||||
$tempuser = $DB->get_record('user', array('id' => $USER->id), '*', MUST_EXIST);
|
||||
$tempuser->email = $email;
|
||||
$noreplyuser = core_user::get_noreply_user();
|
||||
|
||||
// Generate the verification email body.
|
||||
$verificationurl = '/badges/backpackemailverify.php';
|
||||
$verificationurl = new moodle_url($verificationurl);
|
||||
$verificationpath = $verificationurl->out(false);
|
||||
|
||||
$site = get_site();
|
||||
$args = new stdClass();
|
||||
$args->link = $verificationpath . '?data='. $secret;
|
||||
$args->sitename = $site->fullname;
|
||||
$args->admin = generate_email_signoff();
|
||||
|
||||
$messagesubject = get_string('backpackemailverifyemailsubject', 'badges', $site->fullname);
|
||||
$messagetext = get_string('backpackemailverifyemailbody', 'badges', $args);
|
||||
$messagehtml = text_to_html($messagetext, false, false, true);
|
||||
|
||||
return email_to_user($tempuser, $noreplyuser, $messagesubject, $messagetext, $messagehtml);
|
||||
}
|
||||
|
||||
+29
-13
@@ -110,23 +110,39 @@ if ($backpack) {
|
||||
}
|
||||
} else {
|
||||
// If backpack is not connected, need to connect first.
|
||||
$form = new edit_backpack_form();
|
||||
// To create a new connection to the backpack, first we need to verify the user's email address:
|
||||
// 1. User enters email and clicks 'Connect to backpack'.
|
||||
// 2. After cross-checking the email address against the backpack provider, an email is sent to the specified address,
|
||||
// and the email and secret are stored in user preferences. These will be cleared upon successful verification.
|
||||
// 3. User clicks verification link in the email to confirm the backpack connection.
|
||||
// 4. User redirected to the mybackpack page.
|
||||
// While the verification process is pending, the edit_backpack_form form will present the user with options to resend the
|
||||
// verification email, and to cancel the current verification attempt and start over.
|
||||
|
||||
// To pass through the current state of the verification attempt to the form.
|
||||
$params['email'] = get_user_preferences('badges_email_verify_address');
|
||||
|
||||
$form = new edit_backpack_form(new moodle_url('/badges/mybackpack.php'), $params);
|
||||
if ($form->is_cancelled()) {
|
||||
redirect(new moodle_url('/badges/mybadges.php'));
|
||||
} else if ($data = $form->get_data()) {
|
||||
$bp = new OpenBadgesBackpackHandler($data);
|
||||
|
||||
$obj = new stdClass();
|
||||
$obj->userid = $data->userid;
|
||||
$obj->email = $data->email;
|
||||
$obj->backpackurl = $data->backpackurl;
|
||||
$obj->backpackuid = $bp->curl_request('user')->userId;
|
||||
$obj->autosync = 0;
|
||||
$obj->password = '';
|
||||
$DB->insert_record('badge_backpack', $obj);
|
||||
|
||||
redirect(new moodle_url('/badges/mybackpack.php'));
|
||||
// The form may have been submitted under one of the following circumstances:
|
||||
// 1. After clicking 'Connect to backpack'. We'll have $data->email.
|
||||
// 2. After clicking 'Resend verification email'. We'll have $data->email.
|
||||
// 3. After clicking 'Connect using a different email' to cancel the verification process. We'll have $data->revertbutton.
|
||||
if (isset($data->revertbutton)) {
|
||||
unset_user_preference('badges_email_verify_secret');
|
||||
unset_user_preference('badges_email_verify_address');
|
||||
redirect(new moodle_url('/badges/mybackpack.php'));
|
||||
} else if (isset($data->email)) {
|
||||
if (send_verification_email($data->email)) {
|
||||
redirect(new moodle_url('/badges/mybackpack.php'),
|
||||
get_string('backpackemailverifypending', 'badges', $data->email),
|
||||
null, \core\output\notification::NOTIFY_INFO);
|
||||
} else {
|
||||
print_error ('backpackcannotsendverification', 'badges');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,7 @@ Feature: Award badges
|
||||
And I press "Enable access"
|
||||
And I press "Continue"
|
||||
And I click on "Admin User" "link"
|
||||
And I follow "Profile" in the open menu
|
||||
And I follow "Edit profile"
|
||||
And I open my profile in edit mode
|
||||
And I expand all fieldsets
|
||||
And I set the field "Phone" to "123456789"
|
||||
And I press "Update profile"
|
||||
|
||||
@@ -673,7 +673,7 @@ class block_activity_results extends block_base {
|
||||
|
||||
$o = html_writer::start_tag('h3');
|
||||
$o .= html_writer::link(new moodle_url('/mod/'.$activity->itemmodule.'/view.php',
|
||||
array('id' => $cm->id)), $activity->itemname);
|
||||
array('id' => $cm->id)), format_string(($activity->itemname), true, ['context' => context_module::instance($cm->id)]));
|
||||
$o .= html_writer::end_tag('h3');
|
||||
return $o;
|
||||
}
|
||||
@@ -690,4 +690,4 @@ class block_activity_results extends block_base {
|
||||
return $scale;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ class block_activity_results_edit_form extends block_edit_form {
|
||||
$sql = 'SELECT id, itemname FROM {grade_items} WHERE courseid = ? and itemtype = ? and (gradetype = ? or gradetype = ?)';
|
||||
$params = array($this->page->course->id, 'mod', GRADE_TYPE_VALUE, GRADE_TYPE_SCALE);
|
||||
$activities = $DB->get_records_sql_menu($sql, $params);
|
||||
core_collator::asort($activities);
|
||||
|
||||
if (empty($activities)) {
|
||||
$mform->addElement('static', 'noactivitieswarning', get_string('config_select_activity', 'block_activity_results'),
|
||||
|
||||
@@ -153,7 +153,8 @@ class block_community_renderer extends plugin_renderer_base {
|
||||
array('class' => 'hubcourseuserinfo'));
|
||||
|
||||
//create course content related information html
|
||||
$course->subject = get_string($course->subject, 'edufields');
|
||||
$course->subject = (get_string_manager()->string_exists($course->subject, 'edufields')) ?
|
||||
get_string($course->subject, 'edufields') : get_string('none');
|
||||
$course->audience = get_string('audience' . $course->audience, 'hub');
|
||||
$course->educationallevel = get_string('edulevel' . $course->educationallevel, 'hub');
|
||||
$coursecontentinfo = '';
|
||||
|
||||
@@ -64,7 +64,7 @@ class block_course_overview extends block_base {
|
||||
$content = array();
|
||||
|
||||
$updatemynumber = optional_param('mynumber', -1, PARAM_INT);
|
||||
if ($updatemynumber >= 0) {
|
||||
if ($updatemynumber >= 0 && optional_param('sesskey', '', PARAM_RAW) && confirm_sesskey()) {
|
||||
block_course_overview_update_mynumber($updatemynumber);
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ function block_course_overview_get_myorder() {
|
||||
// If preference was not found, look in the old location and convert if found.
|
||||
$order = array();
|
||||
if ($value = get_user_preferences('course_overview_course_order')) {
|
||||
$order = unserialize($value);
|
||||
$order = unserialize_array($value);
|
||||
block_course_overview_update_myorder($order);
|
||||
unset_user_preference('course_overview_course_order');
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ class block_course_overview_renderer extends plugin_renderer_base {
|
||||
for ($i = 1; $i <= $max; $i++) {
|
||||
$options[$i] = $i;
|
||||
}
|
||||
$url = new moodle_url('/my/index.php');
|
||||
$url = new moodle_url('/my/index.php', ['sesskey' => sesskey()]);
|
||||
$select = new single_select($url, 'mynumber', $options, block_course_overview_get_max_user_courses(), array());
|
||||
$select->set_label(get_string('numtodisplay', 'block_course_overview'));
|
||||
$output .= $this->output->render($select);
|
||||
|
||||
+45
-26
@@ -50,6 +50,12 @@ class block_edit_form extends moodleform {
|
||||
*/
|
||||
public $page;
|
||||
|
||||
/**
|
||||
* Defaults set in set_data() that need to be returned in get_data() if form elements were not created
|
||||
* @var array
|
||||
*/
|
||||
protected $defaults = [];
|
||||
|
||||
function __construct($actionurl, $block, $page) {
|
||||
global $CFG;
|
||||
$this->block = $block;
|
||||
@@ -92,9 +98,6 @@ class block_edit_form extends moodleform {
|
||||
}
|
||||
|
||||
$parentcontext = context::instance_by_id($this->block->instance->parentcontextid);
|
||||
$mform->addElement('hidden', 'bui_parentcontextid', $parentcontext->id);
|
||||
$mform->setType('bui_parentcontextid', PARAM_INT);
|
||||
|
||||
$mform->addElement('static', 'bui_homecontext', get_string('createdat', 'block'), $parentcontext->get_context_name());
|
||||
$mform->addHelpButton('bui_homecontext', 'createdat', 'block');
|
||||
|
||||
@@ -109,21 +112,9 @@ class block_edit_form extends moodleform {
|
||||
// implies one (and only one) harcoded page-type that will be set later
|
||||
// when processing the form data at {@link block_manager::process_url_edit()}
|
||||
|
||||
// There are some conditions to check related to contexts
|
||||
$ctxconditions = $this->page->context->contextlevel == CONTEXT_COURSE &&
|
||||
$this->page->context->instanceid == get_site()->id;
|
||||
// And also some pagetype conditions
|
||||
$pageconditions = isset($bits[0]) && isset($bits[1]) && $bits[0] == 'site' && $bits[1] == 'index';
|
||||
// So now we can be 100% sure if edition is happening at frontpage
|
||||
$editingatfrontpage = $ctxconditions && $pageconditions;
|
||||
|
||||
// Let the form to know about that, can be useful later
|
||||
$mform->addElement('hidden', 'bui_editingatfrontpage', (int)$editingatfrontpage);
|
||||
$mform->setType('bui_editingatfrontpage', PARAM_INT);
|
||||
|
||||
// Front page, show the page-contexts element and set $pagetypelist to 'any page' (*)
|
||||
// as unique option. Processign the form will do any change if needed
|
||||
if ($editingatfrontpage) {
|
||||
if ($this->is_editing_the_frontpage()) {
|
||||
$contextoptions = array();
|
||||
$contextoptions[BUI_CONTEXTS_FRONTPAGE_ONLY] = get_string('showonfrontpageonly', 'block');
|
||||
$contextoptions[BUI_CONTEXTS_FRONTPAGE_SUBS] = get_string('showonfrontpageandsubs', 'block');
|
||||
@@ -135,16 +126,13 @@ class block_edit_form extends moodleform {
|
||||
// Any other system context block, hide the page-contexts element,
|
||||
// it's always system-wide BUI_CONTEXTS_ENTIRE_SITE
|
||||
} else if ($parentcontext->contextlevel == CONTEXT_SYSTEM) {
|
||||
$mform->addElement('hidden', 'bui_contexts', BUI_CONTEXTS_ENTIRE_SITE);
|
||||
|
||||
} else if ($parentcontext->contextlevel == CONTEXT_COURSE) {
|
||||
// 0 means display on current context only, not child contexts
|
||||
// but if course managers select mod-* as pagetype patterns, block system will overwrite this option
|
||||
// to 1 (display on current context and child contexts)
|
||||
$mform->addElement('hidden', 'bui_contexts', BUI_CONTEXTS_CURRENT);
|
||||
} else if ($parentcontext->contextlevel == CONTEXT_MODULE or $parentcontext->contextlevel == CONTEXT_USER) {
|
||||
// module context doesn't have child contexts, so display in current context only
|
||||
$mform->addElement('hidden', 'bui_contexts', BUI_CONTEXTS_CURRENT);
|
||||
} else {
|
||||
$parentcontextname = $parentcontext->get_context_name();
|
||||
$contextoptions[BUI_CONTEXTS_CURRENT] = get_string('showoncontextonly', 'block', $parentcontextname);
|
||||
@@ -181,8 +169,6 @@ class block_edit_form extends moodleform {
|
||||
} else {
|
||||
$values = array_keys($pagetypelist);
|
||||
$value = array_pop($values);
|
||||
$mform->addElement('hidden', 'bui_pagetypepattern', $value);
|
||||
$mform->setType('bui_pagetypepattern', PARAM_RAW);
|
||||
// Now we are really hiding a lot (both page-contexts and page-type-patterns),
|
||||
// specially in some systemcontext pages having only one option (my/user...)
|
||||
// so, until it's decided if we are going to add the 'bring-back' pattern to
|
||||
@@ -191,7 +177,7 @@ class block_edit_form extends moodleform {
|
||||
// TODO: Revisit this once MDL-30574 has been decided and implemented, although
|
||||
// perhaps it's not bad to always show this statically when only one pattern is
|
||||
// available.
|
||||
if (!$editingatfrontpage) {
|
||||
if (!$this->is_editing_the_frontpage()) {
|
||||
// Try to beautify it
|
||||
$strvalue = $value;
|
||||
$strkey = 'page-'.str_replace('*', 'x', $strvalue);
|
||||
@@ -205,10 +191,7 @@ class block_edit_form extends moodleform {
|
||||
}
|
||||
|
||||
if ($this->page->subpage) {
|
||||
if ($parentcontext->contextlevel == CONTEXT_USER) {
|
||||
$mform->addElement('hidden', 'bui_subpagepattern', '%@NULL@%');
|
||||
$mform->setType('bui_subpagepattern', PARAM_RAW);
|
||||
} else {
|
||||
if ($parentcontext->contextlevel != CONTEXT_USER) {
|
||||
$subpageoptions = array(
|
||||
'%@NULL@%' => get_string('anypagematchingtheabove', 'block'),
|
||||
$this->page->subpage => get_string('thisspecificpage', 'block', $this->page->subpage),
|
||||
@@ -252,6 +235,19 @@ class block_edit_form extends moodleform {
|
||||
$this->add_action_buttons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user is editing a frontpage.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_editing_the_frontpage() {
|
||||
// There are some conditions to check related to contexts.
|
||||
$ctxconditions = $this->page->context->contextlevel == CONTEXT_COURSE &&
|
||||
$this->page->context->instanceid == get_site()->id;
|
||||
$issiteindex = (strpos($this->page->pagetype, 'site-index') === 0);
|
||||
// So now we can be 100% sure if edition is happening at frontpage.
|
||||
return ($ctxconditions && $issiteindex);
|
||||
}
|
||||
|
||||
function set_data($defaults) {
|
||||
// Prefix bui_ on all the core field names.
|
||||
$blockfields = array('showinsubcontexts', 'pagetypepattern', 'subpagepattern', 'parentcontextid',
|
||||
@@ -281,6 +277,13 @@ class block_edit_form extends moodleform {
|
||||
$defaults->bui_contexts = $defaults->bui_showinsubcontexts;
|
||||
}
|
||||
|
||||
// Some fields may not be editable, remember the values here so we can return them in get_data().
|
||||
$this->defaults = [
|
||||
'bui_parentcontextid' => $defaults->bui_parentcontextid,
|
||||
'bui_contexts' => $defaults->bui_contexts,
|
||||
'bui_pagetypepattern' => $defaults->bui_pagetypepattern,
|
||||
'bui_subpagepattern' => $defaults->bui_subpagepattern,
|
||||
];
|
||||
parent::set_data($defaults);
|
||||
}
|
||||
|
||||
@@ -291,4 +294,20 @@ class block_edit_form extends moodleform {
|
||||
protected function specific_definition($mform) {
|
||||
// By default, do nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* Return submitted data if properly submitted or returns NULL if validation fails or
|
||||
* if there is no submitted data.
|
||||
*
|
||||
* @return object submitted data; NULL if not valid or not submitted or cancelled
|
||||
*/
|
||||
public function get_data() {
|
||||
if ($data = parent::get_data()) {
|
||||
// Blocklib expects 'bui_editingatfrontpage' property to be returned from this form.
|
||||
$data->bui_editingatfrontpage = $this->is_editing_the_frontpage();
|
||||
// Some fields are non-editable and we need to populate them with the values from set_data().
|
||||
return (object)((array)$data + $this->defaults);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ class block_login extends block_base {
|
||||
}
|
||||
|
||||
$this->content->text .= '<div class="c1 btn"><input type="submit" value="'.get_string('login').'" /></div>';
|
||||
$this->content->text .= '<input type="hidden" name="logintoken" value="'.s(\core\session\manager::get_login_token()).'" />';
|
||||
|
||||
$this->content->text .= "</form>\n";
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
define(["jquery"],function(a){function b(d,e){var f=a("<ul></ul>");f.attr("role","group"),f.attr("aria-hidden",!0),a.each(e,function(d,e){if("object"==typeof e){var g=a("<li></li>"),h=a("<p></p>"),i=e.id||e.key+"_tree_item",j=null,k=e.expandable||e.haschildren?!0:!1;if(h.addClass("tree_item"),h.attr("id",i),h.attr("role","treeitem"),h.attr("tabindex","-1"),e.requiresajaxloading&&(h.attr("data-requires-ajax",!0),h.attr("data-node-id",e.id),h.attr("data-node-key",e.key),h.attr("data-node-type",e.type)),k&&(g.addClass("collapsed contains_branch"),h.attr("aria-expanded",!1),h.addClass("branch")),!e.icon||k&&e.type!==c.ACTIVITY&&e.type!==c.RESOURCE||(g.addClass("item_with_icon"),h.addClass("hasicon"),j=a("<img/>"),j.attr("alt",e.icon.alt),j.attr("title",e.icon.title),j.attr("src",M.util.image_url(e.icon.pix,e.icon.component)),a.each(e.icon.classes,function(a,b){j.addClass(b)})),e.link){var l=a('<a title="'+e.title+'" href="'+e.link+'"></a>');j?(l.append(j),l.append('<span class="item-content-wrap">'+e.name+"</span>")):l.append(e.name),e.hidden&&l.addClass("dimmed"),h.append(l)}else{var m=a("<span></span>");j?(m.append(j),m.append('<span class="item-content-wrap">'+e.name+"</span>")):m.append(e.name),e.hidden&&m.addClass("dimmed"),h.append(m)}g.append(h),f.append(g),e.children&&e.children.length?b(h,e.children):k&&!e.requiresajaxloading&&(g.removeClass("contains_branch"),h.addClass("emptybranch"))}}),d.parent().append(f);var g=d.attr("id")+"_group";f.attr("id",g),d.attr("aria-owns",g),d.attr("role","treeitem")}var c={ACTIVITY:40,RESOURCE:50};return{render:function(a,c){if(c.children&&c.children.length){b(a,c.children);var d=a.children("[role='treeitem']").first(),e=a.find("#"+d.attr("aria-owns"));d.attr("aria-expanded",!0),e.attr("aria-hidden",!1)}else a.parent().hasClass("contains_branch")&&(a.parent().removeClass("contains_branch"),a.addClass("emptybranch"))}}});
|
||||
define(["jquery"],function(a){function b(d,e){var f=a("<ul></ul>");f.attr("role","group"),f.attr("aria-hidden",!0),a.each(e,function(d,e){if("object"==typeof e){var g=a("<li></li>"),h=a("<p></p>"),i=e.id||e.key+"_tree_item",j=null,k=!(!e.expandable&&!e.haschildren);if(h.addClass("tree_item"),h.attr("id",i),h.attr("role","treeitem"),h.attr("tabindex","-1"),e.requiresajaxloading&&(h.attr("data-requires-ajax",!0),h.attr("data-node-id",e.id),h.attr("data-node-key",e.key),h.attr("data-node-type",e.type)),k&&(g.addClass("collapsed contains_branch"),h.attr("aria-expanded",!1),h.addClass("branch")),!e.icon||k&&e.type!==c.ACTIVITY&&e.type!==c.RESOURCE||(g.addClass("item_with_icon"),h.addClass("hasicon"),j=a("<img/>"),j.attr("alt",e.icon.alt),j.attr("title",e.icon.title),j.attr("src",M.util.image_url(e.icon.pix,e.icon.component)),a.each(e.icon.classes,function(a,b){j.addClass(b)})),e.link){var l=a('<a title="'+e.title+'" href="'+e.link+'"></a>');j?(l.append(j),l.append('<span class="item-content-wrap">'+e.name+"</span>")):l.append(e.name),e.hidden&&l.addClass("dimmed"),h.append(l)}else{var m=a("<span></span>");j?(m.append(j),m.append('<span class="item-content-wrap">'+e.name+"</span>")):m.append(e.name),e.hidden&&m.addClass("dimmed"),h.append(m)}g.append(h),f.append(g),e.children&&e.children.length?b(h,e.children):k&&!e.requiresajaxloading&&(g.removeClass("contains_branch"),h.addClass("emptybranch"))}}),d.parent().append(f);var g=d.attr("id")+"_group";f.attr("id",g),d.attr("aria-owns",g),d.attr("role","treeitem")}var c={ACTIVITY:40,RESOURCE:50};return{render:function(a,c){if(c.children&&c.children.length){b(a,c.children);var d=a.children("[role='treeitem']").first(),e=a.find("#"+d.attr("aria-owns"));d.attr("aria-expanded",!0),e.attr("aria-hidden",!1)}else a.parent().hasClass("contains_branch")&&(a.parent().removeClass("contains_branch"),a.addClass("emptybranch"))}}});
|
||||
@@ -52,11 +52,11 @@ $action = (empty($id)) ? 'add' : 'edit';
|
||||
|
||||
$external = new stdClass();
|
||||
|
||||
// Check that this id exists.
|
||||
if (!empty($id) && !$DB->record_exists('blog_external', array('id' => $id))) {
|
||||
print_error('wrongexternalid', 'blog');
|
||||
} else if (!empty($id)) {
|
||||
$external = $DB->get_record('blog_external', array('id' => $id));
|
||||
// Retrieve the external blog record.
|
||||
if (!empty($id)) {
|
||||
if (!$external = $DB->get_record('blog_external', array('id' => $id, 'userid' => $USER->id))) {
|
||||
print_error('wrongexternalid', 'blog');
|
||||
}
|
||||
$external->autotags = core_tag_tag::get_item_tags_array('core', 'blog_external', $id);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -872,7 +872,7 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
|
||||
}
|
||||
|
||||
// Append Search info.
|
||||
if (!empty($search)) {
|
||||
if (!empty($search) && has_capability('moodle/blog:search', $sitecontext)) {
|
||||
$headers['filters']['search'] = $search;
|
||||
$blogurl->param('search', $search);
|
||||
$PAGE->navbar->add(get_string('searchterm', 'blog', $search), $blogurl->out());
|
||||
|
||||
Vendored
+10
-2
@@ -239,6 +239,7 @@ class cache_helper {
|
||||
$invalidationeventset = false;
|
||||
$factory = cache_factory::instance();
|
||||
$inuse = $factory->get_caches_in_use();
|
||||
$purgetoken = null;
|
||||
foreach ($instance->get_definitions() as $name => $definitionarr) {
|
||||
$definition = cache_definition::load($name, $definitionarr);
|
||||
if ($definition->invalidates_on_event($event)) {
|
||||
@@ -265,8 +266,11 @@ class cache_helper {
|
||||
$data = array();
|
||||
}
|
||||
// Add our keys to them with the current cache timestamp.
|
||||
if (null === $purgetoken) {
|
||||
$purgetoken = cache::get_purge_token(true);
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
$data[$key] = cache::now();
|
||||
$data[$key] = $purgetoken;
|
||||
}
|
||||
// Set that data back to the cache.
|
||||
$cache->set($event, $data);
|
||||
@@ -310,6 +314,7 @@ class cache_helper {
|
||||
$invalidationeventset = false;
|
||||
$factory = cache_factory::instance();
|
||||
$inuse = $factory->get_caches_in_use();
|
||||
$purgetoken = null;
|
||||
foreach ($instance->get_definitions() as $name => $definitionarr) {
|
||||
$definition = cache_definition::load($name, $definitionarr);
|
||||
if ($definition->invalidates_on_event($event)) {
|
||||
@@ -333,8 +338,11 @@ class cache_helper {
|
||||
// Get the event invalidation cache.
|
||||
$cache = cache::make('core', 'eventinvalidation');
|
||||
// Create a key to invalidate all.
|
||||
if (null === $purgetoken) {
|
||||
$purgetoken = cache::get_purge_token(true);
|
||||
}
|
||||
$data = array(
|
||||
'purged' => cache::now()
|
||||
'purged' => $purgetoken,
|
||||
);
|
||||
// Set that data back to the cache.
|
||||
$cache->set($event, $data);
|
||||
|
||||
Vendored
+127
-18
@@ -50,6 +50,14 @@ class cache implements cache_loader {
|
||||
*/
|
||||
protected static $now;
|
||||
|
||||
/**
|
||||
* A purge token used to distinguish between multiple cache purges in the same second.
|
||||
* This is in the format <microtime>-<random string>.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $purgetoken;
|
||||
|
||||
/**
|
||||
* The definition used when loading this cache if there was one.
|
||||
* @var cache_definition
|
||||
@@ -213,7 +221,7 @@ class cache implements cache_loader {
|
||||
$this->definition = $definition;
|
||||
$this->store = $store;
|
||||
$this->storetype = get_class($store);
|
||||
$this->perfdebug = !empty($CFG->perfdebug);
|
||||
$this->perfdebug = (!empty($CFG->perfdebug) and $CFG->perfdebug > 7);
|
||||
if ($loader instanceof cache_loader) {
|
||||
$this->loader = $loader;
|
||||
// Mark the loader as a sub (chained) loader.
|
||||
@@ -1144,13 +1152,70 @@ class cache implements cache_loader {
|
||||
* This stamp needs to be used for all ttl and time based operations to ensure that we don't end up with
|
||||
* timing issues.
|
||||
*
|
||||
* @return int
|
||||
* @param bool $float Whether to use floating precision accuracy.
|
||||
* @return int|float
|
||||
*/
|
||||
public static function now() {
|
||||
public static function now($float = false) {
|
||||
if (self::$now === null) {
|
||||
self::$now = time();
|
||||
self::$now = microtime(true);
|
||||
}
|
||||
|
||||
if ($float) {
|
||||
return self::$now;
|
||||
} else {
|
||||
return (int) self::$now;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a 'purge' token used for cache invalidation handling.
|
||||
*
|
||||
* Note: This function is intended for use from within the Cache API only and not by plugins, or cache stores.
|
||||
*
|
||||
* @param bool $reset Whether to reset the token and generate a new one.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_purge_token($reset = false) {
|
||||
if (self::$purgetoken === null || $reset) {
|
||||
self::$now = null;
|
||||
self::$purgetoken = self::now(true) . '-' . uniqid('', true);
|
||||
}
|
||||
|
||||
return self::$purgetoken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare a pair of purge tokens.
|
||||
*
|
||||
* If the two tokens are identical, then the return value is 0.
|
||||
* If the time component of token A is newer than token B, then a positive value is returned.
|
||||
* If the time component of token B is newer than token A, then a negative value is returned.
|
||||
*
|
||||
* Note: This function is intended for use from within the Cache API only and not by plugins, or cache stores.
|
||||
*
|
||||
* @param string $tokena
|
||||
* @param string $tokenb
|
||||
* @return int
|
||||
*/
|
||||
public static function compare_purge_tokens($tokena, $tokenb) {
|
||||
if ($tokena === $tokenb) {
|
||||
// There is an exact match.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The token for when the cache was last invalidated.
|
||||
list($atime) = explode('-', "{$tokena}-", 2);
|
||||
|
||||
// The token for this cache.
|
||||
list($btime) = explode('-', "{$tokenb}-", 2);
|
||||
|
||||
if ($atime >= $btime) {
|
||||
// Token A is newer.
|
||||
return 1;
|
||||
} else {
|
||||
// Token A is older.
|
||||
return -1;
|
||||
}
|
||||
return self::$now;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1236,18 +1301,40 @@ class cache_application extends cache implements cache_loader_with_locking {
|
||||
if ($lastinvalidation === false) {
|
||||
// This is a new session, there won't be anything to invalidate. Set the time of the last invalidation and
|
||||
// move on.
|
||||
$this->set('lastinvalidation', cache::now());
|
||||
$this->set('lastinvalidation', self::get_purge_token());
|
||||
return;
|
||||
} else if ($lastinvalidation == cache::now()) {
|
||||
// We've already invalidated during this request.
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the event invalidation cache.
|
||||
/*
|
||||
* Now that the whole cache check is complete, we check the meaning of any specific cache invalidation events.
|
||||
* These are stored in the core/eventinvalidation cache as an multi-dimensinoal array in the form:
|
||||
* [
|
||||
* eventname => [
|
||||
* keyname => purgetoken,
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* The 'keyname' value is used to delete a specific key in the cache.
|
||||
* If the keyname is set to the special value 'purged', then the whole cache is purged instead.
|
||||
*
|
||||
* The 'purgetoken' is the token that this key was last purged.
|
||||
* a) If the purgetoken matches the last invalidation, then the key/cache is not purged.
|
||||
* b) If the purgetoken is newer than the last invalidation, then the key/cache is not purged.
|
||||
* c) If the purge token is older than the last invalidation, or it has a different token component, then
|
||||
* the cache is purged.
|
||||
*
|
||||
* Option b should not happen under normal operation, but may happen in race condition whereby a long-running
|
||||
* request's cache is cleared in another process during that request, and prior to that long-running request
|
||||
* creating the cache. In such a condition, it would be incorrect to clear that cache.
|
||||
*/
|
||||
$cache = cache::make('core', 'eventinvalidation');
|
||||
$events = $cache->get_many($definition->get_invalidation_events());
|
||||
$todelete = array();
|
||||
$purgeall = false;
|
||||
|
||||
// Iterate the returned data for the events.
|
||||
foreach ($events as $event => $keys) {
|
||||
if ($keys === false) {
|
||||
@@ -1255,10 +1342,10 @@ class cache_application extends cache implements cache_loader_with_locking {
|
||||
continue;
|
||||
}
|
||||
// Look at each key and check the timestamp.
|
||||
foreach ($keys as $key => $timestamp) {
|
||||
foreach ($keys as $key => $purgetoken) {
|
||||
// If the timestamp of the event is more than or equal to the last invalidation (happened between the last
|
||||
// invalidation and now)then we need to invaliate the key.
|
||||
if ($timestamp >= $lastinvalidation) {
|
||||
if (self::compare_purge_tokens($purgetoken, $lastinvalidation) > 0) {
|
||||
if ($key === 'purged') {
|
||||
$purgeall = true;
|
||||
break;
|
||||
@@ -1276,7 +1363,7 @@ class cache_application extends cache implements cache_loader_with_locking {
|
||||
}
|
||||
// Set the time of the last invalidation.
|
||||
if ($purgeall || !empty($todelete)) {
|
||||
$this->set('lastinvalidation', cache::now());
|
||||
$this->set('lastinvalidation', self::get_purge_token(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1637,18 +1724,40 @@ class cache_session extends cache {
|
||||
if ($lastinvalidation === false) {
|
||||
// This is a new session, there won't be anything to invalidate. Set the time of the last invalidation and
|
||||
// move on.
|
||||
$this->set('lastsessioninvalidation', cache::now());
|
||||
$this->set('lastsessioninvalidation', self::get_purge_token());
|
||||
return;
|
||||
} else if ($lastinvalidation == cache::now()) {
|
||||
} else if ($lastinvalidation == self::get_purge_token()) {
|
||||
// We've already invalidated during this request.
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the event invalidation cache.
|
||||
/*
|
||||
* Now that the whole cache check is complete, we check the meaning of any specific cache invalidation events.
|
||||
* These are stored in the core/eventinvalidation cache as an multi-dimensinoal array in the form:
|
||||
* [
|
||||
* eventname => [
|
||||
* keyname => purgetoken,
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* The 'keyname' value is used to delete a specific key in the cache.
|
||||
* If the keyname is set to the special value 'purged', then the whole cache is purged instead.
|
||||
*
|
||||
* The 'purgetoken' is the token that this key was last purged.
|
||||
* a) If the purgetoken matches the last invalidation, then the key/cache is not purged.
|
||||
* b) If the purgetoken is newer than the last invalidation, then the key/cache is not purged.
|
||||
* c) If the purge token is older than the last invalidation, or it has a different token component, then
|
||||
* the cache is purged.
|
||||
*
|
||||
* Option b should not happen under normal operation, but may happen in race condition whereby a long-running
|
||||
* request's cache is cleared in another process during that request, and prior to that long-running request
|
||||
* creating the cache. In such a condition, it would be incorrect to clear that cache.
|
||||
*/
|
||||
$cache = cache::make('core', 'eventinvalidation');
|
||||
$events = $cache->get_many($definition->get_invalidation_events());
|
||||
$todelete = array();
|
||||
$purgeall = false;
|
||||
|
||||
// Iterate the returned data for the events.
|
||||
foreach ($events as $event => $keys) {
|
||||
if ($keys === false) {
|
||||
@@ -1656,10 +1765,10 @@ class cache_session extends cache {
|
||||
continue;
|
||||
}
|
||||
// Look at each key and check the timestamp.
|
||||
foreach ($keys as $key => $timestamp) {
|
||||
foreach ($keys as $key => $purgetoken) {
|
||||
// If the timestamp of the event is more than or equal to the last invalidation (happened between the last
|
||||
// invalidation and now)then we need to invaliate the key.
|
||||
if ($timestamp >= $lastinvalidation) {
|
||||
// invalidation and now), then we need to invaliate the key.
|
||||
if (self::compare_purge_tokens($purgetoken, $lastinvalidation) > 0) {
|
||||
if ($key === 'purged') {
|
||||
$purgeall = true;
|
||||
break;
|
||||
@@ -1677,7 +1786,7 @@ class cache_session extends cache {
|
||||
}
|
||||
// Set the time of the last invalidation.
|
||||
if ($purgeall || !empty($todelete)) {
|
||||
$this->set('lastsessioninvalidation', cache::now());
|
||||
$this->set('lastsessioninvalidation', self::get_purge_token(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2214,4 +2323,4 @@ class cache_session extends cache {
|
||||
*/
|
||||
class cache_request extends cache {
|
||||
// This comment appeases code pre-checker ;) !
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+54
-2
@@ -229,8 +229,60 @@ class cachestore_memcached extends cache_store implements cache_is_configurable
|
||||
$version = phpversion('memcached');
|
||||
$this->candeletemulti = ($version && version_compare($version, self::REQUIRED_VERSION, '>='));
|
||||
|
||||
// Test the connection to the main connection.
|
||||
$this->isready = @$this->connection->set("ping", 'ping', 1);
|
||||
$this->isready = $this->is_connection_ready();
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm whether the connection is ready and usable.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_connection_ready() {
|
||||
if (!@$this->connection->set("ping", 'ping', 1)) {
|
||||
// Test the connection to the server.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isshared) {
|
||||
// There is a bug in libmemcached which means that it is not possible to purge the cache in a shared cache
|
||||
// configuration.
|
||||
// This issue currently affects:
|
||||
// - memcached 1.4.23+ with php-memcached <= 2.2.0
|
||||
// The following combinations are not affected:
|
||||
// - memcached <= 1.4.22 with any version of php-memcached
|
||||
// - any version of memcached with php-memcached >= 3.0.1
|
||||
|
||||
|
||||
// This check is cheapest as it does not involve connecting to the server at all.
|
||||
$safecombination = false;
|
||||
$extension = new ReflectionExtension('memcached');
|
||||
if ((version_compare($extension->getVersion(), '3.0.1') >= 0)) {
|
||||
// This is php-memcached version >= 3.0.1 which is a safe combination.
|
||||
$safecombination = true;
|
||||
}
|
||||
|
||||
if (!$safecombination) {
|
||||
$allsafe = true;
|
||||
foreach ($this->connection->getVersion() as $version) {
|
||||
$allsafe = $allsafe && (version_compare($version, '1.4.22') <= 0);
|
||||
}
|
||||
// All memcached servers connected are version <= 1.4.22 which is a safe combination.
|
||||
$safecombination = $allsafe;
|
||||
}
|
||||
|
||||
if (!$safecombination) {
|
||||
// This is memcached 1.4.23+ and php-memcached < 3.0.1.
|
||||
// The issue may have been resolved in a subsequent update to any of the three libraries.
|
||||
// The only way to safely determine if the combination is safe is to call getAllKeys.
|
||||
// A safe combination will return an array, whilst an affected combination will return false.
|
||||
// This is the most expensive check.
|
||||
if (!is_array($this->connection->getAllKeys())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -286,6 +286,10 @@ class cachestore_memcached_test extends cachestore_tests {
|
||||
|
||||
$definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_memcached', 'phpunit_test');
|
||||
$cachestore = $this->create_test_cache_with_config($definition, array('isshared' => true));
|
||||
if (!$cachestore->is_connection_ready()) {
|
||||
$this->markTestSkipped('Could not test cachestore_memcached. Connection is not ready.');
|
||||
}
|
||||
|
||||
$connection = new Memcached(crc32(__METHOD__));
|
||||
$connection->addServers($this->get_servers(TEST_CACHESTORE_MEMCACHED_TESTSERVERS));
|
||||
$connection->setOptions(array(
|
||||
|
||||
Vendored
+117
-10
@@ -1009,7 +1009,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
$timefile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/las-cache/lastinvalidation-$hash.cache";
|
||||
// Make sure the file is correct.
|
||||
$this->assertTrue(file_exists($timefile));
|
||||
$timecont = serialize(cache::now() - 60); // Back 60sec in the past to force it to re-invalidate.
|
||||
$timecont = serialize(cache::now(true) - 60); // Back 60sec in the past to force it to re-invalidate.
|
||||
make_writable_directory(dirname($timefile));
|
||||
file_put_contents($timefile, $timecont);
|
||||
$this->assertTrue(file_exists($timefile));
|
||||
@@ -1035,6 +1035,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_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/eventinvalidationtest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
@@ -1046,6 +1047,7 @@ class core_cache_testcase extends advanced_testcase {
|
||||
'crazyevent'
|
||||
)
|
||||
));
|
||||
|
||||
$cache = cache::make('phpunit', 'eventinvalidationtest');
|
||||
$this->assertFalse($cache->get('testkey1'));
|
||||
|
||||
@@ -1053,23 +1055,29 @@ class core_cache_testcase extends advanced_testcase {
|
||||
|
||||
// Make a new cache class. This should should invalidate testkey2.
|
||||
$cache = cache::make('phpunit', 'eventinvalidationtest');
|
||||
// Timestamp should have updated to cache::now().
|
||||
$this->assertEquals(cache::now(), $cache->get('lastinvalidation'));
|
||||
|
||||
// Invalidation token should have been reset.
|
||||
$this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation'));
|
||||
|
||||
// Set testkey2 data.
|
||||
$cache->set('testkey2', 'test data 2');
|
||||
|
||||
// Backdate the event invalidation time by 30 seconds.
|
||||
$invalidationcache = cache::make('core', 'eventinvalidation');
|
||||
$invalidationcache->set('crazyevent', array('testkey2' => cache::now() - 30));
|
||||
|
||||
// Lastinvalidation should already be cache::now().
|
||||
$this->assertEquals(cache::now(), $cache->get('lastinvalidation'));
|
||||
$this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation'));
|
||||
|
||||
// Set it to 15 seconds ago so that we know if it changes.
|
||||
$cache->set('lastinvalidation', cache::now() - 15);
|
||||
$cache->set('lastinvalidation', cache::now(true) - 15);
|
||||
|
||||
// Make a new cache class. This should not invalidate anything.
|
||||
cache_factory::instance()->reset_cache_instances();
|
||||
$cache = cache::make('phpunit', 'eventinvalidationtest');
|
||||
|
||||
// Lastinvalidation shouldn't change since it was already newer than invalidation event.
|
||||
$this->assertEquals(cache::now() - 15, $cache->get('lastinvalidation'));
|
||||
$this->assertEquals(cache::now(true) - 15, $cache->get('lastinvalidation'));
|
||||
|
||||
// Now set the event invalidation to newer than the lastinvalidation time.
|
||||
$invalidationcache->set('crazyevent', array('testkey2' => cache::now() - 5));
|
||||
@@ -1077,18 +1085,18 @@ class core_cache_testcase extends advanced_testcase {
|
||||
cache_factory::instance()->reset_cache_instances();
|
||||
$cache = cache::make('phpunit', 'eventinvalidationtest');
|
||||
// Lastinvalidation timestamp should have updated to cache::now().
|
||||
$this->assertEquals(cache::now(), $cache->get('lastinvalidation'));
|
||||
$this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation'));
|
||||
|
||||
// Now simulate a purge_by_event 5 seconds ago.
|
||||
$invalidationcache = cache::make('core', 'eventinvalidation');
|
||||
$invalidationcache->set('crazyevent', array('purged' => cache::now() - 5));
|
||||
$invalidationcache->set('crazyevent', array('purged' => cache::now(true) - 5));
|
||||
// Set our lastinvalidation timestamp to 15 seconds ago.
|
||||
$cache->set('lastinvalidation', cache::now() - 15);
|
||||
$cache->set('lastinvalidation', cache::now(true) - 15);
|
||||
// Make a new cache class. This should invalidate the cache.
|
||||
cache_factory::instance()->reset_cache_instances();
|
||||
$cache = cache::make('phpunit', 'eventinvalidationtest');
|
||||
// Lastinvalidation timestamp should have updated to cache::now().
|
||||
$this->assertEquals(cache::now(), $cache->get('lastinvalidation'));
|
||||
$this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation'));
|
||||
|
||||
}
|
||||
|
||||
@@ -2170,4 +2178,103 @@ class core_cache_testcase extends advanced_testcase {
|
||||
$this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['sets'] -
|
||||
$startstats[$requestid]['stores']['cachestore_static']['sets']);
|
||||
}
|
||||
|
||||
public function test_performance_debug_off() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest(true);
|
||||
$CFG->perfdebug = 7;
|
||||
|
||||
$instance = cache_config_testing::instance();
|
||||
$applicationid = 'phpunit/applicationperfoff';
|
||||
$instance->phpunit_add_definition($applicationid, array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'applicationperfoff'
|
||||
));
|
||||
$sessionid = 'phpunit/sessionperfoff';
|
||||
$instance->phpunit_add_definition($sessionid, array(
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'sessionperfoff'
|
||||
));
|
||||
$requestid = 'phpunit/requestperfoff';
|
||||
$instance->phpunit_add_definition($requestid, array(
|
||||
'mode' => cache_store::MODE_REQUEST,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'requestperfoff'
|
||||
));
|
||||
|
||||
$application = cache::make('phpunit', 'applicationperfoff');
|
||||
$session = cache::make('phpunit', 'sessionperfoff');
|
||||
$request = cache::make('phpunit', 'requestperfoff');
|
||||
|
||||
// Check that no stats are recorded for these definitions yet.
|
||||
$stats = cache_helper::get_stats();
|
||||
$this->assertArrayNotHasKey($applicationid, $stats);
|
||||
$this->assertArrayNotHasKey($sessionid, $stats);
|
||||
$this->assertArrayNotHasKey($requestid, $stats);
|
||||
|
||||
// Trigger cache misses, cache sets and cache hits.
|
||||
$this->assertFalse($application->get('missMe'));
|
||||
$this->assertTrue($application->set('setMe', 1));
|
||||
$this->assertEquals(1, $application->get('setMe'));
|
||||
$this->assertFalse($session->get('missMe'));
|
||||
$this->assertTrue($session->set('setMe', 3));
|
||||
$this->assertEquals(3, $session->get('setMe'));
|
||||
$this->assertFalse($request->get('missMe'));
|
||||
$this->assertTrue($request->set('setMe', 4));
|
||||
$this->assertEquals(4, $request->get('setMe'));
|
||||
|
||||
// Check that no stats are being recorded for these definitions.
|
||||
$endstats = cache_helper::get_stats();
|
||||
$this->assertArrayNotHasKey($applicationid, $endstats);
|
||||
$this->assertArrayNotHasKey($sessionid, $endstats);
|
||||
$this->assertArrayNotHasKey($requestid, $endstats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests session cache event purge and subsequent visit in the same request.
|
||||
*
|
||||
* This test simulates a cache being created, a value being set, then the value being purged.
|
||||
* A subsequent use of the same cache is started in the same request which fills the cache.
|
||||
* A new request is started a short time later.
|
||||
* The cache should be filled.
|
||||
*/
|
||||
public function test_session_event_purge_same_second() {
|
||||
$instance = cache_config_testing::instance();
|
||||
$instance->phpunit_add_definition('phpunit/eventpurgetest', array(
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'eventpurgetest',
|
||||
'invalidationevents' => array(
|
||||
'crazyevent',
|
||||
)
|
||||
));
|
||||
|
||||
// Create the cache, set a value, and immediately purge it by event.
|
||||
$cache = cache::make('phpunit', 'eventpurgetest');
|
||||
$cache->set('testkey1', 'test data 1');
|
||||
$this->assertEquals('test data 1', $cache->get('testkey1'));
|
||||
cache_helper::purge_by_event('crazyevent');
|
||||
$this->assertFalse($cache->get('testkey1'));
|
||||
|
||||
// Set up the cache again in the same request and add a new value back in.
|
||||
$factory = \cache_factory::instance();
|
||||
$factory->reset_cache_instances();
|
||||
$cache = cache::make('phpunit', 'eventpurgetest');
|
||||
$cache->set('testkey1', 'test data 2');
|
||||
$this->assertEquals('test data 2', $cache->get('testkey1'));
|
||||
|
||||
// Trick the cache into thinking that this is a new request.
|
||||
cache_phpunit_cache::simulate_new_request();
|
||||
$factory = \cache_factory::instance();
|
||||
$factory->reset_cache_instances();
|
||||
|
||||
// Set up the cache again.
|
||||
// This is a subsequent request at a new time, so we instead the invalidation time will be checked.
|
||||
// The invalidation time should match the last purged time and the cache will not be re-purged.
|
||||
$cache = cache::make('phpunit', 'eventpurgetest');
|
||||
$this->assertEquals('test data 2', $cache->get('testkey1'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vendored
+14
-40
@@ -534,47 +534,21 @@ class cache_phpunit_factory extends cache_factory {
|
||||
public static function phpunit_disable() {
|
||||
parent::disable();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache PHPUnit specific Cache helper.
|
||||
*
|
||||
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_phpunit_cache extends cache {
|
||||
/**
|
||||
* @var bool Whether the warning notice about alternative cache store used has been displayed.
|
||||
* Make the changes which simulate a new request within the cache.
|
||||
* This essentially resets currently held static values in the class, and increments the current timestamp.
|
||||
*/
|
||||
protected $altcachestorenotice = false;
|
||||
|
||||
/**
|
||||
* Creates a store instance given its name and configuration.
|
||||
*
|
||||
* If the store has already been instantiated then the original object will be returned. (reused)
|
||||
*
|
||||
* @param string $name The name of the store (must be unique remember)
|
||||
* @param array $details
|
||||
* @param cache_definition $definition The definition to instantiate it for.
|
||||
* @return boolean|cache_store
|
||||
*/
|
||||
public function create_store_from_config($name, array $details, cache_definition $definition) {
|
||||
|
||||
if (isset($details['use_test_store'])) {
|
||||
// name, plugin, alt
|
||||
$class = 'cachestore_'.$details['plugin'];
|
||||
$method = 'initialise_unit_test_instance';
|
||||
if (class_exists($class) && method_exists($class, $method)) {
|
||||
$instance = $class::$method($definition);
|
||||
|
||||
if ($instance) {
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
|
||||
// Notify user that alternative store is being used, so action can be taken.
|
||||
if (!$this->altcachestorenotice) {
|
||||
echo PHP_EOL . "++ WARNING: " . 'Failed to use "' . $details['plugin'] . '" cache store, alt "' .
|
||||
$details['alt']['plugin'] . '" cache store is used.' . PHP_EOL . PHP_EOL;
|
||||
$this->altcachestorenotice = true;
|
||||
}
|
||||
$details = $details['alt'];
|
||||
$details['class'] = 'cachestore_'.$details['plugin'];
|
||||
$name = $details['name'];
|
||||
}
|
||||
|
||||
return parent::create_store_from_config($name, $details, $definition);
|
||||
public static function simulate_new_request() {
|
||||
self::$now += 0.1;
|
||||
self::$purgetoken = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+4
@@ -1,6 +1,10 @@
|
||||
This files describes API changes in /cache/stores/* - cache store plugins.
|
||||
Information provided here is intended especially for developers.
|
||||
|
||||
=== 3.1.14 ===
|
||||
* The `cache::now()` function now takes an optional boolean parameter to indicate that the cache should return a more
|
||||
accurate time, generated by the PHP `microtime` function.
|
||||
|
||||
=== 3.1 ===
|
||||
* Cache stores has a new feature DEREFERENCES_OBJECTS.
|
||||
This allows the cache loader to decide if it needs to handle dereferencing or whether the data
|
||||
|
||||
+1024
-338
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -58,9 +58,11 @@ if (!$course) {
|
||||
$PAGE->set_context(context_system::instance()); //TODO: wrong
|
||||
}
|
||||
|
||||
$title = get_string('deleteevent', 'calendar');
|
||||
|
||||
// Check the user has the required capabilities to edit an event
|
||||
if (!calendar_edit_event_allowed($event)) {
|
||||
print_error('nopermissions');
|
||||
if (!calendar_edit_event_allowed($event) || !empty($event->modulename)) {
|
||||
print_error('nopermissions', 'error', $PAGE->url, $title);
|
||||
}
|
||||
|
||||
// Count the repeats, do we need to consider the possibility of deleting repeats
|
||||
@@ -88,7 +90,6 @@ if ($confirm) {
|
||||
}
|
||||
|
||||
// Prepare the page to show the confirmation form
|
||||
$title = get_string('deleteevent', 'calendar');
|
||||
$strcalendar = get_string('calendar', 'calendar');
|
||||
|
||||
$PAGE->navbar->add($strcalendar, $viewcalendarurl);
|
||||
|
||||
@@ -81,8 +81,9 @@ class core_calendar_external extends external_api {
|
||||
$eventobj = calendar_event::load($event['eventid']);
|
||||
|
||||
// Let's check if the user is allowed to delete an event.
|
||||
if (!calendar_edit_event_allowed($eventobj)) {
|
||||
throw new moodle_exception("nopermissions");
|
||||
if (!empty($eventobj->modulename) || !calendar_edit_event_allowed($eventobj)) {
|
||||
throw new moodle_exception("nopermissions", 'error', '',
|
||||
get_string('deleteevent', 'calendar'));
|
||||
}
|
||||
// Time to do the magic.
|
||||
$eventobj->delete($event['repeat']);
|
||||
|
||||
+1
-1
@@ -411,7 +411,7 @@ function calendar_get_mini($courses, $groups, $users, $calmonth = false, $calyea
|
||||
$name = format_string($event->name, true);
|
||||
}
|
||||
}
|
||||
$popupcontent .= html_writer::link($dayhref, $name);
|
||||
$popupcontent .= html_writer::link($dayhref, clean_text($name));
|
||||
$popupcontent .= html_writer::end_tag('div');
|
||||
}
|
||||
|
||||
|
||||
@@ -506,4 +506,27 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
|
||||
$this->assertEquals(1, count($eventsret['events']));
|
||||
$this->assertEquals(2, count($eventsret['warnings']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for deleting module events.
|
||||
*/
|
||||
public function test_delete_calendar_events_for_modules() {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$nexttime = time() + DAYSECS;
|
||||
$this->getDataGenerator()->create_module('assign', ['course' => $course->id, 'duedate' => $nexttime]);
|
||||
$events = calendar_get_events(time(), $nexttime, true, true, true);
|
||||
$this->assertCount(1, $events);
|
||||
$params = [];
|
||||
foreach($events as $event) {
|
||||
$params[] = [
|
||||
'eventid' => $event->id,
|
||||
'repeat' => false
|
||||
];
|
||||
}
|
||||
|
||||
$this->setExpectedException('moodle_exception');
|
||||
core_calendar_external::delete_calendar_events($params);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,535 +0,0 @@
|
||||
<?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 test class to test manage rrule during ical imports.
|
||||
*
|
||||
* @package core_calendar
|
||||
* @category test
|
||||
* @copyright 2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
|
||||
/** @var stdClass a dummy event */
|
||||
protected $event;
|
||||
|
||||
/**
|
||||
* Set up method.
|
||||
*/
|
||||
protected function setUp() {
|
||||
global $DB, $CFG;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$this->setTimezone('Australia/Perth');
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$sub = new stdClass();
|
||||
$sub->url = '';
|
||||
$sub->courseid = 0;
|
||||
$sub->groupid = 0;
|
||||
$sub->userid = $user->id;
|
||||
$sub->pollinterval = 0;
|
||||
$subid = $DB->insert_record('event_subscriptions', $sub, true);
|
||||
|
||||
$event = new stdClass();
|
||||
$event->name = 'Event name';
|
||||
$event->description = '';
|
||||
$event->timestart = 1385913700; // A 2013-12-2 Monday event.
|
||||
$event->timeduration = 3600;
|
||||
$event->uuid = 'uuid';
|
||||
$event->subscriptionid = $subid;
|
||||
$event->userid = $user->id;
|
||||
$event->groupid = 0;
|
||||
$event->courseid = 0;
|
||||
$event->eventtype = 'user';
|
||||
$eventobj = calendar_event::create($event, false);
|
||||
$DB->set_field('event', 'repeatid', $eventobj->id, array('id' => $eventobj->id));
|
||||
$eventobj->repeatid = $eventobj->id;
|
||||
$this->event = $eventobj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test parse_rrule() method.
|
||||
*/
|
||||
public function test_parse_rrule() {
|
||||
|
||||
$rrule = "FREQ=DAILY;COUNT=3;INTERVAL=4;BYSECOND=20,40;BYMINUTE=2,30;BYHOUR=3,4;BYDAY=MO,TH;BYMONTHDAY=20,
|
||||
30;BYYEARDAY=300,-20;BYWEEKNO=22,33;BYMONTH=3,4";
|
||||
$mang = new core_tests_calendar_rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$this->assertEquals(\core_calendar\rrule_manager::FREQ_DAILY, $mang->freq);
|
||||
$this->assertEquals(3, $mang->count);
|
||||
$this->assertEquals(4, $mang->interval);
|
||||
$this->assertEquals(array(20, 40), $mang->bysecond);
|
||||
$this->assertEquals(array(2, 30), $mang->byminute);
|
||||
$this->assertEquals(array(3, 4), $mang->byhour);
|
||||
$this->assertEquals(array('MO', 'TH'), $mang->byday);
|
||||
$this->assertEquals(array(20, 30), $mang->bymonthday);
|
||||
$this->assertEquals(array(300, -20), $mang->byyearday);
|
||||
$this->assertEquals(array(22, 33), $mang->byweekno);
|
||||
$this->assertEquals(array(3, 4), $mang->bymonth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test exception is thrown for invalid property.
|
||||
*/
|
||||
public function test_parse_rrule_validation() {
|
||||
|
||||
$rrule = "RANDOM=PROPERTY;";
|
||||
$this->setExpectedException('moodle_exception');
|
||||
$mang = new core_tests_calendar_rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test exception is thrown for invalid frequency.
|
||||
*/
|
||||
public function test_freq_validation() {
|
||||
|
||||
$rrule = "FREQ=RANDOMLY;";
|
||||
$this->setExpectedException('moodle_exception');
|
||||
$mang = new core_tests_calendar_rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test recurrence rules for daily frequency.
|
||||
*/
|
||||
public function test_daily_events() {
|
||||
global $DB;
|
||||
|
||||
$rrule = 'FREQ=DAILY;COUNT=3'; // This should generate 2 child events + 1 parent.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(3, $count);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + DAYSECS)));
|
||||
$this->assertTrue($result);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + 2 * DAYSECS)));
|
||||
$this->assertTrue($result);
|
||||
|
||||
$until = $this->event->timestart + DAYSECS * 2;
|
||||
$until = date('Y-m-d', $until);
|
||||
$rrule = "FREQ=DAILY;UNTIL=$until"; // This should generate 1 child event + 1 parent,since by then until bound would be hit.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(2, $count);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + DAYSECS)));
|
||||
$this->assertTrue($result);
|
||||
|
||||
$rrule = 'FREQ=DAILY;COUNT=3;INTERVAL=3'; // This should generate 2 child events + 1 parent, every 3rd day.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(3, $count);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + 3 * DAYSECS)));
|
||||
$this->assertTrue($result);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + 6 * DAYSECS)));
|
||||
$this->assertTrue($result);
|
||||
|
||||
// Forever event. This should generate events for time() + 10 year period, every 300th day.
|
||||
$rrule = 'FREQ=DAILY;INTERVAL=300';
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $time = $this->event->timestart + 300 * DAYSECS * $i) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test recurrence rules for weekly frequency.
|
||||
*/
|
||||
public function test_weekly_events() {
|
||||
global $DB;
|
||||
|
||||
$rrule = 'FREQ=WEEKLY;COUNT=1'; // This should generate 7 events in total, one for each day.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(7, $count);
|
||||
for ($i = 0; $i < 7; $i++) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + $i * DAYSECS)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This should generate 4 child event + 1 parent, since by then until bound would be hit.
|
||||
$until = $this->event->timestart + WEEKSECS * 4;
|
||||
$until = date('YmdThis', $until);
|
||||
$rrule = "FREQ=WEEKLY;BYDAY=MO;UNTIL=$until";
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(5, $count);
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + $i * WEEKSECS)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This should generate 4 events in total every monday and Wednesday of every 3rd week.
|
||||
$rrule = 'FREQ=WEEKLY;INTERVAL=3;BYDAY=MO,WE;COUNT=2';
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(4, $count);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + 3 * WEEKSECS))); // Monday event.
|
||||
$this->assertTrue($result);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + 2 * DAYSECS))); // Wednesday event.
|
||||
$this->assertTrue($result);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + 3 * WEEKSECS + 2 * DAYSECS))); // Wednesday event.
|
||||
$this->assertTrue($result);
|
||||
|
||||
// Forever event. This should generate events over time() + 10 year period, every 50th monday.
|
||||
$rrule = 'FREQ=WEEKLY;BYDAY=MO;INTERVAL=50';
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $time = $this->event->timestart + 50 * WEEKSECS * $i) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test recurrence rules for monthly frequency.
|
||||
*/
|
||||
public function test_monthly_events() {
|
||||
global $DB;
|
||||
$rrule = 'FREQ=MONTHLY;COUNT=3;BYMONTHDAY=2'; // This should generate 3 events in total.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(3, $count);
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => (strtotime("+$i month", $this->event->timestart))));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This much seconds after the start of the day.
|
||||
$offset = $this->event->timestart - mktime(0, 0, 0, date("n", $this->event->timestart), date("j", $this->event->timestart),
|
||||
date("Y", $this->event->timestart));
|
||||
$monthstart = mktime(0, 0, 0, date("n", $this->event->timestart), 1, date("Y", $this->event->timestart));
|
||||
|
||||
$rrule = 'FREQ=MONTHLY;COUNT=3;BYDAY=1MO'; // This should generate 3 events in total, first monday of the month.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(3, $count);
|
||||
$time = strtotime('1 Monday', strtotime("+1 months", $monthstart)) + $offset;
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time));
|
||||
$this->assertTrue($result);
|
||||
$time = strtotime('1 Monday', strtotime("+2 months", $monthstart)) + $offset;
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time));
|
||||
$this->assertTrue($result);
|
||||
|
||||
// This should generate 10 child event + 1 parent, since by then until bound would be hit.
|
||||
$until = strtotime('+1 day +10 months', $this->event->timestart);
|
||||
$until = date('YmdThis', $until);
|
||||
$rrule = "FREQ=MONTHLY;BYMONTHDAY=2;UNTIL=$until";
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(11, $count);
|
||||
for ($i = 0; $i < 11; $i++) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => (strtotime("+$i month", $this->event->timestart))));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This should generate 10 child event + 1 parent, since by then until bound would be hit.
|
||||
$until = strtotime('+1 day +10 months', $this->event->timestart);
|
||||
$until = date('YmdThis', $until);
|
||||
$rrule = "FREQ=MONTHLY;BYDAY=1MO;UNTIL=$until";
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(10, $count);
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$time = strtotime('1 Monday', strtotime("+$i months", $monthstart)) + $offset;
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This should generate 11 child event + 1 parent, since by then until bound would be hit.
|
||||
$until = strtotime('+10 day +10 months', $this->event->timestart); // 12 oct 2014.
|
||||
$until = date('YmdThis', $until);
|
||||
$rrule = "FREQ=MONTHLY;INTERVAL=2;BYMONTHDAY=2,5;UNTIL=$until";
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(12, $count);
|
||||
for ($i = 0; $i < 6; $i++) {
|
||||
$moffset = $i * 2;
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => (strtotime("+$moffset month", $this->event->timestart))));
|
||||
$this->assertTrue($result);
|
||||
// Event on the 5th of a month.
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => (strtotime("+3 days +$moffset month", $this->event->timestart))));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This should generate 11 child event + 1 parent, since by then until bound would be hit.
|
||||
$until = strtotime('+20 day +10 months', $this->event->timestart); // 22 oct 2014.
|
||||
$until = date('YmdTHis', $until);
|
||||
$rrule = "FREQ=MONTHLY;INTERVAL=2;BYDAY=1MO,3WE;UNTIL=$until";
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(12, $count);
|
||||
for ($i = 0; $i < 6; $i++) {
|
||||
$moffset = $i * 2;
|
||||
$time = strtotime("+$moffset month", $monthstart);
|
||||
$time2 = strtotime("+1 Monday", $time) + $offset;
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time2));
|
||||
$this->assertTrue($result);
|
||||
$time2 = strtotime("+3 Wednesday", $time) + $offset;
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => $time2)); // Event on the 5th of a month.
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// Forever event. This should generate events over 10 year period, on 2nd of every 12th month.
|
||||
$rrule = 'FREQ=MONTHLY;INTERVAL=12;BYMONTHDAY=2';
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $moffset = $i * 12,
|
||||
$time = strtotime("+$moffset month", $this->event->timestart)) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// Forever event. This should generate 10 child events + 1 parent over 10 year period, every 50th Monday.
|
||||
$rrule = 'FREQ=MONTHLY;BYDAY=1MO;INTERVAL=12';
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(11, $count);
|
||||
for ($i = 0, $moffset = 0, $time = $this->event->timestart; $time < $until; $i++, $moffset = $i * 12) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
$time = strtotime("+$moffset month", $monthstart);
|
||||
$time = strtotime("+1 Monday", $time) + $offset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test recurrence rules for yearly frequency.
|
||||
*/
|
||||
public function test_yearly_events() {
|
||||
global $DB;
|
||||
|
||||
$rrule = 'FREQ=YEARLY;COUNT=3;BYMONTH=12'; // This should generate 3 events in total.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(3, $count);
|
||||
for ($i = 0, $time = $this->event->timestart; $i < 3; $i++, $time = strtotime("+$i years", $this->event->timestart)) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// Create an event every december, until the time limit is hit.
|
||||
$until = strtotime('+20 day +10 years', $this->event->timestart);
|
||||
$until = date('YmdTHis', $until);
|
||||
$rrule = "FREQ=YEARLY;BYMONTH=12;UNTIL=$until"; // Forever event.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(11, $count);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $yoffset = $i * 2,
|
||||
$time = strtotime("+$yoffset years", $this->event->timestart)) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This should generate 5 events in total, every second year in the month of december.
|
||||
$rrule = 'FREQ=YEARLY;BYMONTH=12;INTERVAL=2;COUNT=5';
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(5, $count);
|
||||
for ($i = 0, $time = $this->event->timestart; $i < 5; $i++, $yoffset = $i * 2,
|
||||
$time = strtotime("+$yoffset years", $this->event->timestart)) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
$rrule = 'FREQ=YEARLY;BYMONTH=12;INTERVAL=2'; // Forever event.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(6, $count);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $yoffset = $i * 2,
|
||||
$time = strtotime("+$yoffset years", $this->event->timestart)) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This much seconds after the start of the day.
|
||||
$offset = $this->event->timestart - mktime(0, 0, 0, date("n", $this->event->timestart), date("j", $this->event->timestart),
|
||||
date("Y", $this->event->timestart));
|
||||
$yearstart = mktime(0, 0, 0, 1, 1, date("Y", $this->event->timestart));
|
||||
|
||||
$rrule = 'FREQ=YEARLY;COUNT=3;BYMONTH=12;BYDAY=1MO'; // This should generate 3 events in total.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(3, $count);
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$time = strtotime("+11 months +$i years", $yearstart);
|
||||
$time = strtotime("+1 Monday", $time) + $offset;
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// Create an event every december, until the time limit is hit.
|
||||
$until = strtotime('+20 day +10 years', $this->event->timestart);
|
||||
$until = date('YmdTHis', $until);
|
||||
$rrule = "FREQ=YEARLY;BYMONTH=12;UNTIL=$until;BYDAY=1MO";
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(11, $count);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
$time = strtotime("+11 months +$i years", $yearstart);
|
||||
$time = strtotime("+1 Monday", $time) + $offset;
|
||||
}
|
||||
|
||||
// This should generate 5 events in total, every second year in the month of december.
|
||||
$rrule = 'FREQ=YEARLY;BYMONTH=12;INTERVAL=2;COUNT=5;BYDAY=1MO';
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(5, $count);
|
||||
for ($i = $yoffset = 0, $time = $this->event->timestart; $i < 5; $i++, $yoffset = $i * 2) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
$time = strtotime("+11 months +$yoffset years", $yearstart);
|
||||
$time = strtotime("+1 Monday", $time) + $offset;
|
||||
}
|
||||
|
||||
$rrule = 'FREQ=YEARLY;BYMONTH=12;INTERVAL=2;BYDAY=1MO'; // Forever event.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(6, $count);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $yoffset = $i * 2) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
$time = strtotime("+11 months +$yoffset years", $yearstart);
|
||||
$time = strtotime("+1 Monday", $time) + $offset;
|
||||
}
|
||||
|
||||
$rrule = 'FREQ=YEARLY;INTERVAL=2'; // Forever event.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(6, $count);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $yoffset = $i * 2) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
$time = strtotime("+11 months +$yoffset years", $yearstart);
|
||||
$time = strtotime("+1 Monday", $time) + $offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class core_calendar_test_rrule_manager
|
||||
*
|
||||
* Wrapper to access protected vars for testing.
|
||||
*
|
||||
* @package core_calendar
|
||||
* @category test
|
||||
* @copyright 2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_tests_calendar_rrule_manager extends \core_calendar\rrule_manager{
|
||||
|
||||
/**
|
||||
* Magic method to get properties.
|
||||
*
|
||||
* @param $prop string property
|
||||
*
|
||||
* @return mixed
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function __get($prop) {
|
||||
if (property_exists($this, $prop)) {
|
||||
return $this->$prop;
|
||||
}
|
||||
throw new coding_exception('invalidproperty');
|
||||
}
|
||||
}
|
||||
+24
-1
@@ -57,6 +57,12 @@ M.core_comment = {
|
||||
this.view(0);
|
||||
return false;
|
||||
}, this);
|
||||
// Also handle space/enter key.
|
||||
handle.on('key', function(e) {
|
||||
e.preventDefault();
|
||||
this.view(0);
|
||||
return false;
|
||||
}, '13,32', this);
|
||||
}
|
||||
scope.toggle_textarea(false);
|
||||
},
|
||||
@@ -186,7 +192,17 @@ 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.util.get_string('deletecomment', 'moodle')+'"><img alt="" src="'+M.util.image_url('t/delete', 'core')+'" /></a></div>' + list[i].content;
|
||||
var tokens = {
|
||||
user: list[i].fullname,
|
||||
time: list[i].time
|
||||
};
|
||||
var deleteStr = Y.Escape.html(M.util.get_string('deletecommentbyon', 'moodle', tokens));
|
||||
list[i].content = '<div class="comment-delete">' +
|
||||
'<a href="#" role="button" id ="comment-delete-' + this.client_id + '-' + list[i].id + '"' +
|
||||
' title="' + deleteStr + '">' +
|
||||
'<img alt="' + deleteStr + '" 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);
|
||||
@@ -335,6 +351,7 @@ M.core_comment = {
|
||||
);
|
||||
},
|
||||
view: function(page) {
|
||||
var commenttoggler = Y.one('#comment-link-' + this.client_id);
|
||||
var container = Y.one('#comment-ctrl-'+this.client_id);
|
||||
var ta = Y.one('#dlg-content-'+this.client_id);
|
||||
var img = Y.one('#comment-img-'+this.client_id);
|
||||
@@ -351,6 +368,9 @@ M.core_comment = {
|
||||
if (img) {
|
||||
img.set('src', M.util.image_url('t/expanded', 'core'));
|
||||
}
|
||||
if (commenttoggler) {
|
||||
commenttoggler.setAttribute('aria-expanded', 'true');
|
||||
}
|
||||
} else {
|
||||
// hide
|
||||
container.setStyle('display', 'none');
|
||||
@@ -364,6 +384,9 @@ M.core_comment = {
|
||||
if (ta) {
|
||||
ta.set('value','');
|
||||
}
|
||||
if (commenttoggler) {
|
||||
commenttoggler.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
}
|
||||
if (ta) {
|
||||
//toggle_textarea.apply(ta, [false]);
|
||||
|
||||
+8
-2
@@ -263,7 +263,7 @@ class comment {
|
||||
'comments',
|
||||
'commentscount',
|
||||
'commentsrequirelogin',
|
||||
'deletecomment',
|
||||
'deletecommentbyon',
|
||||
),
|
||||
'moodle'
|
||||
);
|
||||
@@ -460,7 +460,13 @@ class comment {
|
||||
} else {
|
||||
$collapsedimage= 't/collapsed';
|
||||
}
|
||||
$html .= html_writer::start_tag('a', array('class' => 'comment-link', 'id' => 'comment-link-'.$this->cid, 'href' => '#'));
|
||||
$html .= html_writer::start_tag('a', array(
|
||||
'class' => 'comment-link',
|
||||
'id' => 'comment-link-'.$this->cid,
|
||||
'href' => '#',
|
||||
'role' => 'button',
|
||||
'aria-expanded' => 'false')
|
||||
);
|
||||
$html .= html_writer::empty_tag('img', array('id' => 'comment-img-'.$this->cid, 'src' => $OUTPUT->pix_url($collapsedimage), 'alt' => $this->linktext, 'title' => $this->linktext));
|
||||
$html .= html_writer::tag('span', $this->linktext.' '.$countstring, array('id' => 'comment-link-text-'.$this->cid));
|
||||
$html .= html_writer::end_tag('a');
|
||||
|
||||
@@ -68,7 +68,7 @@ class competency extends persistent {
|
||||
),
|
||||
'description' => array(
|
||||
'default' => '',
|
||||
'type' => PARAM_RAW
|
||||
'type' => PARAM_CLEANHTML
|
||||
),
|
||||
'descriptionformat' => array(
|
||||
'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN),
|
||||
@@ -676,7 +676,8 @@ class competency extends persistent {
|
||||
public static function share_same_framework(array $ids) {
|
||||
global $DB;
|
||||
list($insql, $params) = $DB->get_in_or_equal($ids);
|
||||
return $DB->count_records_select(self::TABLE, "id $insql", $params, "COUNT(DISTINCT(competencyframeworkid))") == 1;
|
||||
$sql = "SELECT COUNT('x') FROM (SELECT DISTINCT(competencyframeworkid) FROM {" . self::TABLE . "} WHERE id {$insql}) f";
|
||||
return $DB->count_records_sql($sql, $params) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -90,7 +90,7 @@ class competency_framework extends persistent {
|
||||
'type' => PARAM_RAW
|
||||
),
|
||||
'description' => array(
|
||||
'type' => PARAM_RAW,
|
||||
'type' => PARAM_CLEANHTML,
|
||||
'default' => ''
|
||||
),
|
||||
'descriptionformat' => array(
|
||||
|
||||
+3
-2
@@ -334,7 +334,8 @@ abstract class exporter {
|
||||
*/
|
||||
final protected static function get_format_field($definitions, $property) {
|
||||
$formatproperty = $property . 'format';
|
||||
if ($definitions[$property]['type'] == PARAM_RAW && isset($definitions[$formatproperty])
|
||||
if (($definitions[$property]['type'] == PARAM_RAW || $definitions[$property]['type'] == PARAM_CLEANHTML)
|
||||
&& isset($definitions[$formatproperty])
|
||||
&& $definitions[$formatproperty]['type'] == PARAM_INT) {
|
||||
return $formatproperty;
|
||||
}
|
||||
@@ -451,7 +452,7 @@ abstract class exporter {
|
||||
// This is a nested array of more properties.
|
||||
$thisvalue = self::get_read_structure_from_properties($type, $proprequired, $propdefault);
|
||||
} else {
|
||||
if ($definition['type'] == PARAM_TEXT) {
|
||||
if ($definition['type'] == PARAM_TEXT || $definition['type'] == PARAM_CLEANHTML) {
|
||||
// PARAM_TEXT always becomes PARAM_RAW because filters may be applied.
|
||||
$type = PARAM_RAW;
|
||||
}
|
||||
|
||||
@@ -248,7 +248,8 @@ abstract class persistent {
|
||||
$formatted = array();
|
||||
foreach ($properties as $property => $definition) {
|
||||
$propertyformat = $property . 'format';
|
||||
if ($definition['type'] == PARAM_RAW && array_key_exists($propertyformat, $properties)
|
||||
if (($definition['type'] == PARAM_RAW || $definition['type'] == PARAM_CLEANHTML)
|
||||
&& array_key_exists($propertyformat, $properties)
|
||||
&& $properties[$propertyformat]['type'] == PARAM_INT) {
|
||||
$formatted[$property] = $propertyformat;
|
||||
}
|
||||
@@ -616,6 +617,10 @@ abstract class persistent {
|
||||
// Validate_param() does not like false with PARAM_BOOL, better to convert it to int.
|
||||
$value = 0;
|
||||
}
|
||||
if ($definition['type'] === PARAM_CLEANHTML) {
|
||||
// We silently clean for this type. It may introduce changes even to valid data.
|
||||
$value = clean_param($value, PARAM_CLEANHTML);
|
||||
}
|
||||
validate_param($value, $definition['type'], $definition['null']);
|
||||
} catch (invalid_parameter_exception $e) {
|
||||
$errors[$property] = static::get_property_error_message($property);
|
||||
|
||||
@@ -71,7 +71,7 @@ class plan extends persistent {
|
||||
'type' => PARAM_TEXT,
|
||||
),
|
||||
'description' => array(
|
||||
'type' => PARAM_RAW,
|
||||
'type' => PARAM_CLEANHTML,
|
||||
'default' => ''
|
||||
),
|
||||
'descriptionformat' => array(
|
||||
|
||||
@@ -53,7 +53,7 @@ class template extends persistent {
|
||||
),
|
||||
'description' => array(
|
||||
'default' => '',
|
||||
'type' => PARAM_RAW,
|
||||
'type' => PARAM_CLEANHTML,
|
||||
),
|
||||
'descriptionformat' => array(
|
||||
'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN),
|
||||
|
||||
@@ -53,7 +53,7 @@ class user_evidence extends persistent {
|
||||
'type' => PARAM_TEXT
|
||||
),
|
||||
'description' => array(
|
||||
'type' => PARAM_RAW,
|
||||
'type' => PARAM_CLEANHTML,
|
||||
'default' => '',
|
||||
),
|
||||
'descriptionformat' => array(
|
||||
|
||||
@@ -288,6 +288,7 @@ function core_competency_pluginfile($course, $cm, $context, $filearea, $args, $f
|
||||
if ($filearea == 'userevidence' && $context->contextlevel == CONTEXT_USER) {
|
||||
if (user_evidence::can_read_user($context->instanceid)) {
|
||||
$file = $fs->get_file($context->id, 'core_competency', $filearea, $itemid, $filepath, $filename);
|
||||
$forcedownload = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -121,12 +121,13 @@ class behat_completion extends behat_base {
|
||||
public function activity_marked_as_complete($activityname, $activitytype, $completiontype) {
|
||||
if ($completiontype == "manual") {
|
||||
$imgalttext = get_string("completion-alt-manual-y", 'core_completion', $activityname);
|
||||
$xpathtocheck = "//input[@type='image'][contains(@alt, '$imgalttext')]";
|
||||
} else {
|
||||
$imgalttext = get_string("completion-alt-auto-y", 'core_completion', $activityname);
|
||||
$xpathtocheck = "//img[contains(@alt, '$imgalttext')]";
|
||||
}
|
||||
$csselementforactivitytype = "li.modtype_".strtolower($activitytype);
|
||||
|
||||
$xpathtocheck = "//img[contains(@alt, '$imgalttext')]";
|
||||
$this->execute("behat_general::should_exist_in_the",
|
||||
array($xpathtocheck, "xpath_element", $csselementforactivitytype, "css_element")
|
||||
);
|
||||
@@ -141,12 +142,13 @@ class behat_completion extends behat_base {
|
||||
public function activity_marked_as_not_complete($activityname, $activitytype, $completiontype) {
|
||||
if ($completiontype == "manual") {
|
||||
$imgalttext = get_string("completion-alt-manual-n", 'core_completion', $activityname);
|
||||
$xpathtocheck = "//input[@type='image'][contains(@alt, '$imgalttext')]";
|
||||
} else {
|
||||
$imgalttext = get_string("completion-alt-auto-n", 'core_completion', $activityname);
|
||||
$xpathtocheck = "//img[contains(@alt, '$imgalttext')]";
|
||||
}
|
||||
$csselementforactivitytype = "li.modtype_".strtolower($activitytype);
|
||||
|
||||
$xpathtocheck = "//img[contains(@alt, '$imgalttext')]";
|
||||
$this->execute("behat_general::should_exist_in_the",
|
||||
array($xpathtocheck, "xpath_element", $csselementforactivitytype, "css_element")
|
||||
);
|
||||
|
||||
@@ -25,15 +25,16 @@ Feature: Allow students to manually mark an activity as complete
|
||||
And I set the following fields to these values:
|
||||
| Enable completion tracking | Yes |
|
||||
And I press "Save and display"
|
||||
When I add a "Forum" to section "1" and I fill the form with:
|
||||
And I add a "Forum" to section "1" and I fill the form with:
|
||||
| Forum name | Test forum name |
|
||||
| Description | Test forum description |
|
||||
Then "Student First" user has not completed "Test forum name" activity
|
||||
And "Student First" user has not completed "Test forum name" activity
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on site homepage
|
||||
And I follow "Course 1"
|
||||
And I press "Mark as complete: Test forum name"
|
||||
When I press "Mark as complete: Test forum name"
|
||||
Then the "Test forum name" "forum" activity with "manual" completion should be marked as complete
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on site homepage
|
||||
|
||||
+1
-1
@@ -4,6 +4,6 @@
|
||||
"phpunit/dbUnit": "1.4.1",
|
||||
"sebastian/environment": "1.3.7",
|
||||
"sebastian/recursion-context": "1.0.2",
|
||||
"moodlehq/behat-extension": "3.31.5"
|
||||
"moodlehq/behat-extension": "3.31.7"
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user