Compare commits
1018 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 560c1c8ea3 | |||
| 7922f118db | |||
| f8872ff3e8 | |||
| d4edd6c99f | |||
| 301e79acae | |||
| baad460c91 | |||
| c3cf690cee | |||
| 6cc4b72fb9 | |||
| d311f9cc75 | |||
| bc6f61fe9f | |||
| b1455d0d74 | |||
| 01a2722f32 | |||
| 711f77e4ca | |||
| 615e2caf67 | |||
| cad44b4778 | |||
| c3cefce21c | |||
| 393133707a | |||
| 0fb12db343 | |||
| b1e4d7afc8 | |||
| eca84ae277 | |||
| c578a134fa | |||
| 53661a6767 | |||
| 3d115053d2 | |||
| 27db0b4376 | |||
| 45c79022ac | |||
| e15c728434 | |||
| 2c1fc0e086 | |||
| 35662296e5 | |||
| f5c99c5144 | |||
| 82efa6c9f2 | |||
| e02256507c | |||
| 794f42d81d | |||
| add479a88e | |||
| 429d0d711b | |||
| 47b2416012 | |||
| a01bd41865 | |||
| 555ad195a0 | |||
| 1ba20f5594 | |||
| d0890a7791 | |||
| b7a1f71911 | |||
| 7fd34aa0e6 | |||
| a78bb0cc83 | |||
| 2acdf0af6b | |||
| ba5ecbc72a | |||
| 4bb2eaa7d0 | |||
| 55e3c2cd82 | |||
| d320d0b285 | |||
| 9424bcb66e | |||
| 08a8eb917e | |||
| df78a7902b | |||
| 43fa28331e | |||
| c512b7cc65 | |||
| 8a7078b28b | |||
| dcb6e4169b | |||
| 8cd148efe0 | |||
| e2fb6ffa24 | |||
| 71a5c487ce | |||
| 23ea3287ea | |||
| a3e6f40dbe | |||
| 42cc95c245 | |||
| 9ea075bb32 | |||
| 08839bbcb8 | |||
| abfdce235b | |||
| 70427c648e | |||
| 5287178537 | |||
| abaaa8cdae | |||
| 9312fbe16e | |||
| d120a7baf2 | |||
| fb494ae9c8 | |||
| 09f2963373 | |||
| 6917b452bf | |||
| 919efdf632 | |||
| b3e58b7d0b | |||
| 4844fc124c | |||
| d98f1751e8 | |||
| 9ab44b3f99 | |||
| 6c32a2f003 | |||
| f23a63b545 | |||
| 6758c0fa99 | |||
| eef875e0c1 | |||
| 150735fbdb | |||
| accfa102fe | |||
| e5663dfd70 | |||
| 91d309074f | |||
| 9a3f511056 | |||
| de4d45ce8e | |||
| 2d22a9d474 | |||
| c64eea0a51 | |||
| 0e099cc444 | |||
| fda6ef7a1f | |||
| 7b8dba0b52 | |||
| c3673c5531 | |||
| 1efa930202 | |||
| 87c3a9fd9f | |||
| 1f26c7053a | |||
| fff86d45c1 | |||
| 54a0a16b53 | |||
| 8cb63784e5 | |||
| 9ac938636f | |||
| 3bdbe1b08b | |||
| eaccd0d486 | |||
| 0bc9604212 | |||
| 17a0f7350f | |||
| 0ed5fa3b88 | |||
| 34b3aa4d49 | |||
| 5f22097114 | |||
| 43fed596e5 | |||
| acb1bf6fac | |||
| 6a748d5e8b | |||
| d0a9524463 | |||
| 42e7fb5e49 | |||
| 5c550aaafc | |||
| ea6e1851e9 | |||
| bd0d739550 | |||
| 75d8d06ffb | |||
| 8c92e24434 | |||
| cbd0157d66 | |||
| c9bcb47d54 | |||
| c58f3ed7ff | |||
| 3b34d79d36 | |||
| 8f19a957e2 | |||
| 94cc2c769c | |||
| 4f718dc60c | |||
| d1b0786a51 | |||
| 1cdd43b12b | |||
| b807cc6334 | |||
| 8cbfc9614a | |||
| 9de894db2d | |||
| 7a2db63954 | |||
| 8a3894de89 | |||
| 60ca7d5b07 | |||
| 697ecc6db1 | |||
| e2536570a3 | |||
| af90211efd | |||
| 78855a6c7b | |||
| 1640389bcf | |||
| a70c9434d7 | |||
| 441b8d6e69 | |||
| de61d99ad1 | |||
| f311ba2714 | |||
| 06032cd928 | |||
| d05a5b126c | |||
| 45e4351ade | |||
| 2811b3d262 | |||
| 0cb2846998 | |||
| 4db1863687 | |||
| 4a256d6e8e | |||
| 44666a2d31 | |||
| a122d95343 | |||
| af11bd26b6 | |||
| b8aa36a063 | |||
| a10d621fc5 | |||
| a322a0ab53 | |||
| 969d8f0e3f | |||
| 36cfbfe841 | |||
| 314cc22a28 | |||
| bd1fae5fc0 | |||
| ac23be5d24 | |||
| 6e72e0a6aa | |||
| 57bafe6ebc | |||
| 4ae00d74bc | |||
| 60293b276a | |||
| 4673a6a8fc | |||
| ce5a85c354 | |||
| ff952aa064 | |||
| 675fa4b813 | |||
| 73b0524202 | |||
| 58c57f94d6 | |||
| b6ced2f7e4 | |||
| 6123c76bc7 | |||
| 8beb495df4 | |||
| 90b46f39a4 | |||
| 1f3a85aa54 | |||
| 98e4f24ff6 | |||
| 48763c4d21 | |||
| d91225aae4 | |||
| a5436ae8ce | |||
| 7a9000b1ff | |||
| 85d47590cc | |||
| 8789750cc5 | |||
| b15478526a | |||
| 8786deb033 | |||
| 59e8879130 | |||
| e81aa9e8e6 | |||
| 8a2e491ee7 | |||
| 3a6857090e | |||
| d2ff78d173 | |||
| ef37c13e29 | |||
| a8d816982a | |||
| ac7f6f4b96 | |||
| 6da08c0e2c | |||
| f72e3fbbf2 | |||
| 8afe4de645 | |||
| 1fc88334a6 | |||
| 7643bd4ca9 | |||
| 1b9f0beaf3 | |||
| 993612b00e | |||
| 4eb084f1a2 | |||
| 34b3d144fe | |||
| c61decc816 | |||
| 5864eb6017 | |||
| d9131593c7 | |||
| 16e566b99f | |||
| bd010f8854 | |||
| a364f61121 | |||
| cf2fb7579b | |||
| 945aa97617 | |||
| ca4349c1ce | |||
| 267b45e0a1 | |||
| 4c53ec3355 | |||
| 7bd19d562d | |||
| 49e3cda704 | |||
| e8ec840f3c | |||
| 0c018318c6 | |||
| 3bd02768df | |||
| 39adfbde72 | |||
| a42e313460 | |||
| d6b99cf123 | |||
| 77eb24f886 | |||
| d4609de200 | |||
| 6c2d9137b3 | |||
| 5dc182dd6e | |||
| 88e2d12038 | |||
| b6a84b6fdd | |||
| 3e2821e6f5 | |||
| d93610d4b0 | |||
| 34d55059cb | |||
| 6257097a3f | |||
| cd35f43eac | |||
| 788105dd3d | |||
| 80cee53996 | |||
| dd94734d9a | |||
| 786538f74f | |||
| 925ec67b22 | |||
| b159b57207 | |||
| c01c608fdc | |||
| ca887bc9bc | |||
| 2ff9ac5608 | |||
| f63fc5c41a | |||
| d276541169 | |||
| 72b266d54e | |||
| 8f3702ee1a | |||
| 54b951fb5f | |||
| 9844660858 | |||
| d0012b72ad | |||
| 623b678c3e | |||
| 75f6d03855 | |||
| 236a2fca9b | |||
| a7c5e2d24d | |||
| 4ae75c1847 | |||
| d7788d69f6 | |||
| a033a6b4c1 | |||
| 0626a71686 | |||
| 756fbd91fd | |||
| 3b5c7727f2 | |||
| e5b13b7fc0 | |||
| 49c29a3f46 | |||
| 92fdea041d | |||
| a7fd949571 | |||
| 32d77062b4 | |||
| 92416730d1 | |||
| 4b95721a26 | |||
| 29d526b495 | |||
| 227d3fcbd3 | |||
| 05c3b5dd2f | |||
| cf55843d07 | |||
| c7c613b45b | |||
| 06450ed398 | |||
| 3d986cf8cd | |||
| 6e3013be7c | |||
| 63b66cf276 | |||
| aaf95d0161 | |||
| 473aab4ffb | |||
| 2504a9d2ec | |||
| a24c22cd12 | |||
| 2ccad8f967 | |||
| b152769f44 | |||
| 77b4970559 | |||
| 8492c192af | |||
| f621e8e3d8 | |||
| 5a804c0f54 | |||
| bfff058cc8 | |||
| 9e72a16e14 | |||
| 90317c258b | |||
| fcb5548c38 | |||
| 654f0c0cf7 | |||
| 402469ce8b | |||
| fb092979bf | |||
| 768779972e | |||
| b0124c9703 | |||
| d2674929ba | |||
| 47ba8d481e | |||
| 814054616f | |||
| 7bc37de925 | |||
| 0f00c5c594 | |||
| 4b8ed14f54 | |||
| 9840da227e | |||
| 4948ac2e0c | |||
| 954b1b5504 | |||
| 9e81532432 | |||
| 6726f0073e | |||
| 5011fb91ff | |||
| 657beb8924 | |||
| 1d5c188798 | |||
| ac7cbc012e | |||
| 05f1cccc37 | |||
| 1081b8ec84 | |||
| af0a06b521 | |||
| 9963b0b6a6 | |||
| d7b20f1650 | |||
| 694cb9b053 | |||
| 14dcbdb0f4 | |||
| 38753adc60 | |||
| da7c844c30 | |||
| 0f6d896e43 | |||
| 0137a13d55 | |||
| 254bf485f1 | |||
| e81fce929a | |||
| c1536ff1ef | |||
| 43e1ed2cf3 | |||
| 46f15f03d2 | |||
| 0cddce2c56 | |||
| 18cbf0db95 | |||
| 3b2c61fdb8 | |||
| d64a0e9173 | |||
| c88b113c1f | |||
| aa6699f4db | |||
| 1e557db89d | |||
| 5e99c1f9a1 | |||
| 84197f41ca | |||
| b604cb81e4 | |||
| 05f853e2db | |||
| a77864208e | |||
| 4b3521ec69 | |||
| 294673628e | |||
| 081e303415 | |||
| b14d0e4713 | |||
| d2dc75fb4e | |||
| 7c95cf02af | |||
| a7f3f08722 | |||
| 5db72d63c5 | |||
| d58d0239db | |||
| e9ea8e9b63 | |||
| e523503fb8 | |||
| 4da2fefa4d | |||
| 0f85e6ba0b | |||
| 8baa0cab98 | |||
| 5a730d0a80 | |||
| 5a36d6292d | |||
| b7a4701d1f | |||
| 6cb665f18c | |||
| eae5890112 | |||
| fd9ecb595f | |||
| 1fdc20cbdb | |||
| 1830fc428c | |||
| c53069c60a | |||
| 97a0c05d46 | |||
| 700e92b8d4 | |||
| 28980dbee6 | |||
| 39616d39c4 | |||
| 178af4770e | |||
| eb57e9e60b | |||
| 5f8e23c1a9 | |||
| 7ed78289b5 | |||
| 77458d700b | |||
| 52b617673c | |||
| efcbed9b73 | |||
| 2dd8a44c20 | |||
| 3b8005fae7 | |||
| 097abcc59c | |||
| fe38aff3a7 | |||
| e78a2d0e44 | |||
| f257805573 | |||
| 2e5d19ddfc | |||
| 708a1cec00 | |||
| 3caeaa448e | |||
| 3e230a757c | |||
| c8b1ecedc7 | |||
| 4520f30def | |||
| d1af85a398 | |||
| 75cf024576 | |||
| b5f9a3b0e3 | |||
| 53165cbc05 | |||
| 13ad8def1b | |||
| f246359c29 | |||
| 3bc786adb1 | |||
| e75bd18064 | |||
| 7a35f9f9fc | |||
| 6026b9157d | |||
| 557b8de4cf | |||
| da337c3f39 | |||
| ef103c84bb | |||
| f68ebb8282 | |||
| d65c2e5d44 | |||
| 03b992e43b | |||
| e8bc65a85e | |||
| c7aac02925 | |||
| 832a19829b | |||
| d0ddf08dc0 | |||
| 30d3f1d220 | |||
| a7f94caa2e | |||
| aadac9873b | |||
| ed016dd8fc | |||
| afe1dde0c8 | |||
| 1fdb431dfe | |||
| 5a407be1d4 | |||
| 8793ba310d | |||
| 74d458916b | |||
| 370f95edf0 | |||
| 79dc70d99c | |||
| 4b23a6fa05 | |||
| 7a3b3ac4d2 | |||
| 85ed2ec89b | |||
| 4a3728ee50 | |||
| c959f27415 | |||
| 2bcfd98eab | |||
| e727d2aa2a | |||
| 6cef890fbc | |||
| afd76d0c5d | |||
| 0fc55f1d0a | |||
| 83f27b6cca | |||
| 530e43776b | |||
| 8e4c96c519 | |||
| 7e8638914f | |||
| 36d31f8081 | |||
| d8669d5bef | |||
| ee3433a44b | |||
| 7d64b40db1 | |||
| 08d19eaeb9 | |||
| e3559db951 | |||
| 4c37aadb32 | |||
| e4e6173568 | |||
| 58605be7f2 | |||
| 9684aab5a2 | |||
| 4acc7c0106 | |||
| 279f7582a4 | |||
| f8758dc3d3 | |||
| 0f567efc8f | |||
| 727c220c7a | |||
| 33fec82b0a | |||
| ba55a0f6c9 | |||
| ea6deb7e09 | |||
| 433bba6b2f | |||
| 0534f2dd9b | |||
| 1e27536646 | |||
| cd118a23e7 | |||
| 4e66dc96b3 | |||
| 81ff8adcc5 | |||
| 527b36b905 | |||
| ab2d7d4f3d | |||
| 426e9c79ac | |||
| 2254ea48a9 | |||
| 1be71b3131 | |||
| 7064882041 | |||
| 56f24fadd4 | |||
| 91b9fb3749 | |||
| a8416f35c5 | |||
| 95899cab2f | |||
| 0d435a3d7c | |||
| abb767192f | |||
| f0a60c5f2f | |||
| ee262fa7f5 | |||
| c951ff9d39 | |||
| 17f8a9f75f | |||
| 1b883b8c20 | |||
| fc4f36be11 | |||
| 5689cb3a27 | |||
| 6050809347 | |||
| f08bce9a54 | |||
| 67051eb403 | |||
| 6dc4e89073 | |||
| 5f6877ddee | |||
| 381f53e27e | |||
| 466fb01621 | |||
| a8f5f4b6e2 | |||
| af5e39e1b6 | |||
| 661d4816df | |||
| 184309fa55 | |||
| 26af11ece6 | |||
| 4bc9eec96f | |||
| 1eb3364a9e | |||
| fef821d726 | |||
| 334827b61e | |||
| 69127087c9 | |||
| b43863f6a9 | |||
| 86cef7fbca | |||
| b1cd2f8248 | |||
| 80e86364f4 | |||
| 59c487a7f3 | |||
| 18c49b1420 | |||
| d161d6bbe9 | |||
| 981ca84aef | |||
| e8efd1fbe2 | |||
| 796819b631 | |||
| a62d50ffb8 | |||
| 4b350436cd | |||
| 7452709d69 | |||
| 07173cac05 | |||
| ab97bc0a1e | |||
| 79b958a4a7 | |||
| c93707771f | |||
| 790b3da848 | |||
| da51402724 | |||
| 4973a0cb41 | |||
| e3d9d02381 | |||
| 3822616ba6 | |||
| db7d0186ee | |||
| d0720e784e | |||
| c68f17dfc5 | |||
| 202d7e6ad3 | |||
| 5821a7c5f1 | |||
| b4aba7cac2 | |||
| a6bc89e4d2 | |||
| f6868f9c9d | |||
| 1fc4e2b414 | |||
| 2f64dd6dc8 | |||
| 9372820a6e | |||
| f2237bd7dc | |||
| b014bb8b4c | |||
| b9f0a2b869 | |||
| 8fbb62c9bc | |||
| e859dfdfb8 | |||
| 0efd7e9d2e | |||
| 14e8af6687 | |||
| 0bfeff4c5e | |||
| a60b7797cd | |||
| 72e810ca24 | |||
| c7b085daa0 | |||
| 5fa41023da | |||
| 376ba9f726 | |||
| 12cc56072a | |||
| 9ec19c21d2 | |||
| 07b248886d | |||
| d835ad6935 | |||
| 2e91dfc66c | |||
| c7f4deecbd | |||
| 0047905f91 | |||
| 452ff25284 | |||
| 1a9dc90313 | |||
| 3155dcf541 | |||
| 06737a22ea | |||
| c5dd64f1e9 | |||
| abafd9a81b | |||
| c41a6dfcd3 | |||
| 869a5896d0 | |||
| f568a40ac7 | |||
| 06a42cff90 | |||
| cda91161df | |||
| aa39783515 | |||
| f26c65003d | |||
| 2b4dbc30ee | |||
| cdc4b4647d | |||
| c6a86e6686 | |||
| 6796b5630c | |||
| bfa8f562f7 | |||
| 390cb87e76 | |||
| 22ecd9658c | |||
| ad7577e144 | |||
| 498cc62c33 | |||
| ce09c74ab1 | |||
| 9aa1fe0f77 | |||
| 51c14ac9d3 | |||
| 053617fcb6 | |||
| 74f48cfa05 | |||
| 1d2247bed6 | |||
| 5039961852 | |||
| be09a8982b | |||
| ea3f5b5d95 | |||
| a6e4a83d99 | |||
| ae14fe3bb6 | |||
| 1a60cd4e5c | |||
| 9e532d0ca3 | |||
| 00410c03d7 | |||
| b6a2a20236 | |||
| 2980ec2eea | |||
| b19a900a9b | |||
| 5878abf8c6 | |||
| b4b50810d4 | |||
| eea2c47d6f | |||
| 164cc5f0e9 | |||
| 504dc60048 | |||
| 052f235f24 | |||
| ecd106d68d | |||
| 1498e85483 | |||
| ebb5c53b51 | |||
| 93334057de | |||
| 685ed22b13 | |||
| 86ebf30fcf | |||
| 2c6fe0c698 | |||
| da6fc6fe17 | |||
| 8460efc65b | |||
| 7d6f0b8224 | |||
| 9d146cf08a | |||
| ac23d64400 | |||
| 1c4b87211d | |||
| 461bac938a | |||
| be58294484 | |||
| a01c00333c | |||
| 05342e935c | |||
| 093aeffbea | |||
| 822f3e1178 | |||
| d6a0927ba1 | |||
| a3d12f993a | |||
| 0460f175d2 | |||
| 5d2b57a822 | |||
| 95ddab5f31 | |||
| 057def4fa5 | |||
| d2a0dce409 | |||
| 574abfafc6 | |||
| 65f2bb0984 | |||
| b7ee8b96a9 | |||
| 49ca7310a3 | |||
| 29c8710bf5 | |||
| 397b5c063e | |||
| 9a17f7d3cc | |||
| 929088ec57 | |||
| 336f2df35d | |||
| 214dd19aa2 | |||
| 683fbbb8d7 | |||
| 682f6d57cd | |||
| dacf5b62e8 | |||
| aa713f26bd | |||
| 9da794f8aa | |||
| b7c61a8b99 | |||
| 0c3fe2f5f0 | |||
| bde942541e | |||
| 1fed67296c | |||
| 60c61daebe | |||
| f30c9280d4 | |||
| 82c3032d98 | |||
| 0d4706dd22 | |||
| 205a3cd8a0 | |||
| da18133994 | |||
| 01a3633cc7 | |||
| 9bec0c088f | |||
| cd08b5c878 | |||
| 1cdbc04d86 | |||
| c8cada1552 | |||
| ff77405577 | |||
| 8b8bd17aac | |||
| 9655e2e335 | |||
| 4e60c378cf | |||
| ff1fb4619b | |||
| 4523e6af16 | |||
| 4f6f2013bf | |||
| 9ed79d1801 | |||
| 066c7987b2 | |||
| bb925bb8ba | |||
| 1f3be76708 | |||
| 5e0e88e5a3 | |||
| 9260f1bdd2 | |||
| 9dae491a94 | |||
| 8e3c3ac499 | |||
| 4cf35a316e | |||
| a04662aad4 | |||
| c60752f45c | |||
| a62e342c3d | |||
| ad739cd700 | |||
| 5886e039ec | |||
| bc6bf8d1e6 | |||
| 0d62d8196e | |||
| 7544d10492 | |||
| 50a4b42ed7 | |||
| 7831560995 | |||
| 58940c96c4 | |||
| 1a449aa5e7 | |||
| 855b35604d | |||
| 20ea7f2f79 | |||
| 2d2b3c39b3 | |||
| 9f837ac934 | |||
| 89478f00cd | |||
| e66647085e | |||
| d76bba6e24 | |||
| d8bbfdfbe0 | |||
| 405d2962d2 | |||
| 4474895d37 | |||
| d9debeb038 | |||
| 18cf2255f0 | |||
| ccf721640c | |||
| a2de489c3e | |||
| 994d7ebdc3 | |||
| bc583cacfd | |||
| a7228f586b | |||
| 9f124aa5ad | |||
| f620ae6c11 | |||
| 5cae1b013f | |||
| 76edea3d61 | |||
| 558f2de1b9 | |||
| 174405bd5d | |||
| 1bdb3dcd2b | |||
| 643786623e | |||
| ae5e95af2d | |||
| 35dc874292 | |||
| c291dd69ea | |||
| 4fae47dedc | |||
| 688c3bffbd | |||
| 6026121e10 | |||
| 762789f5d7 | |||
| f25af6571f | |||
| e4f1467a75 | |||
| ddc6c118c6 | |||
| 12de66b2bf | |||
| 514132f8a8 | |||
| c181c6137d | |||
| 3bf3451872 | |||
| 2c66b71ab4 | |||
| 95455fc76a | |||
| 658c7b8a7a | |||
| ee2316f022 | |||
| dd0f944141 | |||
| 537d8f8a58 | |||
| 2a2d53bd97 | |||
| 114c924d94 | |||
| 622425d2fb | |||
| 2215cc347a | |||
| ed79029f0e | |||
| 286232d695 | |||
| 97d98e9920 | |||
| 3b0c725de1 | |||
| 4dcd073864 | |||
| 800c5757c3 | |||
| 4cc817aa33 | |||
| 41ffd24e82 | |||
| 1f304be72c | |||
| 304343830b | |||
| d3dc9a334c | |||
| bb6cacb913 | |||
| 4eff6ed9fe | |||
| 954ba4bce4 | |||
| b7aac8b6d1 | |||
| be9cd2ff26 | |||
| b719bf20b4 | |||
| d79c15550f | |||
| 92cfad95c8 | |||
| f4f853f8cd | |||
| 51cefce972 | |||
| f0be68a15e | |||
| c8d8b400f9 | |||
| 6ef0160a64 | |||
| 919251f295 | |||
| 712bcc3e09 | |||
| ed928e942c | |||
| a16ec93666 | |||
| 28c8189c7f | |||
| c004ced96b | |||
| ef84ab84bf | |||
| b5906ea400 | |||
| 76e6fbd776 | |||
| 7e51747ede | |||
| 22785530b9 | |||
| d8732ee8b7 | |||
| c11ea21997 | |||
| 1619a06942 | |||
| bc6835b1d1 | |||
| 122d59390f | |||
| cb4beb2d2c | |||
| c3f4867a41 | |||
| ca6904b6c9 | |||
| 955172a5a0 | |||
| d1e2ee9eec | |||
| 5832a8f737 | |||
| 1edad3fd90 | |||
| 01d2c82e1b | |||
| 4b450df00d | |||
| e09fc9f3c1 | |||
| ea0b123d07 | |||
| 301d9518ed | |||
| 3c108a95df | |||
| 8ae15861de | |||
| ec99b59381 | |||
| 1596dca3d0 | |||
| ba8a53a4f9 | |||
| 86e56e40bd | |||
| 3db9731fb6 | |||
| 3d6b490b81 | |||
| 2122af37b6 | |||
| c45ca47759 | |||
| d121290607 | |||
| bdd014e2c2 | |||
| 7bcdac4882 | |||
| c982630a94 | |||
| dc56547079 | |||
| fe4bb9a3f6 | |||
| 96cf04159c | |||
| 1de08a0e60 | |||
| 4207e4771a | |||
| 81c7328717 | |||
| da660f362b | |||
| 4417a705cf | |||
| cc6641f040 | |||
| 6e4059838c | |||
| 211c473452 | |||
| 9505fcffe4 | |||
| 7759839612 | |||
| 9cf6081027 | |||
| 6d4fe99033 | |||
| bf296d3cf3 | |||
| 98159ddec2 | |||
| 2c8e21ac35 | |||
| 073a972375 | |||
| 40818b7d48 | |||
| b64526c715 | |||
| 8be321e3d9 | |||
| 134a6ddcf0 | |||
| 478cac84e1 | |||
| c07c4324c7 | |||
| bebd6a6cef | |||
| 272105604a | |||
| 8401eb9425 | |||
| 2e6cc99352 | |||
| 23f186b5c8 | |||
| af06f5def5 | |||
| 46c30b7e1b | |||
| c8802b6e6c | |||
| 392aa3ff35 | |||
| 5008f4fffd | |||
| 7b8783b751 | |||
| 8fb30e9d1f | |||
| dfdea57b26 | |||
| c4eab12234 | |||
| 57efe7bdba | |||
| 11270c4b2d | |||
| f7c0e172c6 | |||
| 79cb5006f9 | |||
| bea68c50d6 | |||
| 288ab0b16c | |||
| 4978c2ae05 | |||
| f5dd4b478c | |||
| bcdda24942 | |||
| 8c12e634ea | |||
| 433222d6c0 | |||
| d39e66a6cd | |||
| bce7ef0bd5 | |||
| 6e74e428af | |||
| 0ad16081a2 | |||
| 8fd47854a6 | |||
| e7d8c56098 | |||
| ffb486a996 | |||
| 2a31efea1b | |||
| c1dfb624bf | |||
| 1ebac4eab3 | |||
| c5250462f6 | |||
| 351f4729e2 | |||
| b26a15ff67 | |||
| f41aaa2551 | |||
| dc44a83f83 | |||
| a3ad98f25d | |||
| 9ab6e7d851 | |||
| 24e89189a7 | |||
| 48919dba12 | |||
| 3f7511f022 | |||
| e4737333eb | |||
| e9ec512875 | |||
| 60a6533433 | |||
| 4fe8c2c799 | |||
| 5dbb8b3909 | |||
| e7b6fc35bf | |||
| 1db4d513a7 | |||
| 4964633e3a | |||
| 5dc899f004 | |||
| 8348ec06ea | |||
| d80b2c736e | |||
| 69d963254e | |||
| 24c954c181 | |||
| 1cf70434e2 | |||
| 0454409240 | |||
| 13cea3d030 | |||
| 20043f4392 | |||
| 6c227b64e2 | |||
| d4eb62d66b | |||
| 031e2352da | |||
| 29c2cafaee | |||
| 6c413970b7 | |||
| 60fcc78c1f | |||
| 91f07e7e8b | |||
| 520f292699 | |||
| 82f43805fe | |||
| cbb612d2bf | |||
| 64a21db766 | |||
| 84031db72b | |||
| 4dd5ce20ff | |||
| 960a321544 | |||
| 13c6a81607 | |||
| f078a515c7 | |||
| 58d9daa435 | |||
| 416b9b3803 | |||
| b0237a093f | |||
| b9cdd1150e | |||
| 118242be26 | |||
| 7165b1586f | |||
| 09b91f0fff | |||
| d000c1a380 | |||
| e4ddfbe0f8 | |||
| 25f9cba3d2 | |||
| b9e16a5137 | |||
| 4802b1ed8e | |||
| 0dc499ec7f | |||
| 4c8664099e | |||
| d86662e14e | |||
| fc97a877fd | |||
| d60d1d0db8 | |||
| 4746dc557a | |||
| b4a8c66cca | |||
| b0997b27fc | |||
| d4ef1bbbcf | |||
| 771b35c6fe | |||
| 613b32bbb8 | |||
| 813db77aa2 | |||
| e4c30d9639 | |||
| 954e384783 | |||
| d1bb51aaad | |||
| 4db0ad5756 | |||
| 29b18e6c68 | |||
| 93cf55eb72 | |||
| 8069eac74b | |||
| 74d4a8e314 | |||
| 8228d7a653 | |||
| 9e5d7687e3 | |||
| db13f65136 | |||
| 734581a35e | |||
| 20431aacf8 | |||
| 27bab164f2 | |||
| 48dedbe914 | |||
| 752fb7113d | |||
| a33fc4907e | |||
| c0f5255cb9 | |||
| aefa1b0e3e | |||
| ae61eea349 | |||
| b78cd7b0c9 | |||
| 8574fb6f41 | |||
| 5f11ce035d | |||
| 7bf288a640 | |||
| 24a3f392ba | |||
| 7500249a74 | |||
| 847f1dd489 | |||
| fce7fd554c | |||
| d04e51bd18 | |||
| 6134a1f844 | |||
| 5ffd03ea74 | |||
| 4fffc06f65 | |||
| 48f747b8da | |||
| c8b05c3e83 | |||
| 1deb9c4d18 | |||
| b3ae0c52bf | |||
| a67482cb0e | |||
| cd9dab04b9 | |||
| 829b842997 | |||
| 02338685a8 | |||
| 7c742620d2 | |||
| 7934b786a9 | |||
| 43b13e92b8 | |||
| bf2bee168a | |||
| 5c9ce5a281 | |||
| 89ad278957 | |||
| a3106cf3eb | |||
| 58c28d44e0 | |||
| 1fdba7b992 | |||
| f8e60e04b9 | |||
| 713b818c40 | |||
| 038fd6e78f | |||
| 4453f3255f | |||
| be06282e1b | |||
| e60224a32a | |||
| 0733e9883a | |||
| ec01b3201d | |||
| 5efc195a32 | |||
| f0697b7088 | |||
| 8a06e21c2a | |||
| 17bca7a2b9 | |||
| c22a0ad980 | |||
| 617947c0b7 | |||
| fad64c0d6b | |||
| 8fedda2fc4 | |||
| b27636c4db | |||
| 0db0bcd60b | |||
| 133defa353 | |||
| de78a8b251 | |||
| 6b3ca4dd2c | |||
| 512f5d9086 | |||
| dfd948b0f0 | |||
| 6336c04705 | |||
| 0377e4fbc9 | |||
| 18d5870e8b | |||
| d55eadbc92 | |||
| a1972f5717 | |||
| 0571a6856d | |||
| 3c61d125c4 | |||
| 8fec03bc81 | |||
| ef5ec161ee | |||
| 167c498cc8 | |||
| 235aa1b1d1 | |||
| 4205983fad | |||
| 0408edbb13 | |||
| fb69297c66 | |||
| 3557f63112 | |||
| bb597bbc1a | |||
| 73e7cca3c2 | |||
| 949a68d987 | |||
| 7a73b806c3 | |||
| eb30de6812 | |||
| 313b6da1b2 | |||
| bcd8c30b45 | |||
| fbe50c0945 | |||
| a87523d86e | |||
| f34634eaf3 | |||
| 84d356f73c | |||
| 6642c7d756 | |||
| cd8f8a8cb5 | |||
| c2fd66ab6e | |||
| 3cba96e344 | |||
| e5bc3e3dde | |||
| 2fc7799975 | |||
| 02d61e0481 | |||
| a67e827913 | |||
| a7fce60867 | |||
| 61c9217b2a | |||
| 004f868825 | |||
| 5c2a800bfa |
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This script fixed incorrectly deleted users.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage cli
|
||||
* @copyright 2013 Petr Skoda (http://skodak.org)
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__.'/../../config.php');
|
||||
require_once($CFG->libdir.'/clilib.php');
|
||||
|
||||
|
||||
// Now get cli options.
|
||||
list($options, $unrecognized) = cli_get_params(array('help'=>false),
|
||||
array('h'=>'help'));
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
if ($options['help']) {
|
||||
$help =
|
||||
"Fix incorrectly deleted users.
|
||||
|
||||
This scripts detects users that are marked as deleted instead
|
||||
of calling delete_user().
|
||||
|
||||
Deleted users do not have original username, idnumber or email,
|
||||
we must also delete all roles, enrolments, group memberships, etc.
|
||||
|
||||
Please note this script does not delete any public information
|
||||
such as forum posts.
|
||||
|
||||
Options:
|
||||
-h, --help Print out this help
|
||||
|
||||
Example:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/fix_deleted_users.php
|
||||
";
|
||||
|
||||
echo $help;
|
||||
die;
|
||||
}
|
||||
|
||||
cli_heading('Looking for sloppy user deletes');
|
||||
|
||||
// Look for sloppy deleted users where somebody only flipped the deleted flag.
|
||||
$sql = "SELECT *
|
||||
FROM {user}
|
||||
WHERE deleted = 1 AND email LIKE '%@%' AND username NOT LIKE '%@%'";
|
||||
$rs = $DB->get_recordset_sql($sql);
|
||||
foreach ($rs as $user) {
|
||||
echo "Redeleting user $user->id: $user->username ($user->email)\n";
|
||||
delete_user($user);
|
||||
}
|
||||
|
||||
cli_heading('Deleting all leftovers');
|
||||
|
||||
$DB->set_field('user', 'idnumber', '', array('deleted'=>1));
|
||||
|
||||
$DB->delete_records_select('role_assignments', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
|
||||
$DB->delete_records_select('cohort_members', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
|
||||
$DB->delete_records_select('groups_members', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
|
||||
$DB->delete_records_select('user_enrolments', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
|
||||
$DB->delete_records_select('user_preferences', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
|
||||
$DB->delete_records_select('user_info_data', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
|
||||
$DB->delete_records_select('user_lastaccess', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
|
||||
$DB->delete_records_select('external_tokens', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
|
||||
$DB->delete_records_select('external_services_users', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
|
||||
|
||||
exit(0);
|
||||
@@ -892,4 +892,119 @@
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
</MOODLE>
|
||||
<MOODLE version="2.6" requires="2.2">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mysql" version="5.1.33" />
|
||||
<VENDOR name="postgres" version="8.3" />
|
||||
<VENDOR name="mssql" version="9.0" />
|
||||
<VENDOR name="oracle" version="10.2" />
|
||||
</DATABASE>
|
||||
<PHP version="5.3.3" 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="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opensslrecommended" />
|
||||
</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="optional">
|
||||
</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="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_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="64M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="safe_mode" value="0" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingsafemode" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
+30
-30
@@ -27,37 +27,37 @@ require_once('../config.php');
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
|
||||
$confirm = optional_param('confirm', 0, PARAM_BOOL);
|
||||
$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
|
||||
|
||||
// If we have got here as a confirmed aciton, do it.
|
||||
if ($confirm && isloggedin() && confirm_sesskey()) {
|
||||
require_capability('moodle/site:config', context_system::instance());
|
||||
|
||||
// Valid request. Purge, and redirect the user back to where they came from.
|
||||
purge_all_caches();
|
||||
|
||||
if ($returnurl) {
|
||||
$returnurl = $CFG->wwwroot . $returnurl;
|
||||
} else {
|
||||
$returnurl = new moodle_url('/admin/purgecaches.php');
|
||||
}
|
||||
redirect($returnurl, get_string('purgecachesfinished', 'admin'));
|
||||
}
|
||||
|
||||
// Otherwise, show a button to actually purge the caches.
|
||||
admin_externalpage_setup('purgecaches');
|
||||
|
||||
require_login();
|
||||
require_capability('moodle/site:config', context_system::instance());
|
||||
|
||||
if ($confirm) {
|
||||
require_sesskey();
|
||||
|
||||
// Valid request. Purge, and redisplay the form so it is easy to purge again
|
||||
// in the near future.
|
||||
purge_all_caches();
|
||||
redirect(new moodle_url('/admin/purgecaches.php'), get_string('purgecachesfinished', 'admin'));
|
||||
|
||||
} else {
|
||||
// Show a confirm form.
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading(get_string('purgecaches', 'admin'));
|
||||
|
||||
$url = new moodle_url('/admin/purgecaches.php', array('sesskey'=>sesskey(), 'confirm'=>1));
|
||||
$button = new single_button($url, get_string('purgecaches','admin'), 'post');
|
||||
|
||||
// Cancel button takes them back to the page the were on, if possible,
|
||||
// otherwise to the site home page.
|
||||
$return = new moodle_url('/');
|
||||
if (isset($_SERVER['HTTP_REFERER']) and !empty($_SERVER['HTTP_REFERER'])) {
|
||||
if ($_SERVER['HTTP_REFERER'] !== "$CFG->wwwroot/$CFG->admin/purgecaches.php") {
|
||||
$return = $_SERVER['HTTP_REFERER'];
|
||||
}
|
||||
}
|
||||
|
||||
echo $OUTPUT->confirm(get_string('purgecachesconfirm', 'admin'), $button, $return);
|
||||
echo $OUTPUT->footer();
|
||||
$actionurl = new moodle_url('/admin/purgecaches.php', array('sesskey'=>sesskey(), 'confirm'=>1));
|
||||
if ($returnurl) {
|
||||
$actionurl->param('returnurl', $returnurl);
|
||||
}
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading(get_string('purgecaches', 'admin'));
|
||||
|
||||
echo $OUTPUT->box_start('generalbox', 'notice');
|
||||
echo html_writer::tag('p', get_string('purgecachesconfirm', 'admin'));
|
||||
echo $OUTPUT->single_button($actionurl, get_string('purgecaches', 'admin'), 'post');
|
||||
echo $OUTPUT->box_end();
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
+3
-1
@@ -918,9 +918,11 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
} else {
|
||||
$str = 'otherplugin';
|
||||
}
|
||||
$componenturl = new moodle_url('https://moodle.org/plugins/view.php?plugin='.$component);
|
||||
$componenturl = html_writer::tag('a', $component, array('href' => $componenturl->out()));
|
||||
$requires[] = html_writer::tag('li',
|
||||
get_string($str, 'core_plugin',
|
||||
array('component' => $component, 'version' => $requiredversion)),
|
||||
array('component' => $componenturl, 'version' => $requiredversion)),
|
||||
array('class' => $class));
|
||||
}
|
||||
|
||||
|
||||
@@ -140,8 +140,10 @@ if (($action == 'edit') || ($action == 'new')) {
|
||||
$success = $repositorytype->update_options($settings);
|
||||
} else {
|
||||
$type = new repository_type($plugin, (array)$fromform, $visible);
|
||||
$type->create();
|
||||
$success = true;
|
||||
if (!$repoid = $type->create()) {
|
||||
$success = false;
|
||||
}
|
||||
$data = data_submitted();
|
||||
}
|
||||
if ($success) {
|
||||
@@ -286,9 +288,9 @@ if (($action == 'edit') || ($action == 'new')) {
|
||||
|
||||
// Get list of used plug-ins
|
||||
$instances = repository::get_types();
|
||||
// Array to store plugins being used
|
||||
$alreadyplugins = array();
|
||||
if (!empty($instances)) {
|
||||
// Array to store plugins being used
|
||||
$alreadyplugins = array();
|
||||
$totalinstances = count($instances);
|
||||
$updowncount = 1;
|
||||
foreach ($instances as $i) {
|
||||
|
||||
@@ -32,7 +32,7 @@ $lng = required_param('lng', PARAM_LANG);
|
||||
$currentpage = optional_param('p', 0, PARAM_INT);
|
||||
$translatorsubmitted = optional_param('translatorsubmitted', 0, PARAM_BOOL);
|
||||
|
||||
$PAGE->set_pagelayout('standard');
|
||||
$PAGE->set_pagelayout('report'); // Allows for wide page contents.
|
||||
$PAGE->set_url('/admin/tool/customlang/edit.php', array('lng' => $lng));
|
||||
navigation_node::override_active_url(new moodle_url('/admin/tool/customlang/index.php'));
|
||||
$PAGE->set_title(get_string('pluginname', 'tool_customlang'));
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['install'] = 'Install selected language pack';
|
||||
$string['install'] = 'Install selected language pack(s)';
|
||||
$string['installedlangs'] = 'Installed language packs';
|
||||
$string['langimport'] = 'Language import utility';
|
||||
$string['langimportdisabled'] = 'Language import feature has been disabled. You have to update your language packs manually at the file-system level. Do not forget to purge string caches after you do so.';
|
||||
@@ -37,7 +37,7 @@ $string['missinglangparent'] = 'Missing parent language <em>{$a->parent}</em> of
|
||||
$string['nolangupdateneeded'] = 'All your language packs are up to date, no update is needed';
|
||||
$string['pluginname'] = 'Language packs';
|
||||
$string['purgestringcaches'] = 'Purge string caches';
|
||||
$string['remotelangnotavailable'] = 'Because Moodle can not connect to download.moodle.org, we are unable to do language pack installation automatically. Please download the appropriate zip file(s) from http://download.moodle.org, copy them to your {$a} directory and unzip them manually.';
|
||||
$string['remotelangnotavailable'] = 'Because Moodle cannot connect to download.moodle.org, it is not possible for language packs to be installed automatically. Please download the appropriate ZIP file(s) from <a href="http://download.moodle.org/langpack/">download.moodle.org/langpack</a>, copy them to your {$a} directory and unzip them manually.';
|
||||
$string['uninstall'] = 'Uninstall selected language pack';
|
||||
$string['uninstallconfirm'] = 'You are about to completely uninstall language pack {$a}, are you sure?';
|
||||
$string['updatelangs'] = 'Update all installed language packs';
|
||||
|
||||
@@ -98,12 +98,16 @@ $STD_FIELDS = array('id', 'firstname', 'lastname', 'username', 'email',
|
||||
|
||||
$PRF_FIELDS = array();
|
||||
|
||||
if ($prof_fields = $DB->get_records('user_info_field')) {
|
||||
foreach ($prof_fields as $prof_field) {
|
||||
$PRF_FIELDS[] = 'profile_field_'.$prof_field->shortname;
|
||||
if ($proffields = $DB->get_records('user_info_field')) {
|
||||
foreach ($proffields as $key => $proffield) {
|
||||
$profilefieldname = 'profile_field_'.$proffield->shortname;
|
||||
$PRF_FIELDS[] = $profilefieldname;
|
||||
// Re-index $proffields with key as shortname. This will be
|
||||
// used while checking if profile data is key and needs to be converted (eg. menu profile field)
|
||||
$proffields[$profilefieldname] = $proffield;
|
||||
unset($proffields[$key]);
|
||||
}
|
||||
}
|
||||
unset($prof_fields);
|
||||
|
||||
if (empty($iid)) {
|
||||
$mform1 = new admin_uploaduser_form1();
|
||||
@@ -331,6 +335,16 @@ if ($formdata = $mform2->is_cancelled()) {
|
||||
if (isset($formdata->$field)) {
|
||||
// process templates
|
||||
$user->$field = uu_process_template($formdata->$field, $user);
|
||||
|
||||
// Form contains key and later code expects value.
|
||||
// Convert key to value for required profile fields.
|
||||
require_once($CFG->dirroot.'/user/profile/field/'.$proffields[$field]->datatype.'/field.class.php');
|
||||
$profilefieldclass = 'profile_field_'.$proffields[$field]->datatype;
|
||||
$profilefield = new $profilefieldclass($proffields[$field]->id);
|
||||
if (method_exists($profilefield, 'convert_external_data')) {
|
||||
$user->$field = $profilefield->edit_save_data_preprocess($user->$field, null);
|
||||
}
|
||||
|
||||
$formdefaults[$field] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,8 +321,8 @@ function uu_process_template_callback($username, $firstname, $lastname, $block)
|
||||
* @return array type=>name
|
||||
*/
|
||||
function uu_supported_auths() {
|
||||
// only following plugins are guaranteed to work properly
|
||||
$whitelist = array('manual', 'nologin', 'none', 'email');
|
||||
// Only the following plugins are guaranteed to work properly.
|
||||
$whitelist = array('email', 'ldap', 'manual', 'nologin', 'none');
|
||||
$plugins = get_enabled_auth_plugins();
|
||||
$choices = array();
|
||||
foreach ($plugins as $plugin) {
|
||||
|
||||
@@ -118,7 +118,8 @@ class create_xml_file extends XMLDBAction {
|
||||
function get_plugin_type($dirpath) {
|
||||
global $CFG;
|
||||
$dirpath = $CFG->dirroot.$dirpath;
|
||||
$plugintypes = get_plugin_types();
|
||||
// Reverse order so that we get subplugin matches.
|
||||
$plugintypes = array_reverse(get_plugin_types());
|
||||
foreach ($plugintypes as $plugintype => $pluginbasedir) {
|
||||
if (substr($dirpath, 0, strlen($pluginbasedir)) == $pluginbasedir) {
|
||||
return $plugintype;
|
||||
|
||||
@@ -33,24 +33,38 @@ $string['confirmdeleteindex'] = 'Are you absolutely sure that you want to delete
|
||||
$string['confirmdeletekey'] = 'Are you absolutely sure that you want to delete the key:';
|
||||
$string['confirmdeletetable'] = 'Are you absolutely sure that you want to delete the table:';
|
||||
$string['confirmdeletexmlfile'] = 'Are you absolutely sure that you want to delete the file:';
|
||||
$string['confirmcheckbigints'] = 'This functionality will search for <a href="http://tracker.moodle.org/browse/MDL-11038">potential wrong integer fields</a> in your Moodle server, generating (but not executing!) automatically the needed SQL statements to have all the integers in your DB properly defined.<br /><br />
|
||||
Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).<br /><br />
|
||||
It\'s highly recommended to be running the latest (+ version) available of your Moodle release (1.8, 1.9, 2.x ...) before executing the search of wrong integers.<br /><br />
|
||||
$string['confirmcheckbigints'] = 'This functionality will search for <a href="http://tracker.moodle.org/browse/MDL-11038">potential wrong integer fields</a> in your Moodle server, generating (but not executing!) automatically the needed SQL statements to have all the integers in your DB properly defined.
|
||||
|
||||
Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).
|
||||
|
||||
It\'s highly recommended to be running the latest (+ version) available of your Moodle release before executing the search of wrong integers.
|
||||
|
||||
This functionality doesn\'t perform any action against the DB (just reads from it), so can be safely executed at any moment.';
|
||||
$string['confirmcheckdefaults'] = 'This functionality will search for inconsistent default values in your Moodle server, generating (but not executing!) the needed SQL statements to have all the default values properly defined.<br /><br />
|
||||
Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).<br /><br />
|
||||
It\'s highly recommended to be running the latest (+ version) available of your Moodle release (1.8, 1.9, 2.x ...) before executing the search of inconsistent default values.<br /><br />
|
||||
$string['confirmcheckdefaults'] = 'This functionality will search for inconsistent default values in your Moodle server, generating (but not executing!) the needed SQL statements to have all the default values properly defined.
|
||||
|
||||
Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).
|
||||
|
||||
It\'s highly recommended to be running the latest (+ version) available of your Moodle release before executing the search of inconsistent default values.
|
||||
|
||||
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.)<br /><br />
|
||||
It\'s highly recommended to be running the latest (+ version) available of your Moodle release (1.8, 1.9, 2.x ...) before executing the search of missing indexes.<br /><br />
|
||||
$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.
|
||||
|
||||
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.<br /><br />
|
||||
Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).<br /><br />
|
||||
It\'s highly recommended to be running the latest (+ version) available of your Moodle release (1.8, 1.9, 2.x ...) before executing the search of missing indexes.<br /><br />
|
||||
$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.
|
||||
|
||||
Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).
|
||||
|
||||
It\'s highly recommended to be running the latest (+ version) available of your Moodle release before executing the search of missing indexes.
|
||||
|
||||
This functionality doesn\'t perform any action against the DB (just reads from it), so can be safely executed at any moment.';
|
||||
$string['confirmcheckoraclesemantics'] = 'This functionality will search for <a href="http://tracker.moodle.org/browse/MDL-29322">Oracle varchar2 columns using BYTE semantics</a> in your Moodle server, generating (but not executing!) automatically the needed SQL statements to have all the columns converted to use CHAR semantics instead (better for cross-db compatibility and increased contents max. length).<br /><br />
|
||||
Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).<br /><br />
|
||||
It\'s highly recommended to be running the latest (+ version) available of your Moodle release (2.2, 2.3, 2.x ...) before executing the search of BYTE semantics.<br /><br />
|
||||
$string['confirmcheckoraclesemantics'] = 'This functionality will search for <a href="http://tracker.moodle.org/browse/MDL-29322">Oracle varchar2 columns using BYTE semantics</a> in your Moodle server, generating (but not executing!) automatically the needed SQL statements to have all the columns converted to use CHAR semantics instead (better for cross-db compatibility and increased contents max. length).
|
||||
|
||||
Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).
|
||||
|
||||
It\'s highly recommended to be running the latest (+ version) available of your Moodle release before executing the search of BYTE semantics.
|
||||
|
||||
This functionality doesn\'t perform any action against the DB (just reads from it), so can be safely executed at any moment.';
|
||||
$string['confirmrevertchanges'] = 'Are you absolutely sure that you want to revert changes performed over:';
|
||||
$string['create'] = 'Create';
|
||||
|
||||
@@ -376,7 +376,9 @@
|
||||
echo $OUTPUT->heading('<a href="'.$securewwwroot.'/user/editadvanced.php?id=-1">'.get_string('addnewuser').'</a>');
|
||||
}
|
||||
if (!empty($table)) {
|
||||
echo html_writer::start_tag('div', array('class'=>'no-overflow'));
|
||||
echo html_writer::table($table);
|
||||
echo html_writer::end_tag('div');
|
||||
echo $OUTPUT->paging_bar($usercount, $page, $perpage, $baseurl);
|
||||
if (has_capability('moodle/user:create', $sitecontext)) {
|
||||
echo $OUTPUT->heading('<a href="'.$securewwwroot.'/user/editadvanced.php?id=-1">'.get_string('addnewuser').'</a>');
|
||||
|
||||
@@ -23,6 +23,7 @@ echo $OUTPUT->header();
|
||||
//TODO: add support for large number of users
|
||||
|
||||
if ($confirm and confirm_sesskey()) {
|
||||
$notifications = '';
|
||||
list($in, $params) = $DB->get_in_or_equal($SESSION->bulk_users);
|
||||
$rs = $DB->get_recordset_select('user', "id $in", $params, '', 'id, username, secret, confirmed, auth, firstname, lastname');
|
||||
foreach ($rs as $user) {
|
||||
@@ -32,12 +33,19 @@ if ($confirm and confirm_sesskey()) {
|
||||
$auth = get_auth_plugin($user->auth);
|
||||
$result = $auth->user_confirm($user->username, $user->secret);
|
||||
if ($result != AUTH_CONFIRM_OK && $result != AUTH_CONFIRM_ALREADY) {
|
||||
echo $OUTPUT->notification(get_string('usernotconfirmed', '', fullname($user, true)));
|
||||
$notifications .= $OUTPUT->notification(get_string('usernotconfirmed', '', fullname($user, true)));
|
||||
}
|
||||
}
|
||||
$rs->close();
|
||||
redirect($return, get_string('changessaved'));
|
||||
|
||||
echo $OUTPUT->box_start('generalbox', 'notice');
|
||||
if (!empty($notifications)) {
|
||||
echo $notifications;
|
||||
} else {
|
||||
echo $OUTPUT->notification(get_string('changessaved'), 'notifysuccess');
|
||||
}
|
||||
$continue = new single_button(new moodle_url($return), get_string('continue'), 'post');
|
||||
echo $OUTPUT->render($continue);
|
||||
echo $OUTPUT->box_end();
|
||||
} else {
|
||||
list($in, $params) = $DB->get_in_or_equal($SESSION->bulk_users);
|
||||
$userlist = $DB->get_records_select_menu('user', "id $in", $params, 'fullname', 'id,'.$DB->sql_fullname().' AS fullname');
|
||||
|
||||
@@ -23,20 +23,27 @@ echo $OUTPUT->header();
|
||||
//TODO: add support for large number of users
|
||||
|
||||
if ($confirm and confirm_sesskey()) {
|
||||
|
||||
$notifications = '';
|
||||
list($in, $params) = $DB->get_in_or_equal($SESSION->bulk_users);
|
||||
$rs = $DB->get_recordset_select('user', "id $in", $params);
|
||||
foreach ($rs as $user) {
|
||||
if (!is_siteadmin($user) and $USER->id != $user->id and delete_user($user)) {
|
||||
unset($SESSION->bulk_users[$user->id]);
|
||||
} else {
|
||||
echo $OUTPUT->notification(get_string('deletednot', '', fullname($user, true)));
|
||||
$notifications .= $OUTPUT->notification(get_string('deletednot', '', fullname($user, true)));
|
||||
}
|
||||
}
|
||||
$rs->close();
|
||||
session_gc(); // remove stale sessions
|
||||
redirect($return, get_string('changessaved'));
|
||||
|
||||
echo $OUTPUT->box_start('generalbox', 'notice');
|
||||
if (!empty($notifications)) {
|
||||
echo $notifications;
|
||||
} else {
|
||||
echo $OUTPUT->notification(get_string('changessaved'), 'notifysuccess');
|
||||
}
|
||||
$continue = new single_button(new moodle_url($return), get_string('continue'), 'post');
|
||||
echo $OUTPUT->render($continue);
|
||||
echo $OUTPUT->box_end();
|
||||
} else {
|
||||
list($in, $params) = $DB->get_in_or_equal($SESSION->bulk_users);
|
||||
$userlist = $DB->get_records_select_menu('user', "id $in", $params, 'fullname', 'id,'.$DB->sql_fullname().' AS fullname');
|
||||
|
||||
@@ -1370,7 +1370,7 @@ class CASClient
|
||||
|
||||
/**
|
||||
* This method is used to validate a ST; halt on failure, and sets $validate_url,
|
||||
* $text_reponse and $tree_response on success. These parameters are used later
|
||||
* $text_response and $tree_response on success. These parameters are used later
|
||||
* by CASClient::validatePGT() for CAS proxies.
|
||||
* Used for all CAS 1.0 validations
|
||||
* @param $validate_url the URL of the request to the CAS server.
|
||||
@@ -1500,7 +1500,7 @@ class CASClient
|
||||
|
||||
/**
|
||||
* This method is used to validate a SAML TICKET; halt on failure, and sets $validate_url,
|
||||
* $text_reponse and $tree_response on success. These parameters are used later
|
||||
* $text_response and $tree_response on success. These parameters are used later
|
||||
* by CASClient::validatePGT() for CAS proxies.
|
||||
*
|
||||
* @param $validate_url the URL of the request to the CAS server.
|
||||
@@ -2160,7 +2160,7 @@ class CASClient
|
||||
if ($this->_cas_server_cert != '' && $this->_cas_server_ca_cert != '') {
|
||||
// This branch added by IDMS. Seems phpCAS implementor got a bit confused about the curl options CURLOPT_SSLCERT and CURLOPT_CAINFO
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
curl_setopt($ch, CURLOPT_SSLCERT, $this->_cas_server_cert);
|
||||
curl_setopt($ch, CURLOPT_CAINFO, $this->_cas_server_ca_cert);
|
||||
curl_setopt($ch, CURLOPT_VERBOSE, '1');
|
||||
@@ -2172,7 +2172,7 @@ class CASClient
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
|
||||
curl_setopt($ch, CURLOPT_CAINFO, $this->_cas_server_ca_cert);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
}
|
||||
|
||||
|
||||
+23
-2
@@ -125,7 +125,7 @@ class auth_plugin_cas extends auth_plugin_ldap {
|
||||
// test pgtIou parameter for proxy mode (https connection
|
||||
// in background from CAS server to the php server)
|
||||
if ($authCAS != 'CAS' && !isset($_GET['pgtIou'])) {
|
||||
$PAGE->set_url('/auth/cas/auth.php');
|
||||
$PAGE->set_url('/login/index.php');
|
||||
$PAGE->navbar->add($CASform);
|
||||
$PAGE->set_title("$site->fullname: $CASform");
|
||||
$PAGE->set_heading($site->fullname);
|
||||
@@ -162,7 +162,7 @@ class auth_plugin_cas extends auth_plugin_ldap {
|
||||
*
|
||||
*/
|
||||
function connectCAS() {
|
||||
global $PHPCAS_CLIENT;
|
||||
global $CFG, $PHPCAS_CLIENT;
|
||||
|
||||
if (!is_object($PHPCAS_CLIENT)) {
|
||||
// Make sure phpCAS doesn't try to start a new PHP session when connecting to the CAS server.
|
||||
@@ -173,6 +173,27 @@ class auth_plugin_cas extends auth_plugin_ldap {
|
||||
}
|
||||
}
|
||||
|
||||
// If Moodle is configured to use a proxy, phpCAS needs some curl options set.
|
||||
if (!empty($CFG->proxyhost) && !is_proxybypass($this->config->hostname)) {
|
||||
phpCAS::setExtraCurlOption(CURLOPT_PROXY, $CFG->proxyhost);
|
||||
if (!empty($CFG->proxyport)) {
|
||||
phpCAS::setExtraCurlOption(CURLOPT_PROXYPORT, $CFG->proxyport);
|
||||
}
|
||||
if (!empty($CFG->proxytype)) {
|
||||
// Only set CURLOPT_PROXYTYPE if it's something other than the curl-default http
|
||||
if ($CFG->proxytype == 'SOCKS5') {
|
||||
phpCAS::setExtraCurlOption(CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
|
||||
}
|
||||
}
|
||||
if (!empty($CFG->proxyuser) and !empty($CFG->proxypassword)) {
|
||||
phpCAS::setExtraCurlOption(CURLOPT_PROXYUSERPWD, $CFG->proxyuser.':'.$CFG->proxypassword);
|
||||
if (defined('CURLOPT_PROXYAUTH')) {
|
||||
// any proxy authentication if PHP 5.1
|
||||
phpCAS::setExtraCurlOption(CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_NTLM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($this->config->certificate_check && $this->config->certificate_path){
|
||||
phpCAS::setCasServerCACert($this->config->certificate_path);
|
||||
}else{
|
||||
|
||||
@@ -307,7 +307,7 @@ if (!ldap_paged_results_supported($config->ldap_version)) {
|
||||
<label for="bind_pw"><?php print_string('auth_ldap_bind_pw_key', 'auth_ldap') ?></label>
|
||||
</td>
|
||||
<td>
|
||||
<input name="bind_pw" id="bind_pw" type="password" size="30" value="<?php echo $config->bind_pw?>" />
|
||||
<input name="bind_pw" id="bind_pw" type="password" size="30" value="<?php echo $config->bind_pw?>" autocomplete="off"/>
|
||||
<?php if (isset($err['bind_pw'])) { echo $OUTPUT->error_text($err['bind_pw']); } ?>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@
|
||||
<tr valign="top" class="required">
|
||||
<td align="right"><label for="pass"><?php print_string("auth_dbpass_key", "auth_db") ?></label></td>
|
||||
<td>
|
||||
<input id="pass" name="pass" type="text" size="30" value="<?php echo $config->pass?>" />
|
||||
<input id="pass" name="pass" type="password" size="30" value="<?php p($config->pass)?>" autocomplete="off"/>
|
||||
<?php
|
||||
|
||||
if (isset($err["pass"])) {
|
||||
|
||||
@@ -36,13 +36,13 @@ $string['auth_dbfieldpass'] = 'Name of the field containing passwords';
|
||||
$string['auth_dbfieldpass_key'] = 'Password field';
|
||||
$string['auth_dbfielduser'] = 'Name of the field containing usernames';
|
||||
$string['auth_dbfielduser_key'] = 'Username field';
|
||||
$string['auth_dbhost'] = 'The computer hosting the database server.';
|
||||
$string['auth_dbhost'] = 'The computer hosting the database server. Use a system DSN entry if using ODBC.';
|
||||
$string['auth_dbhost_key'] = 'Host';
|
||||
$string['auth_dbchangepasswordurl_key'] = 'Password-change URL';
|
||||
$string['auth_dbinsertuser'] = 'Inserted user {$a->name} id {$a->id}';
|
||||
$string['auth_dbinsertuserduplicate'] = 'Error inserting user {$a->username} - user with this username was already created through \'{$a->auth}\' plugin.';
|
||||
$string['auth_dbinsertusererror'] = 'Error inserting user {$a}';
|
||||
$string['auth_dbname'] = 'Name of the database itself';
|
||||
$string['auth_dbname'] = 'Name of the database itself. Leave empty if using an ODBC DSN.';
|
||||
$string['auth_dbname_key'] = 'DB name';
|
||||
$string['auth_dbpass'] = 'Password matching the above username';
|
||||
$string['auth_dbpass_key'] = 'Password';
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@
|
||||
<tr valign="top" class="required">
|
||||
<td align="right"><label for="passwd"><?php print_string("auth_fcpasswd_key", "auth_fc") ?>:</label></td>
|
||||
<td>
|
||||
<input name="passwd" id="passwd" type="password" size="30" maxlength="12" value="<?php echo $config->passwd?>" />
|
||||
<input name="passwd" id="passwd" type="password" size="30" maxlength="12" value="<?php echo $config->passwd?>" autocomplete="off"/>
|
||||
<?php if (isset($err["passwd"])) echo $OUTPUT->error_text($err["passwd"]); ?>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -564,6 +564,8 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
if ($user->firstaccess == 0) {
|
||||
$DB->set_field('user', 'firstaccess', time(), array('id'=>$user->id));
|
||||
}
|
||||
$euser = $DB->get_record('user', array('id' => $user->id));
|
||||
events_trigger('user_updated', $euser);
|
||||
return AUTH_CONFIRM_OK;
|
||||
}
|
||||
} else {
|
||||
@@ -736,6 +738,8 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
$updateuser->auth = 'nologin';
|
||||
$DB->update_record('user', $updateuser);
|
||||
echo "\t"; print_string('auth_dbsuspenduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)); echo "\n";
|
||||
$euser = $DB->get_record('user', array('id' => $user->id));
|
||||
events_trigger('user_updated', $euser);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -761,6 +765,8 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
$updateuser->auth = $this->authtype;
|
||||
$DB->update_record('user', $updateuser);
|
||||
echo "\t"; print_string('auth_dbreviveduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)); echo "\n";
|
||||
$euser = $DB->get_record('user', array('id' => $user->id));
|
||||
events_trigger('user_updated', $euser);
|
||||
}
|
||||
} else {
|
||||
print_string('nouserentriestorevive', 'auth_ldap');
|
||||
@@ -874,6 +880,8 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
|
||||
$id = $DB->insert_record('user', $user);
|
||||
echo "\t"; print_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)); echo "\n";
|
||||
$euser = $DB->get_record('user', array('id' => $id));
|
||||
events_trigger('user_created', $euser);
|
||||
if (!empty($this->config->forcechangepassword)) {
|
||||
set_user_preference('auth_forcepasswordchange', 1, $id);
|
||||
}
|
||||
@@ -944,6 +952,10 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($updatekeys)) {
|
||||
$euser = $DB->get_record('user', array('id' => $userid));
|
||||
events_trigger('user_updated', $euser);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ if (!ldap_paged_results_supported($config->ldap_version)) {
|
||||
<label for="bind_pw"><?php print_string('auth_ldap_bind_pw_key', 'auth_ldap') ?></label>
|
||||
</td>
|
||||
<td>
|
||||
<input name="bind_pw" id="bind_pw" type="password" size="30" value="<?php echo $config->bind_pw?>" />
|
||||
<input name="bind_pw" id="bind_pw" type="password" size="30" value="<?php echo $config->bind_pw?>" autocomplete="off"/>
|
||||
<?php if (isset($err['bind_pw'])) { echo $OUTPUT->error_text($err['bind_pw']); } ?>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
This files describes API changes in /auth/* - plugins,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
|
||||
=== 2.4 ===
|
||||
|
||||
required changes in code:
|
||||
|
||||
@@ -335,6 +335,9 @@ class restore_controller extends backup implements loggable {
|
||||
if ($this->status != backup::STATUS_NEED_PRECHECK) {
|
||||
throw new restore_controller_exception('cannot_precheck_wrong_status', $this->status);
|
||||
}
|
||||
// Basic/initial prevention against time/memory limits
|
||||
set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
|
||||
raise_memory_limit(MEMORY_EXTRA);
|
||||
$this->precheck = restore_prechecks_helper::execute_prechecks($this, $droptemptablesafter);
|
||||
if (!array_key_exists('errors', $this->precheck)) { // No errors, can be executed
|
||||
$this->set_status(backup::STATUS_AWAITING);
|
||||
|
||||
@@ -139,7 +139,7 @@ class imscc11_backup_convert extends backup_execution_step {
|
||||
|
||||
require_once($CFG->dirroot . '/backup/cc/cc_includes.php');
|
||||
|
||||
$tempdir = $CFG->dataroot . '/temp/backup/' . uniqid('', true);
|
||||
$tempdir = $CFG->tempdir . '/backup/' . uniqid('', true);
|
||||
|
||||
if (mkdir($tempdir, 0777, true)) {
|
||||
|
||||
|
||||
@@ -1210,12 +1210,16 @@ class moodle1_file_manager implements loggable {
|
||||
*/
|
||||
public function migrate_file($sourcepath, $filepath = '/', $filename = null, $sortorder = 0, $timecreated = null, $timemodified = null) {
|
||||
|
||||
$sourcefullpath = $this->basepath.'/'.$sourcepath;
|
||||
// Normalise Windows paths a bit.
|
||||
$sourcepath = str_replace('\\', '/', $sourcepath);
|
||||
|
||||
if ($sourcefullpath !== clean_param($sourcefullpath, PARAM_PATH)) {
|
||||
throw new moodle1_convert_exception('file_invalid_path', $sourcefullpath);
|
||||
// PARAM_PATH must not be used on full OS path!
|
||||
if ($sourcepath !== clean_param($sourcepath, PARAM_PATH)) {
|
||||
throw new moodle1_convert_exception('file_invalid_path', $sourcepath);
|
||||
}
|
||||
|
||||
$sourcefullpath = $this->basepath.'/'.$sourcepath;
|
||||
|
||||
if (!is_readable($sourcefullpath)) {
|
||||
throw new moodle1_convert_exception('file_not_readable', $sourcefullpath);
|
||||
}
|
||||
|
||||
+29
-14
@@ -113,31 +113,46 @@ if ($backup->get_stage() == backup_ui::STAGE_FINAL) {
|
||||
// Mark the UI finished.
|
||||
$rc->finish_ui();
|
||||
// Execute prechecks
|
||||
$warnings = false;
|
||||
if (!$rc->execute_precheck()) {
|
||||
$precheckresults = $rc->get_precheck_results();
|
||||
if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
|
||||
fulldelete($tempdestination);
|
||||
if (is_array($precheckresults)) {
|
||||
if (!empty($precheckresults['errors'])) { // If errors are found, terminate the import.
|
||||
fulldelete($tempdestination);
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $renderer->precheck_notices($precheckresults);
|
||||
echo $OUTPUT->continue_button(new moodle_url('/course/view.php', array('id'=>$course->id)));
|
||||
echo $OUTPUT->footer();
|
||||
die();
|
||||
echo $OUTPUT->header();
|
||||
echo $renderer->precheck_notices($precheckresults);
|
||||
echo $OUTPUT->continue_button(new moodle_url('/course/view.php', array('id'=>$course->id)));
|
||||
echo $OUTPUT->footer();
|
||||
die();
|
||||
}
|
||||
if (!empty($precheckresults['warnings'])) { // If warnings are found, go ahead but display warnings later.
|
||||
$warnings = $precheckresults['warnings'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($restoretarget == backup::TARGET_CURRENT_DELETING || $restoretarget == backup::TARGET_EXISTING_DELETING) {
|
||||
restore_dbops::delete_course_content($course->id);
|
||||
}
|
||||
// Execute the restore
|
||||
$rc->execute_plan();
|
||||
}
|
||||
if ($restoretarget == backup::TARGET_CURRENT_DELETING || $restoretarget == backup::TARGET_EXISTING_DELETING) {
|
||||
restore_dbops::delete_course_content($course->id);
|
||||
}
|
||||
// Execute the restore.
|
||||
$rc->execute_plan();
|
||||
|
||||
// Delete the temp directory now
|
||||
fulldelete($tempdestination);
|
||||
|
||||
// Display a notification and a continue button
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->notification(get_string('importsuccess', 'backup'),'notifysuccess');
|
||||
if ($warnings) {
|
||||
echo $OUTPUT->box_start();
|
||||
echo $OUTPUT->notification(get_string('warning'), 'notifywarning');
|
||||
echo html_writer::start_tag('ul', array('class'=>'list'));
|
||||
foreach ($warnings as $warning) {
|
||||
echo html_writer::tag('li', $warning);
|
||||
}
|
||||
echo html_writer::end_tag('ul');
|
||||
echo $OUTPUT->box_end();
|
||||
}
|
||||
echo $OUTPUT->notification(get_string('importsuccess', 'backup'), 'notifysuccess');
|
||||
echo $OUTPUT->continue_button(new moodle_url('/course/view.php', array('id'=>$course->id)));
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ class create_and_clean_temp_stuff extends backup_execution_step {
|
||||
backup_helper::check_and_create_backup_dir($this->get_backupid());// Create backup temp dir
|
||||
backup_helper::clear_backup_dir($this->get_backupid()); // Empty temp dir, just in case
|
||||
backup_helper::delete_old_backup_dirs(time() - (4 * 60 * 60)); // Delete > 4 hours temp dirs
|
||||
backup_controller_dbops::drop_backup_ids_temp_table($this->get_backupid()); // Drop ids temp table
|
||||
backup_controller_dbops::create_backup_ids_temp_table($this->get_backupid()); // Create ids temp table
|
||||
}
|
||||
}
|
||||
@@ -1626,7 +1627,20 @@ class backup_zip_contents extends backup_execution_step {
|
||||
$zippacker = get_file_packer('application/zip');
|
||||
|
||||
// Zip files
|
||||
$zippacker->archive_to_pathname($files, $zipfile);
|
||||
$result = $zippacker->archive_to_pathname($files, $zipfile, true, $this);
|
||||
|
||||
// Something went wrong.
|
||||
if ($result === false) {
|
||||
@unlink($zipfile);
|
||||
throw new backup_step_exception('error_zip_packing', '', 'An error was encountered while trying to generate backup zip');
|
||||
}
|
||||
// Read to make sure it is a valid backup. Refer MDL-37877 . Delete it, if found not to be valid.
|
||||
try {
|
||||
backup_general_helper::get_backup_information_from_mbz($zipfile);
|
||||
} catch (backup_helper_exception $e) {
|
||||
@unlink($zipfile);
|
||||
throw new backup_step_exception('error_zip_packing', '', $e->debuginfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ global $LINKS_ENCODERS_CACHE;
|
||||
$LINKS_ENCODERS_CACHE = array();
|
||||
|
||||
/**
|
||||
* Class implementing the @xml_contenttrasnformed logic to be applied in moodle2 backups
|
||||
* Class implementing the @xml_contenttransformed logic to be applied in moodle2 backups
|
||||
*
|
||||
* TODO: Finish phpdocs
|
||||
*/
|
||||
@@ -61,13 +61,13 @@ class backup_xml_transformer extends xml_contenttransformer {
|
||||
|
||||
// Array or object, debug and try our best recursively, shouldn't happen but...
|
||||
if (is_array($content)) {
|
||||
debugging('Backup XML transformer should process arrays but plain content always', DEBUG_DEVELOPER);
|
||||
debugging('Backup XML transformer should not process arrays but plain content only', DEBUG_DEVELOPER);
|
||||
foreach($content as $key => $plaincontent) {
|
||||
$content[$key] = $this->process($plaincontent);
|
||||
}
|
||||
return $content;
|
||||
} else if (is_object($content)) {
|
||||
debugging('Backup XML transformer should not process objects but plain content always', DEBUG_DEVELOPER);
|
||||
debugging('Backup XML transformer should not process objects but plain content only', DEBUG_DEVELOPER);
|
||||
foreach((array)$content as $key => $plaincontent) {
|
||||
$content[$key] = $this->process($plaincontent);
|
||||
}
|
||||
|
||||
@@ -73,8 +73,20 @@ class restore_course_task extends restore_task {
|
||||
|
||||
$this->add_step(new restore_course_legacy_files_step('legacy_files'));
|
||||
|
||||
// Restore course enrolments (plugins and membership). Conditionally prevented for any IMPORT/HUB operation
|
||||
if ($this->plan->get_mode() != backup::MODE_IMPORT && $this->plan->get_mode() != backup::MODE_HUB) {
|
||||
// Deal with enrolment methods and user enrolments.
|
||||
if ($this->plan->get_mode() == backup::MODE_IMPORT) {
|
||||
// No need to do anything with enrolments.
|
||||
|
||||
} else if (!$this->get_setting_value('users') or $this->plan->get_mode() == backup::MODE_HUB) {
|
||||
if ($this->get_target() == backup::TARGET_CURRENT_ADDING or $this->get_target() == backup::TARGET_EXISTING_ADDING) {
|
||||
// Keep current enrolments unchanged.
|
||||
} else {
|
||||
// If no instances yet add default enrol methods the same way as when creating new course in UI.
|
||||
$this->add_step(new restore_default_enrolments_step('default_enrolments'));
|
||||
}
|
||||
|
||||
} else {
|
||||
// Restore course enrolment data.
|
||||
$this->add_step(new restore_enrolments_structure_step('course_enrolments', 'enrolments.xml'));
|
||||
}
|
||||
|
||||
|
||||
@@ -143,6 +143,9 @@ class restore_final_task extends restore_task {
|
||||
$rules[] = new restore_log_rule('course', 'report stats', 'report/stats/index.php?id={course}', '{course}');
|
||||
$rules[] = new restore_log_rule('course', 'view section', 'view.php?id={course}§ionid={course_section}', '{course_section}');
|
||||
|
||||
// module 'grade' rules
|
||||
$rules[] = new restore_log_rule('grade', 'update', 'report/grader/index.php?id={course}', null);
|
||||
|
||||
// module 'user' rules
|
||||
$rules[] = new restore_log_rule('user', 'view', 'view.php?id={user}&course={course}', '{user}');
|
||||
$rules[] = new restore_log_rule('user', 'change password', 'view.php?id={user}&course={course}', '{user}');
|
||||
|
||||
@@ -35,6 +35,18 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*/
|
||||
abstract class restore_qtype_plugin extends restore_plugin {
|
||||
|
||||
/*
|
||||
* A simple answer to id cache for a single questions answers.
|
||||
* @var array
|
||||
*/
|
||||
private $questionanswercache = array();
|
||||
|
||||
/*
|
||||
* The id of the current question in the questionanswercache.
|
||||
* @var int
|
||||
*/
|
||||
private $questionanswercacheid = null;
|
||||
|
||||
/**
|
||||
* Add to $paths the restore_path_elements needed
|
||||
* to handle question_answers for a given question
|
||||
@@ -147,38 +159,32 @@ abstract class restore_qtype_plugin extends restore_plugin {
|
||||
|
||||
// The question existed, we need to map the existing question_answers
|
||||
} else {
|
||||
// Look in question_answers by answertext matching
|
||||
$sql = 'SELECT id
|
||||
FROM {question_answers}
|
||||
WHERE question = ?
|
||||
AND ' . $DB->sql_compare_text('answer', 255) . ' = ' . $DB->sql_compare_text('?', 255);
|
||||
$params = array($newquestionid, $data->answertext);
|
||||
$newitemid = $DB->get_field_sql($sql, $params);
|
||||
|
||||
// Not able to find the answer, let's try cleaning the answertext
|
||||
// of all the question answers in DB as slower fallback. MDL-30018.
|
||||
if (!$newitemid) {
|
||||
// Have we cached the current question?
|
||||
if ($this->questionanswercacheid !== $newquestionid) {
|
||||
// The question changed, purge and start again!
|
||||
$this->questionanswercache = array();
|
||||
$params = array('question' => $newquestionid);
|
||||
$answers = $DB->get_records('question_answers', $params, '', 'id, answer');
|
||||
$this->questionanswercacheid = $newquestionid;
|
||||
// Cache all cleaned answers for a simple text match.
|
||||
foreach ($answers as $answer) {
|
||||
// Clean in the same way than {@link xml_writer::xml_safe_utf8()}.
|
||||
// MDL-30018: Clean in the same way as {@link xml_writer::xml_safe_utf8()}.
|
||||
$clean = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is','', $answer->answer); // Clean CTRL chars.
|
||||
$clean = preg_replace("/\r\n|\r/", "\n", $clean); // Normalize line ending.
|
||||
if ($clean === $data->answertext) {
|
||||
$newitemid = $data->id;
|
||||
}
|
||||
$this->questionanswercache[$clean] = $answer->id;
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't found the newitemid, something has gone really wrong, question in DB
|
||||
// is missing answers, exception
|
||||
if (!$newitemid) {
|
||||
if (!isset($this->questionanswercache[$data->answertext])) {
|
||||
// If we haven't found the matching answer, something has gone really wrong, the question in the DB
|
||||
// is missing answers, throw an exception.
|
||||
$info = new stdClass();
|
||||
$info->filequestionid = $oldquestionid;
|
||||
$info->dbquestionid = $newquestionid;
|
||||
$info->answer = $data->answertext;
|
||||
throw new restore_step_exception('error_question_answers_missing_in_db', $info);
|
||||
}
|
||||
$newitemid = $this->questionanswercache[$data->answertext];
|
||||
}
|
||||
// Create mapping (we'll use this intensively when restoring question_states. And also answerfeedback files)
|
||||
$this->set_mapping('question_answer', $oldid, $newitemid);
|
||||
|
||||
@@ -115,6 +115,7 @@ class restore_root_task extends restore_task {
|
||||
$rootenrolmanual = new restore_users_setting('enrol_migratetomanual', base_setting::IS_BOOLEAN, false);
|
||||
$rootenrolmanual->set_ui(new backup_setting_ui_checkbox($rootenrolmanual, get_string('rootenrolmanual', 'backup')));
|
||||
$rootenrolmanual->get_ui()->set_changeable(enrol_is_enabled('manual'));
|
||||
$rootenrolmanual->get_ui()->set_changeable($changeable);
|
||||
$this->add_setting($rootenrolmanual);
|
||||
$users->add_dependency($rootenrolmanual);
|
||||
|
||||
|
||||
@@ -161,6 +161,9 @@ class restore_section_task extends restore_task {
|
||||
$section_included = new restore_section_included_setting($settingname, base_setting::IS_BOOLEAN, true);
|
||||
if (is_number($this->info->title)) {
|
||||
$label = get_string('includesection', 'backup', $this->info->title);
|
||||
} elseif (empty($this->info->title)) { // Don't throw error if title is empty, gracefully continue restore.
|
||||
$this->log('Section title missing in backup for section id '.$this->info->sectionid, backup::LOG_WARNING, $this->name);
|
||||
$label = get_string('unnamedsection', 'backup');
|
||||
} else {
|
||||
$label = $this->info->title;
|
||||
}
|
||||
|
||||
@@ -1616,6 +1616,29 @@ class restore_ras_and_caps_structure_step extends restore_structure_step {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If no instances yet add default enrol methods the same way as when creating new course in UI.
|
||||
*/
|
||||
class restore_default_enrolments_step extends restore_execution_step {
|
||||
public function define_execution() {
|
||||
global $DB;
|
||||
|
||||
$course = $DB->get_record('course', array('id'=>$this->get_courseid()), '*', MUST_EXIST);
|
||||
|
||||
if ($DB->record_exists('enrol', array('courseid'=>$this->get_courseid(), 'enrol'=>'manual'))) {
|
||||
// Something already added instances, do not add default instances.
|
||||
$plugins = enrol_get_plugins(true);
|
||||
foreach ($plugins as $plugin) {
|
||||
$plugin->restore_sync_course($course);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Looks like a newly created course.
|
||||
enrol_course_updated(true, $course, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This structure steps restores the enrol plugins and their underlying
|
||||
* enrolments, performing all the mappings and/or movements required
|
||||
|
||||
@@ -164,8 +164,10 @@ abstract class backup_controller_dbops extends backup_dbops {
|
||||
$dbman = $DB->get_manager(); // We are going to use database_manager services
|
||||
|
||||
$targettablename = 'backup_ids_temp';
|
||||
$table = new xmldb_table($targettablename);
|
||||
$dbman->drop_table($table); // And drop it
|
||||
if ($dbman->table_exists($targettablename)) {
|
||||
$table = new xmldb_table($targettablename);
|
||||
$dbman->drop_table($table); // And drop it
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -614,13 +614,20 @@ abstract class restore_dbops {
|
||||
} else {
|
||||
self::set_backup_ids_record($restoreid, 'question_category', $category->id, $matchcat->id, $targetcontext->id);
|
||||
$questions = self::restore_get_questions($restoreid, $category->id);
|
||||
|
||||
// Collect all the questions for this category into memory so we only talk to the DB once.
|
||||
$questioncache = $DB->get_records_sql_menu("SELECT ".$DB->sql_concat('stamp', "' '", 'version').", id
|
||||
FROM {question}
|
||||
WHERE category = ?", array($matchcat->id));
|
||||
|
||||
foreach ($questions as $question) {
|
||||
$matchq = $DB->get_record('question', array(
|
||||
'category' => $matchcat->id,
|
||||
'stamp' => $question->stamp,
|
||||
'version' => $question->version));
|
||||
if (isset($questioncache[$question->stamp." ".$question->version])) {
|
||||
$matchqid = $questioncache[$question->stamp." ".$question->version];
|
||||
} else {
|
||||
$matchqid = false;
|
||||
}
|
||||
// 5a) No match, check if user can add q
|
||||
if (!$matchq) {
|
||||
if (!$matchqid) {
|
||||
// 6a) User can, mark the q to be created
|
||||
if ($canadd) {
|
||||
// Nothing to mark, newitemid means create
|
||||
@@ -645,7 +652,7 @@ abstract class restore_dbops {
|
||||
|
||||
// 5b) Match, mark q to be mapped
|
||||
} else {
|
||||
self::set_backup_ids_record($restoreid, 'question', $question->id, $matchq->id);
|
||||
self::set_backup_ids_record($restoreid, 'question', $question->id, $matchqid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -901,6 +908,15 @@ abstract class restore_dbops {
|
||||
}
|
||||
|
||||
if (empty($file->repositoryid)) {
|
||||
// If contenthash is empty then gracefully skip adding file.
|
||||
if (empty($file->contenthash)) {
|
||||
$result = new stdClass();
|
||||
$result->code = 'file_missing_in_backup';
|
||||
$result->message = sprintf('missing file (%s) contenthash in backup for component %s', $file->filename, $component);
|
||||
$result->level = backup::LOG_WARNING;
|
||||
$results[] = $result;
|
||||
continue;
|
||||
}
|
||||
// this is a regular file, it must be present in the backup pool
|
||||
$backuppath = $basepath . backup_file_manager::get_backup_content_file_location($file->contenthash);
|
||||
|
||||
|
||||
-95
@@ -26,101 +26,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
// Include all the needed stuff
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
||||
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
|
||||
|
||||
/**
|
||||
* Restore dbops tests (all).
|
||||
*/
|
||||
class restore_dbops_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Verify the xxx_ids_cached (in-memory backup_ids cache) stuff works as expected.
|
||||
*
|
||||
* Note that those private implementations are tested here by using the public
|
||||
* backup_ids API and later performing low-level tests.
|
||||
*/
|
||||
public function test_backup_ids_cached() {
|
||||
global $DB;
|
||||
$dbman = $DB->get_manager(); // We are going to use database_manager services.
|
||||
|
||||
$this->resetAfterTest(true); // Playing with temp tables, better reset once finished.
|
||||
|
||||
// Some variables and objects for testing.
|
||||
$restoreid = 'testrestoreid';
|
||||
|
||||
$mapping = new stdClass();
|
||||
$mapping->itemname = 'user';
|
||||
$mapping->itemid = 1;
|
||||
$mapping->newitemid = 2;
|
||||
$mapping->parentitemid = 3;
|
||||
$mapping->info = 'info';
|
||||
|
||||
// Create the backup_ids temp tables used by restore.
|
||||
restore_controller_dbops::create_restore_temp_tables($restoreid);
|
||||
|
||||
// Send one mapping using the public api with defaults.
|
||||
restore_dbops::set_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
|
||||
// Get that mapping and verify everything is returned as expected.
|
||||
$result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
|
||||
$this->assertSame($mapping->itemname, $result->itemname);
|
||||
$this->assertSame($mapping->itemid, $result->itemid);
|
||||
$this->assertSame(0, $result->newitemid);
|
||||
$this->assertSame(null, $result->parentitemid);
|
||||
$this->assertSame(null, $result->info);
|
||||
|
||||
// Drop the backup_xxx_temp temptables manually, so memory cache won't be invalidated.
|
||||
$dbman->drop_table(new xmldb_table('backup_ids_temp'));
|
||||
$dbman->drop_table(new xmldb_table('backup_files_temp'));
|
||||
|
||||
// Verify the mapping continues returning the same info,
|
||||
// now from cache (the table does not exist).
|
||||
$result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
|
||||
$this->assertSame($mapping->itemname, $result->itemname);
|
||||
$this->assertSame($mapping->itemid, $result->itemid);
|
||||
$this->assertSame(0, $result->newitemid);
|
||||
$this->assertSame(null, $result->parentitemid);
|
||||
$this->assertSame(null, $result->info);
|
||||
|
||||
// Recreate the temp table, just to drop it using the restore API in
|
||||
// order to check that, then, the cache becomes invalid for the same request.
|
||||
restore_controller_dbops::create_restore_temp_tables($restoreid);
|
||||
restore_controller_dbops::drop_restore_temp_tables($restoreid);
|
||||
|
||||
// No cached info anymore, so the mapping request will arrive to
|
||||
// DB leading to error (temp table does not exist).
|
||||
try {
|
||||
$result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
|
||||
$this->fail('Expecting an exception, none occurred');
|
||||
} catch (Exception $e) {
|
||||
$this->assertTrue($e instanceof dml_exception);
|
||||
$this->assertSame('Table "backup_ids_temp" does not exist', $e->getMessage());
|
||||
}
|
||||
|
||||
// Create the backup_ids temp tables once more.
|
||||
restore_controller_dbops::create_restore_temp_tables($restoreid);
|
||||
|
||||
// Send one mapping using the public api with complete values.
|
||||
restore_dbops::set_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid,
|
||||
$mapping->newitemid, $mapping->parentitemid, $mapping->info);
|
||||
// Get that mapping and verify everything is returned as expected.
|
||||
$result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
|
||||
$this->assertSame($mapping->itemname, $result->itemname);
|
||||
$this->assertSame($mapping->itemid, $result->itemid);
|
||||
$this->assertSame($mapping->newitemid, $result->newitemid);
|
||||
$this->assertSame($mapping->parentitemid, $result->parentitemid);
|
||||
$this->assertSame($mapping->info, $result->info);
|
||||
|
||||
// Finally, drop the temp tables properly and get the DB error again (memory caches empty).
|
||||
restore_controller_dbops::drop_restore_temp_tables($restoreid);
|
||||
try {
|
||||
$result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
|
||||
$this->fail('Expecting an exception, none occurred');
|
||||
} catch (Exception $e) {
|
||||
$this->assertTrue($e instanceof dml_exception);
|
||||
$this->assertSame('Table "backup_ids_temp" does not exist', $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Backup dbops tests (all).
|
||||
@@ -0,0 +1,122 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* @package core_backup
|
||||
* @category phpunit
|
||||
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
// Include all the needed stuff
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
|
||||
|
||||
/**
|
||||
* Restore dbops tests (all).
|
||||
*/
|
||||
class restore_dbops_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Verify the xxx_ids_cached (in-memory backup_ids cache) stuff works as expected.
|
||||
*
|
||||
* Note that those private implementations are tested here by using the public
|
||||
* backup_ids API and later performing low-level tests.
|
||||
*/
|
||||
public function test_backup_ids_cached() {
|
||||
global $DB;
|
||||
$dbman = $DB->get_manager(); // We are going to use database_manager services.
|
||||
|
||||
$this->resetAfterTest(true); // Playing with temp tables, better reset once finished.
|
||||
|
||||
// Some variables and objects for testing.
|
||||
$restoreid = 'testrestoreid';
|
||||
|
||||
$mapping = new stdClass();
|
||||
$mapping->itemname = 'user';
|
||||
$mapping->itemid = 1;
|
||||
$mapping->newitemid = 2;
|
||||
$mapping->parentitemid = 3;
|
||||
$mapping->info = 'info';
|
||||
|
||||
// Create the backup_ids temp tables used by restore.
|
||||
restore_controller_dbops::create_restore_temp_tables($restoreid);
|
||||
|
||||
// Send one mapping using the public api with defaults.
|
||||
restore_dbops::set_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
|
||||
// Get that mapping and verify everything is returned as expected.
|
||||
$result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
|
||||
$this->assertSame($mapping->itemname, $result->itemname);
|
||||
$this->assertSame($mapping->itemid, $result->itemid);
|
||||
$this->assertSame(0, $result->newitemid);
|
||||
$this->assertSame(null, $result->parentitemid);
|
||||
$this->assertSame(null, $result->info);
|
||||
|
||||
// Drop the backup_xxx_temp temptables manually, so memory cache won't be invalidated.
|
||||
$dbman->drop_table(new xmldb_table('backup_ids_temp'));
|
||||
$dbman->drop_table(new xmldb_table('backup_files_temp'));
|
||||
|
||||
// Verify the mapping continues returning the same info,
|
||||
// now from cache (the table does not exist).
|
||||
$result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
|
||||
$this->assertSame($mapping->itemname, $result->itemname);
|
||||
$this->assertSame($mapping->itemid, $result->itemid);
|
||||
$this->assertSame(0, $result->newitemid);
|
||||
$this->assertSame(null, $result->parentitemid);
|
||||
$this->assertSame(null, $result->info);
|
||||
|
||||
// Recreate the temp table, just to drop it using the restore API in
|
||||
// order to check that, then, the cache becomes invalid for the same request.
|
||||
restore_controller_dbops::create_restore_temp_tables($restoreid);
|
||||
restore_controller_dbops::drop_restore_temp_tables($restoreid);
|
||||
|
||||
// No cached info anymore, so the mapping request will arrive to
|
||||
// DB leading to error (temp table does not exist).
|
||||
try {
|
||||
$result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
|
||||
$this->fail('Expecting an exception, none occurred');
|
||||
} catch (Exception $e) {
|
||||
$this->assertTrue($e instanceof dml_exception);
|
||||
$this->assertSame('Table "backup_ids_temp" does not exist', $e->getMessage());
|
||||
}
|
||||
|
||||
// Create the backup_ids temp tables once more.
|
||||
restore_controller_dbops::create_restore_temp_tables($restoreid);
|
||||
|
||||
// Send one mapping using the public api with complete values.
|
||||
restore_dbops::set_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid,
|
||||
$mapping->newitemid, $mapping->parentitemid, $mapping->info);
|
||||
// Get that mapping and verify everything is returned as expected.
|
||||
$result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
|
||||
$this->assertSame($mapping->itemname, $result->itemname);
|
||||
$this->assertSame($mapping->itemid, $result->itemid);
|
||||
$this->assertSame($mapping->newitemid, $result->newitemid);
|
||||
$this->assertSame($mapping->parentitemid, $result->parentitemid);
|
||||
$this->assertSame($mapping->info, $result->info);
|
||||
|
||||
// Finally, drop the temp tables properly and get the DB error again (memory caches empty).
|
||||
restore_controller_dbops::drop_restore_temp_tables($restoreid);
|
||||
try {
|
||||
$result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
|
||||
$this->fail('Expecting an exception, none occurred');
|
||||
} catch (Exception $e) {
|
||||
$this->assertTrue($e instanceof dml_exception);
|
||||
$this->assertSame('Table "backup_ids_temp" does not exist', $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,11 +32,11 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*/
|
||||
abstract class backup_cron_automated_helper {
|
||||
|
||||
/** automated backups are active and ready to run */
|
||||
/** Automated backups are active and ready to run */
|
||||
const STATE_OK = 0;
|
||||
/** automated backups are disabled and will not be run */
|
||||
/** Automated backups are disabled and will not be run */
|
||||
const STATE_DISABLED = 1;
|
||||
/** automated backups are all ready running! */
|
||||
/** Automated backups are all ready running! */
|
||||
const STATE_RUNNING = 2;
|
||||
|
||||
/** Course automated backup completed successfully */
|
||||
@@ -49,6 +49,8 @@ abstract class backup_cron_automated_helper {
|
||||
const BACKUP_STATUS_SKIPPED = 3;
|
||||
/** Course automated backup had warnings */
|
||||
const BACKUP_STATUS_WARNING = 4;
|
||||
/** Course automated backup has yet to be run */
|
||||
const BACKUP_STATUS_NOTYETRUN = 5;
|
||||
|
||||
/** Run if required by the schedule set in config. Default. **/
|
||||
const RUN_ON_SCHEDULE = 0;
|
||||
@@ -119,12 +121,13 @@ abstract class backup_cron_automated_helper {
|
||||
|
||||
$rs = $DB->get_recordset('course');
|
||||
foreach ($rs as $course) {
|
||||
$backupcourse = $DB->get_record('backup_courses', array('courseid'=>$course->id));
|
||||
$backupcourse = $DB->get_record('backup_courses', array('courseid' => $course->id));
|
||||
if (!$backupcourse) {
|
||||
$backupcourse = new stdClass;
|
||||
$backupcourse->courseid = $course->id;
|
||||
$DB->insert_record('backup_courses',$backupcourse);
|
||||
$backupcourse = $DB->get_record('backup_courses', array('courseid'=>$course->id));
|
||||
$backupcourse->laststatus = self::BACKUP_STATUS_NOTYETRUN;
|
||||
$DB->insert_record('backup_courses', $backupcourse);
|
||||
$backupcourse = $DB->get_record('backup_courses', array('courseid' => $course->id));
|
||||
}
|
||||
|
||||
// The last backup is considered as successful when OK or SKIPPED.
|
||||
@@ -132,19 +135,23 @@ abstract class backup_cron_automated_helper {
|
||||
$backupcourse->laststatus == self::BACKUP_STATUS_OK) && (
|
||||
$backupcourse->laststarttime > 0 && $backupcourse->lastendtime > 0);
|
||||
|
||||
// Skip courses that do not yet need backup.
|
||||
$skipped = !(($backupcourse->nextstarttime > 0 && $backupcourse->nextstarttime < $now) || $rundirective == self::RUN_IMMEDIATELY);
|
||||
$skippedmessage = 'Does not require backup';
|
||||
// Assume that we are not skipping anything.
|
||||
$skipped = false;
|
||||
$skippedmessage = '';
|
||||
|
||||
// Check if we are going to be running the backup now.
|
||||
$shouldrunnow = (($backupcourse->nextstarttime > 0 && $backupcourse->nextstarttime < $now)
|
||||
|| $rundirective == self::RUN_IMMEDIATELY);
|
||||
|
||||
// If config backup_auto_skip_hidden is set to true, skip courses that are not visible.
|
||||
if (!$skipped && $config->backup_auto_skip_hidden) {
|
||||
if ($shouldrunnow && $config->backup_auto_skip_hidden) {
|
||||
$skipped = ($config->backup_auto_skip_hidden && !$course->visible);
|
||||
$skippedmessage = 'Not visible';
|
||||
}
|
||||
|
||||
// If config backup_auto_skip_modif_days is set to true, skip courses
|
||||
// that have not been modified since the number of days defined.
|
||||
if (!$skipped && $lastbackupwassuccessful && $config->backup_auto_skip_modif_days) {
|
||||
if ($shouldrunnow && !$skipped && $lastbackupwassuccessful && $config->backup_auto_skip_modif_days) {
|
||||
$sqlwhere = "course=:courseid AND time>:time AND ".$DB->sql_like('action', ':action', false, true, true);
|
||||
$timenotmodifsincedays = $now - ($config->backup_auto_skip_modif_days * DAYSECS);
|
||||
// Check log if there were any modifications to the course content.
|
||||
@@ -159,8 +166,9 @@ abstract class backup_cron_automated_helper {
|
||||
|
||||
// If config backup_auto_skip_modif_prev is set to true, skip courses
|
||||
// that have not been modified since previous backup.
|
||||
if (!$skipped && $lastbackupwassuccessful && $config->backup_auto_skip_modif_prev) {
|
||||
if ($shouldrunnow && !$skipped && $lastbackupwassuccessful && $config->backup_auto_skip_modif_prev) {
|
||||
// Check log if there were any modifications to the course content.
|
||||
$sqlwhere = "course=:courseid AND time>:time AND ".$DB->sql_like('action', ':action', false, true, true);
|
||||
$params = array('courseid' => $course->id,
|
||||
'time' => $backupcourse->laststarttime,
|
||||
'action' => '%view%');
|
||||
@@ -170,16 +178,17 @@ abstract class backup_cron_automated_helper {
|
||||
$skippedmessage = 'Not modified since previous backup';
|
||||
}
|
||||
|
||||
// Skip courses not needed for backup.
|
||||
if ($skipped) {
|
||||
// Output the next execution time when it has been updated.
|
||||
if ($backupcourse->nextstarttime != $nextstarttime) {
|
||||
mtrace('Backup of \'' . $course->fullname . '\' is scheduled on ' . $showtime);
|
||||
}
|
||||
// Check if the course is not scheduled to run right now.
|
||||
if (!$shouldrunnow) {
|
||||
$backupcourse->nextstarttime = $nextstarttime;
|
||||
$DB->update_record('backup_courses', $backupcourse);
|
||||
mtrace('Skipping ' . $course->fullname . ' (Not scheduled for backup until ' . $showtime . ')');
|
||||
} else if ($skipped) { // Must have been skipped for a reason.
|
||||
$backupcourse->laststatus = self::BACKUP_STATUS_SKIPPED;
|
||||
$backupcourse->nextstarttime = $nextstarttime;
|
||||
$DB->update_record('backup_courses', $backupcourse);
|
||||
mtrace('Skipping '.$course->fullname.' ('.$skippedmessage.')');
|
||||
mtrace('Skipping ' . $course->fullname . ' (' . $skippedmessage . ')');
|
||||
mtrace('Backup of \'' . $course->fullname . '\' is scheduled on ' . $showtime);
|
||||
} else {
|
||||
// Backup every non-skipped courses.
|
||||
mtrace('Backing up '.$course->fullname.'...');
|
||||
@@ -223,16 +232,17 @@ abstract class backup_cron_automated_helper {
|
||||
$count = backup_cron_automated_helper::get_backup_status_array();
|
||||
$haserrors = ($count[self::BACKUP_STATUS_ERROR] != 0 || $count[self::BACKUP_STATUS_UNFINISHED] != 0);
|
||||
|
||||
//Build the message text
|
||||
//Summary
|
||||
$message .= get_string('summary')."\n";
|
||||
// Build the message text.
|
||||
// Summary.
|
||||
$message .= get_string('summary') . "\n";
|
||||
$message .= "==================================================\n";
|
||||
$message .= " ".get_string('courses').": ".array_sum($count)."\n";
|
||||
$message .= " ".get_string('ok').": ".$count[self::BACKUP_STATUS_OK]."\n";
|
||||
$message .= " ".get_string('skipped').": ".$count[self::BACKUP_STATUS_SKIPPED]."\n";
|
||||
$message .= " ".get_string('error').": ".$count[self::BACKUP_STATUS_ERROR]."\n";
|
||||
$message .= " ".get_string('unfinished').": ".$count[self::BACKUP_STATUS_UNFINISHED]."\n";
|
||||
$message .= " ".get_string('warning').": ".$count[self::BACKUP_STATUS_WARNING]."\n\n";
|
||||
$message .= ' ' . get_string('courses') . '; ' . array_sum($count) . "\n";
|
||||
$message .= ' ' . get_string('ok') . '; ' . $count[self::BACKUP_STATUS_OK] . "\n";
|
||||
$message .= ' ' . get_string('skipped') . '; ' . $count[self::BACKUP_STATUS_SKIPPED] . "\n";
|
||||
$message .= ' ' . get_string('error') . '; ' . $count[self::BACKUP_STATUS_ERROR] . "\n";
|
||||
$message .= ' ' . get_string('unfinished') . '; ' . $count[self::BACKUP_STATUS_UNFINISHED] . "\n";
|
||||
$message .= ' ' . get_string('warning') . '; ' . $count[self::BACKUP_STATUS_WARNING] . "\n";
|
||||
$message .= ' ' . get_string('backupnotyetrun') . '; ' . $count[self::BACKUP_STATUS_NOTYETRUN]."\n\n";
|
||||
|
||||
//Reference
|
||||
if ($haserrors) {
|
||||
@@ -295,7 +305,8 @@ abstract class backup_cron_automated_helper {
|
||||
self::BACKUP_STATUS_OK => 0,
|
||||
self::BACKUP_STATUS_UNFINISHED => 0,
|
||||
self::BACKUP_STATUS_SKIPPED => 0,
|
||||
self::BACKUP_STATUS_WARNING => 0
|
||||
self::BACKUP_STATUS_WARNING => 0,
|
||||
self::BACKUP_STATUS_NOTYETRUN => 0
|
||||
);
|
||||
|
||||
$statuses = $DB->get_records_sql('SELECT DISTINCT bc.laststatus, COUNT(bc.courseid) AS statuscount FROM {backup_courses} bc GROUP BY bc.laststatus');
|
||||
@@ -371,7 +382,11 @@ abstract class backup_cron_automated_helper {
|
||||
|
||||
$outcome = self::BACKUP_STATUS_OK;
|
||||
$config = get_config('backup');
|
||||
$bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_AUTOMATED, $userid);
|
||||
$dir = $config->backup_auto_destination;
|
||||
$storage = (int)$config->backup_auto_storage;
|
||||
|
||||
$bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO,
|
||||
backup::MODE_AUTOMATED, $userid);
|
||||
|
||||
try {
|
||||
|
||||
@@ -392,27 +407,28 @@ abstract class backup_cron_automated_helper {
|
||||
}
|
||||
}
|
||||
|
||||
// Set the default filename
|
||||
// Set the default filename.
|
||||
$format = $bc->get_format();
|
||||
$type = $bc->get_type();
|
||||
$id = $bc->get_id();
|
||||
$users = $bc->get_plan()->get_setting('users')->get_value();
|
||||
$anonymised = $bc->get_plan()->get_setting('anonymize')->get_value();
|
||||
$bc->get_plan()->get_setting('filename')->set_value(backup_plan_dbops::get_default_backup_filename($format, $type, $id, $users, $anonymised));
|
||||
$bc->get_plan()->get_setting('filename')->set_value(backup_plan_dbops::get_default_backup_filename($format, $type,
|
||||
$id, $users, $anonymised));
|
||||
|
||||
$bc->set_status(backup::STATUS_AWAITING);
|
||||
|
||||
$bc->execute_plan();
|
||||
$results = $bc->get_results();
|
||||
$outcome = self::outcome_from_results($results);
|
||||
$file = $results['backup_destination']; // may be empty if file already moved to target location
|
||||
$dir = $config->backup_auto_destination;
|
||||
$storage = (int)$config->backup_auto_storage;
|
||||
$file = $results['backup_destination']; // May be empty if file already moved to target location.
|
||||
if (!file_exists($dir) || !is_dir($dir) || !is_writable($dir)) {
|
||||
$dir = null;
|
||||
}
|
||||
if ($file && !empty($dir) && $storage !== 0) {
|
||||
$filename = backup_plan_dbops::get_default_backup_filename($format, $type, $course->id, $users, $anonymised, !$config->backup_shortname);
|
||||
// Copy file only if there was no error.
|
||||
if ($file && !empty($dir) && $storage !== 0 && $outcome != self::BACKUP_STATUS_ERROR) {
|
||||
$filename = backup_plan_dbops::get_default_backup_filename($format, $type, $course->id, $users, $anonymised,
|
||||
!$config->backup_shortname);
|
||||
if (!$file->copy_content_to($dir.'/'.$filename)) {
|
||||
$outcome = self::BACKUP_STATUS_ERROR;
|
||||
}
|
||||
@@ -428,6 +444,20 @@ abstract class backup_cron_automated_helper {
|
||||
$outcome = self::BACKUP_STATUS_ERROR;
|
||||
}
|
||||
|
||||
// Delete the backup file immediately if something went wrong.
|
||||
if ($outcome === self::BACKUP_STATUS_ERROR) {
|
||||
|
||||
// Delete the file from file area if exists.
|
||||
if (!empty($file)) {
|
||||
$file->delete();
|
||||
}
|
||||
|
||||
// Delete file from external storage if exists.
|
||||
if ($storage !== 0 && !empty($filename) && file_exists($dir.'/'.$filename)) {
|
||||
@unlink($dir.'/'.$filename);
|
||||
}
|
||||
}
|
||||
|
||||
$bc->destroy();
|
||||
unset($bc);
|
||||
|
||||
@@ -524,7 +554,7 @@ abstract class backup_cron_automated_helper {
|
||||
public static function set_state_running($running = true) {
|
||||
if ($running === true) {
|
||||
if (self::get_automated_backup_state() === self::STATE_RUNNING) {
|
||||
throw new backup_exception('backup_automated_already_running');
|
||||
throw new backup_helper_exception('backup_automated_already_running');
|
||||
}
|
||||
set_config('backup_auto_running', '1', 'backup');
|
||||
} else {
|
||||
|
||||
@@ -59,7 +59,13 @@ class database_logger extends base_logger {
|
||||
if ($this->levelcol) {
|
||||
$columns[$this->levelcol] = $level;
|
||||
}
|
||||
$columns[$this->messagecol] = $message; //TODO: should this be cleaned?
|
||||
$message = clean_param($message, PARAM_NOTAGS);
|
||||
// Check if the message exceeds the 255 character limit in the database,
|
||||
// if it does, shorten it so that it can be inserted successfully.
|
||||
if (textlib::strlen($message) > 255) {
|
||||
$message = textlib::substr($message, 0, 252) . '...';
|
||||
}
|
||||
$columns[$this->messagecol] = $message;
|
||||
return $this->insert_log_record($this->logtable, $columns);
|
||||
}
|
||||
|
||||
@@ -69,6 +75,6 @@ class database_logger extends base_logger {
|
||||
// to preserve DB logs if the whole backup/restore transaction is
|
||||
// rollback
|
||||
global $DB;
|
||||
return $DB->insert_record($this->logtable, $columns, false); // Don't return inserted id
|
||||
return $DB->insert_record($table, $columns, false); // Don't return inserted id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,13 +60,17 @@ abstract class base_plan implements checksumable, executable {
|
||||
$task->set_plan($this);
|
||||
// Append task settings to plan array, if not present, for comodity
|
||||
foreach ($task->get_settings() as $key => $setting) {
|
||||
if (!in_array($setting, $this->settings)) {
|
||||
$name = $setting->get_name();
|
||||
if (!isset($this->settings[$name])) {
|
||||
$this->settings[$name] = $setting;
|
||||
} else {
|
||||
throw new base_plan_exception('multiple_settings_by_name_found', $name);
|
||||
}
|
||||
// Check if there is already a setting for this name.
|
||||
$name = $setting->get_name();
|
||||
if (!isset($this->settings[$name])) {
|
||||
// There is no setting, so add it.
|
||||
$this->settings[$name] = $setting;
|
||||
} else if ($this->settings[$name] != $setting) {
|
||||
// If the setting already exists AND it is not the same setting,
|
||||
// then throw an error. (I.e. you're allowed to add the same
|
||||
// setting twice, but cannot add two different ones with same
|
||||
// name.)
|
||||
throw new base_plan_exception('multiple_settings_by_name_found', $name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class backup_base_optigroup_testcase extends basic_testcase {
|
||||
/**
|
||||
* Incorrect creation tests (attributes and final elements)
|
||||
*/
|
||||
function itest_wrong_creation() {
|
||||
function test_wrong_creation() {
|
||||
|
||||
// Create instance with invalid name
|
||||
try {
|
||||
|
||||
@@ -73,7 +73,7 @@ class core_backup_renderer extends plugin_renderer_base {
|
||||
*/
|
||||
public function backup_details($details, $nextstageurl) {
|
||||
$yestick = $this->output->pix_icon('i/valid', get_string('yes'));
|
||||
$notick = $this->output->pix_icon('i/valid', get_string('no'));
|
||||
$notick = $this->output->pix_icon('i/invalid', get_string('no'));
|
||||
|
||||
$html = html_writer::start_tag('div', array('class'=>'backup-restore'));
|
||||
|
||||
@@ -140,7 +140,7 @@ class core_backup_renderer extends plugin_renderer_base {
|
||||
}
|
||||
if (empty($table)) {
|
||||
$table = new html_table();
|
||||
$table->head = array('Module', 'Title', 'Userinfo');
|
||||
$table->head = array(get_string('module','backup'), get_string('title','backup'), get_string('userinfo','backup'));
|
||||
$table->colclasses = array('modulename', 'moduletitle', 'userinfoincluded');
|
||||
$table->align = array('left','left', 'center');
|
||||
$table->attributes = array('class'=>'activitytable generaltable');
|
||||
|
||||
@@ -475,8 +475,9 @@ class restore_ui_stage_schema extends restore_ui_stage {
|
||||
if ($this->stageform === null) {
|
||||
$form = new restore_schema_form($this, $PAGE->url);
|
||||
$tasks = $this->ui->get_tasks();
|
||||
$content = '';
|
||||
$courseheading = false;
|
||||
|
||||
$allsettings = array();
|
||||
foreach ($tasks as $task) {
|
||||
if (!($task instanceof restore_root_task)) {
|
||||
if (!$courseheading) {
|
||||
@@ -484,13 +485,11 @@ class restore_ui_stage_schema extends restore_ui_stage {
|
||||
$form->add_heading('coursesettings', get_string('coursesettings', 'backup'));
|
||||
$courseheading = true;
|
||||
}
|
||||
// First add each setting
|
||||
// Put each setting into an array of settings to add. Adding
|
||||
// a setting individually is a very slow operation, so we add
|
||||
// them all in a batch later on.
|
||||
foreach ($task->get_settings() as $setting) {
|
||||
$form->add_setting($setting, $task);
|
||||
}
|
||||
// The add all the dependencies
|
||||
foreach ($task->get_settings() as $setting) {
|
||||
$form->add_dependencies($setting);
|
||||
$allsettings[] = array($setting, $task);
|
||||
}
|
||||
} else if ($this->ui->enforce_changed_dependencies()) {
|
||||
// Only show these settings if dependencies changed them.
|
||||
@@ -505,6 +504,15 @@ class restore_ui_stage_schema extends restore_ui_stage {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actually add all the settings that we put in the array.
|
||||
$form->add_settings($allsettings);
|
||||
|
||||
// Add the dependencies for all the settings.
|
||||
foreach ($allsettings as $settingtask) {
|
||||
$form->add_dependencies($settingtask[0]);
|
||||
}
|
||||
|
||||
$this->stageform = $form;
|
||||
}
|
||||
return $this->stageform;
|
||||
|
||||
@@ -43,6 +43,18 @@ abstract class grouped_parser_processor extends simplified_parser_processor {
|
||||
protected $groupedpaths; // Paths we are requesting grouped
|
||||
protected $currentdata; // Where we'll be acummulating data
|
||||
|
||||
/**
|
||||
* Keep cache of parent directory paths for XML parsing.
|
||||
* @var array
|
||||
*/
|
||||
protected $parentcache = array();
|
||||
|
||||
/**
|
||||
* Remaining space for parent directory paths.
|
||||
* @var integer
|
||||
*/
|
||||
protected $parentcacheavailablesize = 2048;
|
||||
|
||||
public function __construct(array $paths = array()) {
|
||||
$this->groupedpaths = array();
|
||||
$this->currentdata = null;
|
||||
@@ -65,7 +77,7 @@ abstract class grouped_parser_processor extends simplified_parser_processor {
|
||||
$a->child = $found;
|
||||
throw new progressive_parser_exception('xml_grouped_child_found', $a);
|
||||
}
|
||||
$this->groupedpaths[] = $path;
|
||||
$this->groupedpaths[$path] = true;
|
||||
}
|
||||
parent::add_path($path);
|
||||
}
|
||||
@@ -141,7 +153,7 @@ abstract class grouped_parser_processor extends simplified_parser_processor {
|
||||
}
|
||||
|
||||
protected function path_is_grouped($path) {
|
||||
return in_array($path, $this->groupedpaths);
|
||||
return isset($this->groupedpaths[$path]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,16 +162,41 @@ abstract class grouped_parser_processor extends simplified_parser_processor {
|
||||
* false if not
|
||||
*/
|
||||
protected function grouped_parent_exists($path) {
|
||||
$parentpath = progressive_parser::dirname($path);
|
||||
$parentpath = $this->get_parent_path($path);
|
||||
|
||||
while ($parentpath != '/') {
|
||||
if ($this->path_is_grouped($parentpath)) {
|
||||
return $parentpath;
|
||||
}
|
||||
$parentpath = progressive_parser::dirname($parentpath);
|
||||
$parentpath = $this->get_parent_path($parentpath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent path using a local cache for performance.
|
||||
*
|
||||
* @param $path string The pathname you wish to obtain the parent name for.
|
||||
* @return string The parent pathname.
|
||||
*/
|
||||
protected function get_parent_path($path) {
|
||||
if (!isset($this->parentcache[$path])) {
|
||||
$this->parentcache[$path] = progressive_parser::dirname($path);
|
||||
$this->parentcacheavailablesize--;
|
||||
if ($this->parentcacheavailablesize < 0) {
|
||||
// Older first is cheaper than LRU. We use 10% as items are grouped together and the large quiz
|
||||
// restore from MDL-40585 used only 600 parent paths. This is an XML heirarchy, so common paths
|
||||
// are grouped near each other. eg; /question_bank/question_category/question/element. After keeping
|
||||
// question_bank paths in the cache when we move to another area and the question_bank cache is not
|
||||
// useful any longer.
|
||||
$this->parentcache = array_slice($this->parentcache, 200, null, true);
|
||||
$this->parentcacheavailablesize += 200;
|
||||
}
|
||||
}
|
||||
return $this->parentcache[$path];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function that will look for any grouped
|
||||
* child for the given path, returning it if found,
|
||||
@@ -167,7 +204,7 @@ abstract class grouped_parser_processor extends simplified_parser_processor {
|
||||
*/
|
||||
protected function grouped_child_exists($path) {
|
||||
$childpath = $path . '/';
|
||||
foreach ($this->groupedpaths as $groupedpath) {
|
||||
foreach ($this->groupedpaths as $groupedpath => $set) {
|
||||
if (strpos($groupedpath, $childpath) === 0) {
|
||||
return $groupedpath;
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ $communityid = optional_param('communityid', '', PARAM_INT);
|
||||
if ($remove != -1 and !empty($communityid) and confirm_sesskey()) {
|
||||
$communitymanager->block_community_remove_course($communityid, $USER->id);
|
||||
echo $OUTPUT->header();
|
||||
echo $renderer->remove_success(new moodle_url(get_referer(false)));
|
||||
echo $renderer->remove_success(new moodle_url('/course/view.php', array('id' => $courseid)));
|
||||
echo $OUTPUT->footer();
|
||||
die();
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ class block_community_manager {
|
||||
$community = $this->block_community_get_course($course->url, $userid);
|
||||
|
||||
if (empty($community)) {
|
||||
$community = new stdClass();
|
||||
$community->userid = $userid;
|
||||
$community->coursename = $course->name;
|
||||
$community->coursedescription = $course->description;
|
||||
|
||||
@@ -216,12 +216,12 @@ if (empty($completions)) {
|
||||
echo '(<i>';
|
||||
|
||||
if ($agg == COMPLETION_AGGREGATION_ALL) {
|
||||
echo strtolower(get_string('all', 'completion'));
|
||||
echo textlib::strtolower(get_string('all', 'completion'));
|
||||
} else {
|
||||
echo strtolower(get_string('any', 'completion'));
|
||||
echo textlib::strtolower(get_string('any', 'completion'));
|
||||
}
|
||||
|
||||
echo '</i> '.strtolower(get_string('required')).')';
|
||||
echo '</i> '.textlib::strtolower(get_string('required')).')';
|
||||
$agg_type = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
$string['adminview'] = 'Admin view';
|
||||
$string['allcourses'] = 'Admin user sees all courses';
|
||||
$string['configadminview'] = 'What should the admin see in the course list block?';
|
||||
$string['confighideallcourseslink'] = 'Hide "All courses" link at the bottom of the block. Link hiding does not affects Admin\'s view';
|
||||
$string['confighideallcourseslink'] = 'Remove the \'All courses\' link under the list of courses. (This setting does not affect the admin view.)';
|
||||
$string['course_list:addinstance'] = 'Add a new courses block';
|
||||
$string['course_list:myaddinstance'] = 'Add a new courses block to My home';
|
||||
$string['hideallcourseslink'] = 'Hide All courses link';
|
||||
$string['hideallcourseslink'] = 'Hide \'All courses\' link';
|
||||
$string['owncourses'] = 'Admin user sees own courses';
|
||||
$string['pluginname'] = 'Courses';
|
||||
|
||||
@@ -34,5 +34,17 @@ $capabilities = array(
|
||||
),
|
||||
|
||||
'clonepermissionsfrom' => 'moodle/my:manageblocks'
|
||||
),
|
||||
|
||||
'block/course_overview:addinstance' => array(
|
||||
'riskbitmask' => RISK_SPAM | RISK_XSS,
|
||||
|
||||
'captype' => 'write',
|
||||
'contextlevel' => CONTEXT_BLOCK,
|
||||
'archetypes' => array(
|
||||
'manager' => CAP_ALLOW
|
||||
),
|
||||
|
||||
'clonepermissionsfrom' => 'moodle/site:manageblocks'
|
||||
)
|
||||
);
|
||||
|
||||
@@ -27,6 +27,7 @@ $string['alwaysshowall'] = 'Always Show All';
|
||||
$string['collapseall'] = 'Collapse All Course Lists';
|
||||
$string['configotherexpanded'] = 'If enabled, Other Courses will be expanded by default unless overriden by user preferences.';
|
||||
$string['configpreservestates'] = 'If enabled, the collapsed/expanded states set by the user are stored and used on each load.';
|
||||
$string['course_overview:addinstance'] = 'Add a new course overview block';
|
||||
$string['course_overview:myaddinstance'] = 'Add a new course overview block to My home';
|
||||
$string['defaultmaxcourses'] = 'Default maximum courses';
|
||||
$string['defaultmaxcoursesdesc'] = 'Maximum courses which should be displayed on course overview block, 0 will show all courses';
|
||||
|
||||
@@ -31,8 +31,17 @@
|
||||
function block_course_overview_get_overviews($courses) {
|
||||
$htmlarray = array();
|
||||
if ($modules = get_plugin_list_with_function('mod', 'print_overview')) {
|
||||
foreach ($modules as $fname) {
|
||||
$fname($courses,$htmlarray);
|
||||
// Split courses list into batches with no more than MAX_MODINFO_CACHE_SIZE courses in one batch.
|
||||
// Otherwise we exceed the cache limit in get_fast_modinfo() and rebuild it too often.
|
||||
if (defined('MAX_MODINFO_CACHE_SIZE') && MAX_MODINFO_CACHE_SIZE > 0 && count($courses) > MAX_MODINFO_CACHE_SIZE) {
|
||||
$batches = array_chunk($courses, MAX_MODINFO_CACHE_SIZE, true);
|
||||
} else {
|
||||
$batches = array($courses);
|
||||
}
|
||||
foreach ($batches as $courses) {
|
||||
foreach ($modules as $fname) {
|
||||
$fname($courses, $htmlarray);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $htmlarray;
|
||||
|
||||
@@ -107,6 +107,9 @@ class block_course_overview_renderer extends plugin_renderer_base {
|
||||
// No need to pass title through s() here as it will be done automatically by html_writer.
|
||||
$attributes = array('title' => $course->fullname);
|
||||
if ($course->id > 0) {
|
||||
if (empty($course->visible)) {
|
||||
$attributes['class'] = 'dimmed';
|
||||
}
|
||||
$courseurl = new moodle_url('/course/view.php', array('id' => $course->id));
|
||||
$coursefullname = format_string($course->fullname, true, $course->id);
|
||||
$link = html_writer::link($courseurl, $coursefullname, $attributes);
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2012121000; // The current plugin version (Date: YYYYMMDDXX)
|
||||
$plugin->version = 2012121001; // The current plugin version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2012112900; // Requires this Moodle version
|
||||
$plugin->component = 'block_course_overview'; // Full name of the plugin (used for diagnostics)
|
||||
|
||||
@@ -332,7 +332,7 @@ class block_base {
|
||||
|
||||
/**
|
||||
* Subclasses should override this and return true if the
|
||||
* subclass block has a config_global.html file.
|
||||
* subclass block has a settings.php file.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
|
||||
@@ -105,7 +105,9 @@ class block_navigation_renderer extends plugin_renderer_base {
|
||||
if ($item->hidden) {
|
||||
$attributes['class'] = 'dimmed_text';
|
||||
}
|
||||
if (is_string($item->action) || empty($item->action) || ($item->type === navigation_node::TYPE_CATEGORY && empty($options['linkcategories']))) {
|
||||
if (is_string($item->action) || empty($item->action) ||
|
||||
(($item->type === navigation_node::TYPE_CATEGORY || $item->type === navigation_node::TYPE_MY_CATEGORY) &&
|
||||
empty($options['linkcategories']))) {
|
||||
$attributes['tabindex'] = '0'; //add tab support to span but still maintain character stream sequence.
|
||||
$content = html_writer::tag('span', $content, $attributes);
|
||||
} else if ($item->action instanceof action_link) {
|
||||
|
||||
+5
-1
@@ -376,11 +376,15 @@ BRANCH.prototype = {
|
||||
},
|
||||
/**
|
||||
* Attaches required events to the branch structure.
|
||||
*
|
||||
* @chainable
|
||||
* @method wire
|
||||
* @return {BRANCH} This function is chainable, it always returns itself.
|
||||
*/
|
||||
wire : function() {
|
||||
this.node = this.node || Y.one('#'+this.get('id'));
|
||||
if (!this.node) {
|
||||
return false;
|
||||
return this;
|
||||
}
|
||||
if (this.get('expandable')) {
|
||||
this.event_ajaxload = this.node.on('ajaxload|click', this.ajaxLoad, this);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/* RSS Feeds
|
||||
-------------------------*/
|
||||
.block_rss_client .list li:first-child {
|
||||
border-top-width: 0;
|
||||
}
|
||||
.block_rss_client .list li {
|
||||
border-top: 1px solid;
|
||||
padding: 5px;
|
||||
}
|
||||
@@ -84,15 +84,16 @@ if (!empty($rssrecord->preferredtitle)) {
|
||||
$feedtitle = $rss->get_title();
|
||||
}
|
||||
echo '<table align="center" width="50%" cellspacing="1">'."\n";
|
||||
echo '<tr><td colspan="2"><strong>'. $feedtitle .'</strong></td></tr>'."\n";
|
||||
echo '<tr><td colspan="2"><strong>'. s($feedtitle) .'</strong></td></tr>'."\n";
|
||||
foreach ($rss->get_items() as $item) {
|
||||
echo '<tr><td valign="middle">'."\n";
|
||||
echo '<a href="'. $item->get_link() .'" target="_blank"><strong>'. $item->get_title();
|
||||
echo '<a href="'.$item->get_link().'" target="_blank"><strong>';
|
||||
echo s($item->get_title());
|
||||
echo '</strong></a>'."\n";
|
||||
echo '</td>'."\n";
|
||||
echo '</tr>'."\n";
|
||||
echo '<tr><td colspan="2"><small>';
|
||||
echo $item->get_description() .'</small></td></tr>'."\n";
|
||||
echo format_text($item->get_description(), FORMAT_HTML) .'</small></td></tr>'."\n";
|
||||
}
|
||||
echo '</table>'."\n";
|
||||
|
||||
|
||||
@@ -75,6 +75,11 @@ class block_section_links extends block_base {
|
||||
$courseformatoptions = course_get_format($course)->get_format_options();
|
||||
$context = context_course::instance($course->id);
|
||||
|
||||
// Course format options 'numsections' is required to display the block.
|
||||
if (empty($courseformatoptions['numsections'])) {
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
if ($course->format == 'weeks' or $course->format == 'weekscss') {
|
||||
$highlight = ceil((time()-$course->startdate)/604800);
|
||||
$linktext = get_string('jumptocurrentweek', 'block_section_links');
|
||||
|
||||
@@ -110,12 +110,12 @@ class block_tags extends block_base {
|
||||
$content = '';
|
||||
$moretags = new moodle_url('/tag/coursetags_more.php', array('show'=>$tagtype));
|
||||
if ($tagtype == 'all') {
|
||||
$tags = coursetag_get_tags(0, 0, $this->config->tagtype, $this->config->numberoftags, 'name');
|
||||
$tags = coursetag_get_tags(0, 0, $this->config->tagtype, $this->config->numberoftags);
|
||||
} else if ($tagtype == 'course') {
|
||||
$tags = coursetag_get_tags($this->page->course->id, 0, $this->config->tagtype, $this->config->numberoftags, 'name');
|
||||
$tags = coursetag_get_tags($this->page->course->id, 0, $this->config->tagtype, $this->config->numberoftags);
|
||||
$moretags->param('courseid', $this->page->course->id);
|
||||
} else if ($tagtype == 'my') {
|
||||
$tags = coursetag_get_tags(0, $USER->id, $this->config->tagtype, $this->config->numberoftags, 'name');
|
||||
$tags = coursetag_get_tags(0, $USER->id, $this->config->tagtype, $this->config->numberoftags);
|
||||
}
|
||||
$tagcloud = tag_print_cloud($tags, 150, true);
|
||||
if (!$tagcloud) {
|
||||
|
||||
@@ -120,6 +120,7 @@ if ($action === 'delete'){
|
||||
print_error('nopermissionstodeleteentry', 'blog');
|
||||
} else {
|
||||
$entry->delete();
|
||||
blog_rss_delete_file($userid);
|
||||
redirect($returnurl);
|
||||
}
|
||||
} else if (blog_user_can_edit_entry($entry)) {
|
||||
|
||||
+7
-4
@@ -119,11 +119,14 @@ class core_blog_renderer extends plugin_renderer_base {
|
||||
// Body.
|
||||
$o .= format_text($entry->summary, $entry->summaryformat, array('overflowdiv' => true));
|
||||
|
||||
// Uniquehash is used as a link to an external blog.
|
||||
if (!empty($entry->uniquehash)) {
|
||||
$o .= $this->output->container_start('externalblog');
|
||||
$o .= html_writer::link($entry->uniquehash, get_string('linktooriginalentry', 'blog'));
|
||||
$o .= $this->output->container_end();
|
||||
// Uniquehash is used as a link to an external blog.
|
||||
$url = clean_param($entry->uniquehash, PARAM_URL);
|
||||
if (!empty($url)) {
|
||||
$o .= $this->output->container_start('externalblog');
|
||||
$o .= html_writer::link($url, get_string('linktooriginalentry', 'blog'));
|
||||
$o .= $this->output->container_end();
|
||||
}
|
||||
}
|
||||
|
||||
// Links to tags.
|
||||
|
||||
@@ -330,3 +330,17 @@ function blog_rss_save_file($type, $id, $tagid=0, $contents='') {
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the supplied user's cached blog post RSS feed.
|
||||
* Only user blogs are available by RSS.
|
||||
* This doesn't call rss_delete_file() as blog RSS caching uses it's own file structure.
|
||||
*
|
||||
* @param int $userid
|
||||
*/
|
||||
function blog_rss_delete_file($userid) {
|
||||
$filename = blog_rss_file_name('user', $userid);
|
||||
if (file_exists($filename)) {
|
||||
unlink($filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+7
-8
@@ -24,8 +24,8 @@ A definition:
|
||||
'overrideclassfile' => null, // Optional
|
||||
'datasource' => null, // Optional
|
||||
'datasourcefile' => null, // Optional
|
||||
'persistent' => false, // Optional
|
||||
'persistentmaxsize' => false, // Optional
|
||||
'staticacceleration' => false, // Optional
|
||||
'staticaccelerationsize' => false, // Optional
|
||||
'ttl' => 0, // Optional
|
||||
'mappingsonly' => false // Optional
|
||||
'invalidationevents' => array( // Optional
|
||||
@@ -142,19 +142,18 @@ The following optional settings can also be defined:
|
||||
* overrideclassfile - Included if required when using the overrideclass param.
|
||||
* datasource - If provided this class will be used as a data source for the definition. It must implement the cache_data_source interface.
|
||||
* datasourcefile - Included if required when using the datasource param.
|
||||
* persistent - If set to true the loader will be stored when first created and provided to subsequent requests. More on this later.
|
||||
* persistentmaxsize - If set to an int this will be the maximum number of items stored in the persistent cache.
|
||||
* staticacceleration - Any data passing through the cache will be held onto to make subsequent requests for it faster.
|
||||
* staticaccelerationsize - If set to an int this will be the maximum number of items stored in the static acceleration array.
|
||||
* ttl - Can be used to set a ttl value for data being set for this cache.
|
||||
* mappingsonly - This definition can only be used if there is a store mapping for it. More on this later.
|
||||
* invalidationevents - An array of events that should trigger this cache to invalidate.
|
||||
|
||||
It's important to note that internally the definition is also aware of the component. This is picked up when the definition is read, based upon the location of the caches.php file.
|
||||
|
||||
The persist option.
|
||||
As noted the persist option causes the loader generated for this definition to be stored when first created. Subsequent requests for this definition will be given the original loader instance.
|
||||
The staticacceleration option.
|
||||
Data passed to or retrieved from the loader and its chained loaders gets cached by the instance.
|
||||
This option should be used when you know you will require the loader several times and perhaps in different areas of code.
|
||||
Because it caches key=>value data it avoids the need to re-fetch things from stores after the first request. Its good for performance, bad for memory.
|
||||
Memory use can be controlled by setting the staticaccelerationsize option.
|
||||
It should be used sparingly.
|
||||
|
||||
The mappingsonly option.
|
||||
@@ -227,4 +226,4 @@ Cache information can be invalidated in two ways.
|
||||
The first method is designed to be used when you have a single known definition you want to invalidate entries within.
|
||||
The second method is a lot more intensive for the system. There are defined invalidation events that definitions can "subscribe" to (through the definitions invalidationevents option).
|
||||
When you invalidate by event the cache API finds all of the definitions that subscribe to the event, it then loads the stores for each of those definitions and purges the keys from each store.
|
||||
This is obviously a recursive, and therefore, intense process.
|
||||
This is obviously a recursive, and therefore, intense process.
|
||||
|
||||
Vendored
+74
-28
@@ -75,19 +75,14 @@ defined('MOODLE_INTERNAL') || die();
|
||||
* [string] A class to use as the data loader for this definition.
|
||||
* Any class used here must inherit the cache_data_loader interface.
|
||||
* + datasourcefile
|
||||
* [string] Suplements the above setting indicated the file containing the class to be used. This file is included when
|
||||
* [string] Supplements the above setting indicating the file containing the class to be used. This file is included when
|
||||
* required.
|
||||
* + persistent
|
||||
* [bool] This setting does two important things. First it tells the cache API to only instantiate the cache structure for
|
||||
* this definition once, further requests will be given the original instance.
|
||||
* Second the cache loader will keep an array of the items set and retrieved to the cache during the request.
|
||||
* This has several advantages including better performance without needing to start passing the cache instance between
|
||||
* function calls, the downside is that the cache instance + the items used stay within memory.
|
||||
* Consider using this setting when you know that there are going to be many calls to the cache for the same information
|
||||
* or when you are converting existing code to the cache and need to access the cache within functions but don't want
|
||||
* to add it as an argument to the function.
|
||||
* + persistentmaxsize
|
||||
* [int] This supplements the above setting by limiting the number of items in the caches persistent array of items.
|
||||
* + staticacceleration
|
||||
* The cache loader will keep an array of the items set and retrieved to the cache during the request.
|
||||
* Consider using this setting when you know that there are going to be many calls to the cache for the same information.
|
||||
* Requests for data in this array will be ultra fast, but it will cost memory.
|
||||
* + staticaccelerationsize
|
||||
* [int] This supplements the above setting by limiting the number of items in the static acceleration array.
|
||||
* Tweaking this setting lower will allow you to minimise the memory implications above while hopefully still managing to
|
||||
* offset calls to the cache store.
|
||||
* + ttl
|
||||
@@ -228,16 +223,16 @@ class cache_definition {
|
||||
protected $datasourceaggregate = null;
|
||||
|
||||
/**
|
||||
* Set to true if the definitions cache should be persistent
|
||||
* Set to true if the cache should hold onto items passing through it to speed up subsequent requests.
|
||||
* @var bool
|
||||
*/
|
||||
protected $persistent = false;
|
||||
protected $staticacceleration = false;
|
||||
|
||||
/**
|
||||
* The persistent item array max size.
|
||||
* The maximum number of items that static acceleration cache should hold onto.
|
||||
* @var int
|
||||
*/
|
||||
protected $persistentmaxsize = false;
|
||||
protected $staticaccelerationsize = false;
|
||||
|
||||
/**
|
||||
* The TTL for data in this cache. Please don't use this, instead use event driven invalidation.
|
||||
@@ -320,8 +315,8 @@ class cache_definition {
|
||||
$overrideclassfile = null;
|
||||
$datasource = null;
|
||||
$datasourcefile = null;
|
||||
$persistent = false;
|
||||
$persistentmaxsize = false;
|
||||
$staticacceleration = false;
|
||||
$staticaccelerationsize = false;
|
||||
$ttl = 0;
|
||||
$mappingsonly = false;
|
||||
$invalidationevents = array();
|
||||
@@ -373,10 +368,18 @@ class cache_definition {
|
||||
}
|
||||
|
||||
if (array_key_exists('persistent', $definition)) {
|
||||
$persistent = (bool)$definition['persistent'];
|
||||
// Ahhh this is the legacy persistent option.
|
||||
$staticacceleration = (bool)$definition['persistent'];
|
||||
}
|
||||
if (array_key_exists('staticacceleration', $definition)) {
|
||||
$staticacceleration = (bool)$definition['staticacceleration'];
|
||||
}
|
||||
if (array_key_exists('persistentmaxsize', $definition)) {
|
||||
$persistentmaxsize = (int)$definition['persistentmaxsize'];
|
||||
// Ahhh this is the legacy persistentmaxsize option.
|
||||
$staticaccelerationsize = (int)$definition['persistentmaxsize'];
|
||||
}
|
||||
if (array_key_exists('staticaccelerationsize', $definition)) {
|
||||
$staticaccelerationsize = (int)$definition['staticaccelerationsize'];
|
||||
}
|
||||
if (array_key_exists('ttl', $definition)) {
|
||||
$ttl = (int)$definition['ttl'];
|
||||
@@ -452,8 +455,8 @@ class cache_definition {
|
||||
$cachedefinition->datasource = $datasource;
|
||||
$cachedefinition->datasourcefile = $datasourcefile;
|
||||
$cachedefinition->datasourceaggregate = $datasourceaggregate;
|
||||
$cachedefinition->persistent = $persistent;
|
||||
$cachedefinition->persistentmaxsize = $persistentmaxsize;
|
||||
$cachedefinition->staticacceleration = $staticacceleration;
|
||||
$cachedefinition->staticaccelerationsize = $staticaccelerationsize;
|
||||
$cachedefinition->ttl = $ttl;
|
||||
$cachedefinition->mappingsonly = $mappingsonly;
|
||||
$cachedefinition->invalidationevents = $invalidationevents;
|
||||
@@ -474,7 +477,8 @@ class cache_definition {
|
||||
* - simplekeys : Set to true if the keys you will use are a-zA-Z0-9_
|
||||
* - simpledata : Set to true if the type of the data you are going to store is scalar, or an array of scalar vars
|
||||
* - overrideclass : The class to use as the loader.
|
||||
* - persistent : If set to true the cache will persist construction requests.
|
||||
* - staticacceleration : If set to true the cache will hold onto data passing through it.
|
||||
* - staticaccelerationsize : Set it to an int to limit the size of the staticacceleration cache.
|
||||
* @return cache_application|cache_session|cache_request
|
||||
*/
|
||||
public static function load_adhoc($mode, $component, $area, array $options = array()) {
|
||||
@@ -491,7 +495,14 @@ class cache_definition {
|
||||
$definition['simpledata'] = $options['simpledata'];
|
||||
}
|
||||
if (!empty($options['persistent'])) {
|
||||
$definition['persistent'] = $options['persistent'];
|
||||
// Ahhh this is the legacy persistent option.
|
||||
$definition['staticacceleration'] = (bool)$options['persistent'];
|
||||
}
|
||||
if (!empty($options['staticacceleration'])) {
|
||||
$definition['staticacceleration'] = (bool)$options['staticacceleration'];
|
||||
}
|
||||
if (!empty($options['staticaccelerationsize'])) {
|
||||
$definition['staticaccelerationsize'] = (int)$options['staticaccelerationsize'];
|
||||
}
|
||||
if (!empty($options['overrideclass'])) {
|
||||
$definition['overrideclass'] = $options['overrideclass'];
|
||||
@@ -716,18 +727,53 @@ class cache_definition {
|
||||
|
||||
/**
|
||||
* Returns true if this definitions cache should be made persistent.
|
||||
*
|
||||
* Please call {@link cache_definition::use_static_acceleration()} instead.
|
||||
*
|
||||
* @deprecated since 2.4.7
|
||||
* @return bool
|
||||
*/
|
||||
public function should_be_persistent() {
|
||||
return $this->persistent || $this->mode === cache_store::MODE_SESSION;
|
||||
debugging('Please upgrade your code to use cache_definition::use_static_acceleration', DEBUG_DEVELOPER);
|
||||
return $this->use_static_acceleration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the max size for the persistent item array in the cache.
|
||||
* Returns true if we should hold onto the data flowing through the cache.
|
||||
*
|
||||
* If set to true data flowing through the cache will be stored in a static variable
|
||||
* to make subsequent requests for the data much faster.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function use_static_acceleration() {
|
||||
if ($this->mode === cache_store::MODE_REQUEST) {
|
||||
// Request caches should never use static acceleration - it just doesn't make sense.
|
||||
return false;
|
||||
}
|
||||
return $this->staticacceleration || $this->mode === cache_store::MODE_SESSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the max size for the static acceleration array.
|
||||
*
|
||||
* Please call {@link cache_definition::get_static_acceleration_size()} instead.
|
||||
*
|
||||
* @see cache_definition::get_static_acceleration_size()
|
||||
* @deprecated since 2.4.7
|
||||
* @return int
|
||||
*/
|
||||
public function get_persistent_max_size() {
|
||||
return $this->persistentmaxsize;
|
||||
debugging('Please upgrade your code to call cache_definition::get_static_acceleration_size', DEBUG_DEVELOPER);
|
||||
return $this->get_static_acceleration_size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the max size for the static acceleration array.
|
||||
* @return int
|
||||
*/
|
||||
public function get_static_acceleration_size() {
|
||||
return $this->staticaccelerationsize;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -821,4 +867,4 @@ class cache_definition {
|
||||
protected function get_cache_identifier() {
|
||||
return cache_helper::get_site_identifier();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+9
-8
@@ -46,14 +46,15 @@ class cachestore_dummy extends cache_store {
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Gets set to true if this store is going to persist data.
|
||||
* This happens when the definition doesn't require it as the loader will not be persisting information and something has to.
|
||||
* Gets set to true if this store is going to store data.
|
||||
* This happens when the definition doesn't require static acceleration as the loader will not be storing information and
|
||||
* something has to.
|
||||
* @var bool
|
||||
*/
|
||||
protected $persist = false;
|
||||
|
||||
/**
|
||||
* The persistent store array
|
||||
* The stored data array
|
||||
* @var array
|
||||
*/
|
||||
protected $store = array();
|
||||
@@ -106,13 +107,13 @@ class cachestore_dummy extends cache_store {
|
||||
* @param cache_definition $definition
|
||||
*/
|
||||
public function initialise(cache_definition $definition) {
|
||||
// If the definition isn't persistent then we need to be persistent here.
|
||||
// If the definition isn't using static acceleration then we need to be store data here.
|
||||
// The reasoning behind this is that:
|
||||
// - If the definition is persistent then the cache loader is going to
|
||||
// store things in its persistent cache.
|
||||
// - If the definition is not persistent then the cache loader won't try to store anything
|
||||
// - If the definition is using static acceleration then the cache loader is going to
|
||||
// store things in its static array.
|
||||
// - If the definition is not using static acceleration then the cache loader won't try to store anything
|
||||
// and we will need to store it here in order to make sure it is accessible.
|
||||
$this->persist = !$definition->should_be_persistent();
|
||||
$this->persist = !$definition->use_static_acceleration();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+29
-10
@@ -150,9 +150,7 @@ class cache_factory {
|
||||
*/
|
||||
public static function reset() {
|
||||
$factory = self::instance();
|
||||
$factory->cachesfromdefinitions = array();
|
||||
$factory->cachesfromparams = array();
|
||||
$factory->stores = array();
|
||||
$factory->reset_cache_instances();
|
||||
$factory->configs = array();
|
||||
$factory->definitions = array();
|
||||
$factory->lockplugins = array(); // MUST be null in order to force its regeneration.
|
||||
@@ -160,6 +158,18 @@ class cache_factory {
|
||||
$factory->state = self::STATE_UNINITIALISED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the stores, clearing the array of created stores.
|
||||
*
|
||||
* Cache objects still held onto by the code that initialised them will remain as is
|
||||
* however all future requests for a cache/store will lead to a new instance being re-initialised.
|
||||
*/
|
||||
public function reset_cache_instances() {
|
||||
$this->cachesfromdefinitions = array();
|
||||
$this->cachesfromparams = array();
|
||||
$this->stores = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cache object given the parameters for a definition.
|
||||
*
|
||||
@@ -181,9 +191,8 @@ class cache_factory {
|
||||
$definition = $this->create_definition($component, $area, $aggregate);
|
||||
$definition->set_identifiers($identifiers);
|
||||
$cache = $this->create_cache($definition, $identifiers);
|
||||
if ($definition->should_be_persistent()) {
|
||||
$this->cachesfromdefinitions[$definitionname] = $cache;
|
||||
}
|
||||
// Loaders are always held onto to speed up subsequent requests.
|
||||
$this->cachesfromdefinitions[$definitionname] = $cache;
|
||||
return $cache;
|
||||
}
|
||||
|
||||
@@ -199,7 +208,8 @@ class cache_factory {
|
||||
* @param array $options An array of options, available options are:
|
||||
* - simplekeys : Set to true if the keys you will use are a-zA-Z0-9_
|
||||
* - simpledata : Set to true if the type of the data you are going to store is scalar, or an array of scalar vars
|
||||
* - persistent : If set to true the cache will persist construction requests.
|
||||
* - staticacceleration : If set to true the cache will hold onto data passing through it.
|
||||
* - staticaccelerationsize : The maximum number of items to hold onto for acceleration purposes.
|
||||
* @return cache_application|cache_session|cache_request
|
||||
*/
|
||||
public function create_cache_from_params($mode, $component, $area, array $identifiers = array(), array $options = array()) {
|
||||
@@ -210,9 +220,7 @@ class cache_factory {
|
||||
$definition = cache_definition::load_adhoc($mode, $component, $area, $options);
|
||||
$definition->set_identifiers($identifiers);
|
||||
$cache = $this->create_cache($definition, $identifiers);
|
||||
if ($definition->should_be_persistent()) {
|
||||
$this->cachesfromparams[$key] = $cache;
|
||||
}
|
||||
$this->cachesfromparams[$key] = $cache;
|
||||
return $cache;
|
||||
}
|
||||
|
||||
@@ -297,6 +305,15 @@ class cache_factory {
|
||||
return $this->definitionstores[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache instances that have been used within this request.
|
||||
* @since 2.4.7
|
||||
* @return array
|
||||
*/
|
||||
public function get_caches_in_use() {
|
||||
return $this->cachesfromdefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cache config instance with the ability to write if required.
|
||||
*
|
||||
@@ -585,7 +602,9 @@ class cache_factory {
|
||||
* </code>
|
||||
*/
|
||||
public static function disable_stores() {
|
||||
// First reset to clear any static acceleration array.
|
||||
$factory = self::instance();
|
||||
$factory->reset_cache_instances();
|
||||
$factory->set_state(self::STATE_STORES_DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+32
-20
@@ -238,19 +238,24 @@ class cache_helper {
|
||||
$instance = cache_config::instance();
|
||||
$invalidationeventset = false;
|
||||
$factory = cache_factory::instance();
|
||||
$inuse = $factory->get_caches_in_use();
|
||||
foreach ($instance->get_definitions() as $name => $definitionarr) {
|
||||
$definition = cache_definition::load($name, $definitionarr);
|
||||
if ($definition->invalidates_on_event($event)) {
|
||||
// OK at this point we know that the definition has information to invalidate on the event.
|
||||
// There are two routes, either its an application cache in which case we can invalidate it now.
|
||||
// or it is a persistent cache that also needs to be invalidated now.
|
||||
if ($definition->get_mode() === cache_store::MODE_APPLICATION || $definition->should_be_persistent()) {
|
||||
$cache = $factory->create_cache_from_definition($definition->get_component(), $definition->get_area());
|
||||
$cache->delete_many($keys);
|
||||
// First up check if there is a cache loader for this definition already.
|
||||
// If there is we need to invalidate the keys from there.
|
||||
$definitionkey = $definition->get_component().'/'.$definition->get_area();
|
||||
if (isset($inuse[$definitionkey])) {
|
||||
$inuse[$definitionkey]->delete_many($keys);
|
||||
}
|
||||
|
||||
// We need to flag the event in the "Event invalidation" cache if it hasn't already happened.
|
||||
if ($invalidationeventset === false) {
|
||||
// We should only log events for application and session caches.
|
||||
// Request caches shouldn't have events as all data is lost at the end of the request.
|
||||
// Events should only be logged once of course and likely several definitions are watching so we
|
||||
// track its logging with $invalidationeventset.
|
||||
$logevent = ($invalidationeventset === false && $definition->get_mode() !== cache_store::MODE_REQUEST);
|
||||
|
||||
if ($logevent) {
|
||||
// Get the event invalidation cache.
|
||||
$cache = cache::make('core', 'eventinvalidation');
|
||||
// Get any existing invalidated keys for this cache.
|
||||
@@ -307,23 +312,25 @@ class cache_helper {
|
||||
$instance = cache_config::instance();
|
||||
$invalidationeventset = false;
|
||||
$factory = cache_factory::instance();
|
||||
$inuse = $factory->get_caches_in_use();
|
||||
foreach ($instance->get_definitions() as $name => $definitionarr) {
|
||||
$definition = cache_definition::load($name, $definitionarr);
|
||||
if ($definition->invalidates_on_event($event)) {
|
||||
// Check if this definition would result in a persistent loader being in use.
|
||||
if ($definition->should_be_persistent()) {
|
||||
// There may be a persistent cache loader. Lets purge that first so that any persistent data is removed.
|
||||
$cache = $factory->create_cache_from_definition($definition->get_component(), $definition->get_area());
|
||||
$cache->purge();
|
||||
}
|
||||
// Get all of the store instances that are in use for this store.
|
||||
$stores = $factory->get_store_instances_in_use($definition);
|
||||
foreach ($stores as $store) {
|
||||
// Purge each store individually.
|
||||
$store->purge();
|
||||
// First up check if there is a cache loader for this definition already.
|
||||
// If there is we need to invalidate the keys from there.
|
||||
$definitionkey = $definition->get_component().'/'.$definition->get_area();
|
||||
if (isset($inuse[$definitionkey])) {
|
||||
$inuse[$definitionkey]->purge();
|
||||
}
|
||||
|
||||
// We should only log events for application and session caches.
|
||||
// Request caches shouldn't have events as all data is lost at the end of the request.
|
||||
// Events should only be logged once of course and likely several definitions are watching so we
|
||||
// track its logging with $invalidationeventset.
|
||||
$logevent = ($invalidationeventset === false && $definition->get_mode() !== cache_store::MODE_REQUEST);
|
||||
|
||||
// We need to flag the event in the "Event invalidation" cache if it hasn't already happened.
|
||||
if ($invalidationeventset === false) {
|
||||
if ($logevent && $invalidationeventset === false) {
|
||||
// Get the event invalidation cache.
|
||||
$cache = cache::make('core', 'eventinvalidation');
|
||||
// Create a key to invalidate all.
|
||||
@@ -345,6 +352,11 @@ class cache_helper {
|
||||
* @param string $definition
|
||||
*/
|
||||
protected static function ensure_ready_for_stats($store, $definition) {
|
||||
// This function is performance-sensitive, so exit as quickly as possible
|
||||
// if we do not need to do anything.
|
||||
if (isset(self::$stats[$definition][$store])) {
|
||||
return;
|
||||
}
|
||||
if (!array_key_exists($definition, self::$stats)) {
|
||||
self::$stats[$definition] = array(
|
||||
$store => array(
|
||||
|
||||
Vendored
+287
-208
@@ -93,41 +93,46 @@ class cache implements cache_loader {
|
||||
private $supportsnativettl = null;
|
||||
|
||||
/**
|
||||
* Gets set to true if the cache is going to be using the build in static "persist" cache.
|
||||
* The persist cache statically caches items used during the lifetime of the request. This greatly speeds up interaction
|
||||
* Gets set to true if the cache is going to be using a static array for acceleration.
|
||||
* The array statically caches items used during the lifetime of the request. This greatly speeds up interaction
|
||||
* with the cache in areas where it will be repetitively hit for the same information such as with strings.
|
||||
* There are several other variables to control how this persist cache works.
|
||||
* There are several other variables to control how this static acceleration array works.
|
||||
* @var bool
|
||||
*/
|
||||
private $persist = false;
|
||||
private $staticacceleration = false;
|
||||
|
||||
/**
|
||||
* The persist cache itself.
|
||||
* The static acceleration array.
|
||||
* Items will be stored in this cache as they were provided. This ensure there is no unnecessary processing taking place.
|
||||
* @var array
|
||||
*/
|
||||
private $persistcache = array();
|
||||
private $staticaccelerationarray = array();
|
||||
|
||||
/**
|
||||
* The number of items in the persist cache. Avoids count calls like you wouldn't believe.
|
||||
* The number of items in the static acceleration array. Avoids count calls like you wouldn't believe.
|
||||
* @var int
|
||||
*/
|
||||
private $persistcount = 0;
|
||||
private $staticaccelerationcount = 0;
|
||||
|
||||
/**
|
||||
* An array containing just the keys being used in the persist cache.
|
||||
* This seems redundant perhaps but is used when managing the size of the persist cache.
|
||||
* An array containing just the keys being used in the static acceleration array.
|
||||
* This seems redundant perhaps but is used when managing the size of the static acceleration array.
|
||||
* Items are added to the end of the array and the when we need to reduce the size of the cache we use the
|
||||
* key that is first on this array.
|
||||
* @var array
|
||||
*/
|
||||
private $persistkeys = array();
|
||||
private $staticaccelerationkeys = array();
|
||||
|
||||
/**
|
||||
* The maximum size of the persist cache. If set to false there is no max size.
|
||||
* Caches that make use of the persist cache should seriously consider setting this to something reasonably small, but
|
||||
* The maximum size of the static acceleration array.
|
||||
*
|
||||
* If set to false there is no max size.
|
||||
* Caches that make use of static acceleration should seriously consider setting this to something reasonably small, but
|
||||
* still large enough to offset repetitive calls.
|
||||
*
|
||||
* @var int|false
|
||||
*/
|
||||
private $persistmaxsize = false;
|
||||
private $staticaccelerationsize = false;
|
||||
|
||||
/**
|
||||
* Gets set to true during initialisation if the definition is making use of a ttl.
|
||||
@@ -179,7 +184,8 @@ class cache implements cache_loader {
|
||||
* @param array $options An array of options, available options are:
|
||||
* - simplekeys : Set to true if the keys you will use are a-zA-Z0-9_
|
||||
* - simpledata : Set to true if the type of the data you are going to store is scalar, or an array of scalar vars
|
||||
* - persistent : If set to true the cache will persist construction requests.
|
||||
* - staticacceleration : If set to true the cache will hold onto data passing through it.
|
||||
* - staticaccelerationsize : The max size for the static acceleration array.
|
||||
* @return cache_application|cache_session|cache_store
|
||||
*/
|
||||
public static function make_from_params($mode, $component, $area, array $identifiers = array(), array $options = array()) {
|
||||
@@ -216,9 +222,9 @@ class cache implements cache_loader {
|
||||
$this->datasource = $loader;
|
||||
}
|
||||
$this->definition->generate_definition_hash();
|
||||
$this->persist = $this->definition->should_be_persistent();
|
||||
if ($this->persist) {
|
||||
$this->persistmaxsize = $this->definition->get_persistent_max_size();
|
||||
$this->staticacceleration = $this->definition->use_static_acceleration();
|
||||
if ($this->staticacceleration) {
|
||||
$this->staticaccelerationsize = $this->definition->get_static_acceleration_size();
|
||||
}
|
||||
$this->hasattl = ($this->definition->get_ttl() > 0);
|
||||
}
|
||||
@@ -226,7 +232,7 @@ class cache implements cache_loader {
|
||||
/**
|
||||
* Used to inform the loader of its state as a sub loader, or as the top of the chain.
|
||||
*
|
||||
* This is important as it ensures that we do not have more than one loader keeping persistent data.
|
||||
* This is important as it ensures that we do not have more than one loader keeping static acceleration data.
|
||||
* Subloaders need to be "pure" loaders in the sense that they are used to store and retrieve information from stores or the
|
||||
* next loader/data source in the chain.
|
||||
* Nothing fancy, nothing flash.
|
||||
@@ -236,14 +242,14 @@ class cache implements cache_loader {
|
||||
protected function set_is_sub_loader($setting = true) {
|
||||
if ($setting) {
|
||||
$this->subloader = true;
|
||||
// Subloaders should not keep persistent data.
|
||||
$this->persist = false;
|
||||
$this->persistmaxsize = false;
|
||||
// Subloaders should not keep static acceleration data.
|
||||
$this->staticacceleration = false;
|
||||
$this->staticaccelerationsize = false;
|
||||
} else {
|
||||
$this->subloader = true;
|
||||
$this->persist = $this->definition->should_be_persistent();
|
||||
if ($this->persist) {
|
||||
$this->persistmaxsize = $this->definition->get_persistent_max_size();
|
||||
$this->staticacceleration = $this->definition->use_static_acceleration();
|
||||
if ($this->staticacceleration) {
|
||||
$this->staticaccelerationsize = $this->definition->get_static_acceleration_size();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,12 +275,12 @@ class cache implements cache_loader {
|
||||
* In advanced cases an array may be useful such as in situations requiring the multi-key functionality.
|
||||
* @param int $strictness One of IGNORE_MISSING | MUST_EXIST
|
||||
* @return mixed|false The data from the cache or false if the key did not exist within the cache.
|
||||
* @throws moodle_exception
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function get($key, $strictness = IGNORE_MISSING) {
|
||||
// 1. Parse the key.
|
||||
$parsedkey = $this->parse_key($key);
|
||||
// 2. Get it from the persist cache if we can (only when persist is enabled and it has already been requested/set).
|
||||
// 2. Get it from the static acceleration array if we can (only when it is enabled and it has already been requested/set).
|
||||
$result = false;
|
||||
if ($this->is_using_persist_cache()) {
|
||||
$result = $this->get_from_persist_cache($parsedkey);
|
||||
@@ -289,7 +295,7 @@ class cache implements cache_loader {
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
// 3. Get it from the store. Obviously wasn't in the persist cache.
|
||||
// 3. Get it from the store. Obviously wasn't in the static acceleration array.
|
||||
$result = $this->store->get($parsedkey);
|
||||
if ($result !== false) {
|
||||
if ($result instanceof cache_ttl_wrapper) {
|
||||
@@ -327,7 +333,7 @@ class cache implements cache_loader {
|
||||
}
|
||||
// 5. Validate strictness.
|
||||
if ($strictness === MUST_EXIST && $result === false) {
|
||||
throw new moodle_exception('Requested key did not exist in any cache stores and could not be loaded.');
|
||||
throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
|
||||
}
|
||||
// 6. Set it to the store if we got it from the loader/datasource.
|
||||
if ($setaftervalidation) {
|
||||
@@ -361,7 +367,7 @@ class cache implements cache_loader {
|
||||
* @return array An array of key value pairs for the items that could be retrieved from the cache.
|
||||
* If MUST_EXIST was used and not all keys existed within the cache then an exception will be thrown.
|
||||
* Otherwise any key that did not exist will have a data value of false within the results.
|
||||
* @throws moodle_exception
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function get_many(array $keys, $strictness = IGNORE_MISSING) {
|
||||
|
||||
@@ -402,6 +408,9 @@ class cache implements cache_loader {
|
||||
if ($value instanceof cache_cached_object) {
|
||||
$value = $value->restore_object();
|
||||
}
|
||||
if ($value !== false && $this->is_using_persist_cache()) {
|
||||
$this->set_in_persist_cache($key, $value);
|
||||
}
|
||||
$resultstore[$key] = $value;
|
||||
}
|
||||
}
|
||||
@@ -418,7 +427,7 @@ class cache implements cache_loader {
|
||||
$missingkeys = array();
|
||||
foreach ($result as $key => $value) {
|
||||
if ($value === false) {
|
||||
$missingkeys[] = ($usingloader) ? $key : $parsedkeys[$key];
|
||||
$missingkeys[] = $parsedkeys[$key];
|
||||
}
|
||||
}
|
||||
if (!empty($missingkeys)) {
|
||||
@@ -428,11 +437,9 @@ class cache implements cache_loader {
|
||||
$resultmissing = $this->datasource->load_many_for_cache($missingkeys);
|
||||
}
|
||||
foreach ($resultmissing as $key => $value) {
|
||||
$pkey = ($usingloader) ? $key : $keysparsed[$key];
|
||||
$realkey = ($usingloader) ? $parsedkeys[$key] : $key;
|
||||
$result[$pkey] = $value;
|
||||
$result[$keysparsed[$key]] = $value;
|
||||
if ($value !== false) {
|
||||
$this->set($realkey, $value);
|
||||
$this->set($key, $value);
|
||||
}
|
||||
}
|
||||
unset($resultmissing);
|
||||
@@ -451,7 +458,7 @@ class cache implements cache_loader {
|
||||
if ($strictness === MUST_EXIST) {
|
||||
foreach ($keys as $key) {
|
||||
if (!array_key_exists($key, $fullresult)) {
|
||||
throw new moodle_exception('Not all the requested keys existed within the cache stores.');
|
||||
throw new coding_exception('Not all the requested keys existed within the cache stores.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -481,6 +488,11 @@ class cache implements cache_loader {
|
||||
if ($this->perfdebug) {
|
||||
cache_helper::record_cache_set($this->storetype, $this->definition->get_id());
|
||||
}
|
||||
if ($this->loader !== false) {
|
||||
// We have a loader available set it there as well.
|
||||
// We have to let the loader do its own parsing of data as it may be unique.
|
||||
$this->loader->set($key, $data);
|
||||
}
|
||||
if (is_object($data) && $data instanceof cacheable_object) {
|
||||
$data = new cache_cached_object($data);
|
||||
} else if (!is_scalar($data)) {
|
||||
@@ -504,6 +516,7 @@ class cache implements cache_loader {
|
||||
* Removes references where required.
|
||||
*
|
||||
* @param stdClass|array $data
|
||||
* @return mixed What ever was put in but without any references.
|
||||
*/
|
||||
protected function unref($data) {
|
||||
if ($this->definition->uses_simple_data()) {
|
||||
@@ -591,9 +604,14 @@ class cache implements cache_loader {
|
||||
* ... if they care that is.
|
||||
*/
|
||||
public function set_many(array $keyvaluearray) {
|
||||
if ($this->loader !== false) {
|
||||
// We have a loader available set it there as well.
|
||||
// We have to let the loader do its own parsing of data as it may be unique.
|
||||
$this->loader->set_many($keyvaluearray);
|
||||
}
|
||||
$data = array();
|
||||
$simulatettl = $this->has_a_ttl() && !$this->store_supports_native_ttl();
|
||||
$usepersistcache = $this->is_using_persist_cache();
|
||||
$usestaticaccelerationarray = $this->is_using_persist_cache();
|
||||
foreach ($keyvaluearray as $key => $value) {
|
||||
if (is_object($value) && $value instanceof cacheable_object) {
|
||||
$value = new cache_cached_object($value);
|
||||
@@ -611,7 +629,7 @@ class cache implements cache_loader {
|
||||
'key' => $this->parse_key($key),
|
||||
'value' => $value
|
||||
);
|
||||
if ($usepersistcache) {
|
||||
if ($usestaticaccelerationarray) {
|
||||
$this->set_in_persist_cache($data[$key]['key'], $value);
|
||||
}
|
||||
}
|
||||
@@ -645,7 +663,7 @@ class cache implements cache_loader {
|
||||
public function has($key, $tryloadifpossible = false) {
|
||||
$parsedkey = $this->parse_key($key);
|
||||
if ($this->is_in_persist_cache($parsedkey)) {
|
||||
// Hoorah, that was easy. It exists in the persist cache so we definitely have it.
|
||||
// Hoorah, that was easy. It exists in the static acceleration array so we definitely have it.
|
||||
return true;
|
||||
}
|
||||
if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
|
||||
@@ -787,8 +805,12 @@ class cache implements cache_loader {
|
||||
* @return bool True on success, false otherwise
|
||||
*/
|
||||
public function purge() {
|
||||
// 1. Purge the persist cache.
|
||||
$this->persistcache = array();
|
||||
// 1. Purge the static acceleration array.
|
||||
$this->staticaccelerationarray = array();
|
||||
if ($this->staticaccelerationsize !== false) {
|
||||
$this->staticaccelerationkeys = array();
|
||||
$this->staticaccelerationcount = 0;
|
||||
}
|
||||
// 2. Purge the store.
|
||||
$this->store->purge();
|
||||
// 3. Optionally pruge any stacked loaders.
|
||||
@@ -856,7 +878,7 @@ class cache implements cache_loader {
|
||||
* Returns the loader associated with this instance.
|
||||
*
|
||||
* @since 2.4.4
|
||||
* @return cache_loader|false
|
||||
* @return cache|false
|
||||
*/
|
||||
protected function get_loader() {
|
||||
return $this->loader;
|
||||
@@ -897,49 +919,56 @@ class cache implements cache_loader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this cache is making use of the persist cache.
|
||||
* Returns true if this cache is making use of the static acceleration array.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_using_persist_cache() {
|
||||
return $this->persist;
|
||||
return $this->staticacceleration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the requested key exists within the persist cache.
|
||||
* Returns true if the requested key exists within the static acceleration array.
|
||||
*
|
||||
* @param string $key The parsed key
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_in_persist_cache($key) {
|
||||
if (is_array($key)) {
|
||||
// This method of checking if an array was supplied is faster than is_array.
|
||||
if ($key === (array)$key) {
|
||||
$key = $key['key'];
|
||||
}
|
||||
// This could be written as a single line, however it has been split because the ttl check is faster than the instanceof
|
||||
// and has_expired calls.
|
||||
if (!$this->persist || !array_key_exists($key, $this->persistcache)) {
|
||||
if (!$this->staticacceleration || !array_key_exists($key, $this->staticaccelerationarray)) {
|
||||
return false;
|
||||
}
|
||||
if ($this->has_a_ttl() && $this->store_supports_native_ttl()) {
|
||||
return !($this->persistcache[$key] instanceof cache_ttl_wrapper && $this->persistcache[$key]->has_expired());
|
||||
return !($this->staticaccelerationarray[$key] instanceof cache_ttl_wrapper &&
|
||||
$this->staticaccelerationarray[$key]->has_expired());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the item from the persist cache if it exists there.
|
||||
* Returns the item from the static acceleration array if it exists there.
|
||||
*
|
||||
* @param string $key The parsed key
|
||||
* @return mixed|false The data from the persist cache or false if it wasn't there.
|
||||
* @return mixed|false The data from the static acceleration array or false if it wasn't there.
|
||||
*/
|
||||
protected function get_from_persist_cache($key) {
|
||||
if (is_array($key)) {
|
||||
// This method of checking if an array was supplied is faster than is_array.
|
||||
if ($key === (array)$key) {
|
||||
$key = $key['key'];
|
||||
}
|
||||
if (!$this->persist || !array_key_exists($key, $this->persistcache)) {
|
||||
// This isset check is faster than array_key_exists but will return false
|
||||
// for null values, meaning null values will come from backing store not
|
||||
// the static acceleration array. We think this okay because null usage should be
|
||||
// very rare (see comment in MDL-39472).
|
||||
if (!$this->staticacceleration || !isset($this->staticaccelerationarray[$key])) {
|
||||
$result = false;
|
||||
} else {
|
||||
$data = $this->persistcache[$key];
|
||||
$data = $this->staticaccelerationarray[$key];
|
||||
if (!$this->has_a_ttl() || !$data instanceof cache_ttl_wrapper) {
|
||||
if ($data instanceof cache_cached_object) {
|
||||
$data = $data->restore_object();
|
||||
@@ -957,53 +986,68 @@ class cache implements cache_loader {
|
||||
}
|
||||
if ($result) {
|
||||
if ($this->perfdebug) {
|
||||
cache_helper::record_cache_hit('** static persist **', $this->definition->get_id());
|
||||
cache_helper::record_cache_hit('** static acceleration **', $this->definition->get_id());
|
||||
}
|
||||
if ($this->staticaccelerationsize > 1 && $this->staticaccelerationcount > 1) {
|
||||
// Check to see if this is the last item on the static acceleration keys array.
|
||||
if (end($this->staticaccelerationkeys) !== $key) {
|
||||
// It isn't the last item.
|
||||
// Move the item to the end of the array so that it is last to be removed.
|
||||
unset($this->staticaccelerationkeys[$key]);
|
||||
$this->staticaccelerationkeys[$key] = $key;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
} else {
|
||||
if ($this->perfdebug) {
|
||||
cache_helper::record_cache_miss('** static persist **', $this->definition->get_id());
|
||||
cache_helper::record_cache_miss('** static acceleration **', $this->definition->get_id());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a key value pair into the persist cache.
|
||||
* Sets a key value pair into the static acceleration array.
|
||||
*
|
||||
* @param string $key The parsed key
|
||||
* @param mixed $data
|
||||
* @return bool
|
||||
*/
|
||||
protected function set_in_persist_cache($key, $data) {
|
||||
if (is_array($key)) {
|
||||
// This method of checking if an array was supplied is faster than is_array.
|
||||
if ($key === (array)$key) {
|
||||
$key = $key['key'];
|
||||
}
|
||||
$this->persistcache[$key] = $data;
|
||||
if ($this->persistmaxsize !== false) {
|
||||
$this->persistcount++;
|
||||
if ($this->persistcount > $this->persistmaxsize) {
|
||||
$dropkey = array_shift($this->persistkeys);
|
||||
unset($this->persistcache[$dropkey]);
|
||||
$this->persistcount--;
|
||||
if ($this->staticaccelerationsize !== false && isset($this->staticaccelerationkeys[$key])) {
|
||||
$this->staticaccelerationcount--;
|
||||
unset($this->staticaccelerationkeys[$key]);
|
||||
}
|
||||
$this->staticaccelerationarray[$key] = $data;
|
||||
if ($this->staticaccelerationsize !== false) {
|
||||
$this->staticaccelerationcount++;
|
||||
$this->staticaccelerationkeys[$key] = $key;
|
||||
if ($this->staticaccelerationcount > $this->staticaccelerationsize) {
|
||||
$dropkey = array_shift($this->staticaccelerationkeys);
|
||||
unset($this->staticaccelerationarray[$dropkey]);
|
||||
$this->staticaccelerationcount--;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an item from the persist cache.
|
||||
* Deletes an item from the static acceleration array.
|
||||
*
|
||||
* @param string|int $key As given to get|set|delete
|
||||
* @return bool True on success, false otherwise.
|
||||
*/
|
||||
protected function delete_from_persist_cache($key) {
|
||||
unset($this->persistcache[$key]);
|
||||
if ($this->persistmaxsize !== false) {
|
||||
$dropkey = array_search($key, $this->persistkeys);
|
||||
unset($this->staticaccelerationarray[$key]);
|
||||
if ($this->staticaccelerationsize !== false) {
|
||||
$dropkey = array_search($key, $this->staticaccelerationkeys);
|
||||
if ($dropkey) {
|
||||
unset($this->persistkeys[$dropkey]);
|
||||
$this->persistcount--;
|
||||
unset($this->staticaccelerationkeys[$dropkey]);
|
||||
$this->staticaccelerationcount--;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -1321,7 +1365,6 @@ class cache_application extends cache implements cache_loader_with_locking {
|
||||
* @param string|int $key The key for the data being requested.
|
||||
* @param int $strictness One of IGNORE_MISSING | MUST_EXIST
|
||||
* @return mixed|false The data from the cache or false if the key did not exist within the cache.
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public function get($key, $strictness = IGNORE_MISSING) {
|
||||
if ($this->requirelockingread && $this->check_lock_state($key) === false) {
|
||||
@@ -1345,7 +1388,7 @@ class cache_application extends cache implements cache_loader_with_locking {
|
||||
* @return array An array of key value pairs for the items that could be retrieved from the cache.
|
||||
* If MUST_EXIST was used and not all keys existed within the cache then an exception will be thrown.
|
||||
* Otherwise any key that did not exist will have a data value of false within the results.
|
||||
* @throws moodle_exception
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function get_many(array $keys, $strictness = IGNORE_MISSING) {
|
||||
if ($this->requirelockingread) {
|
||||
@@ -1421,9 +1464,8 @@ class cache_application extends cache implements cache_loader_with_locking {
|
||||
* This class is used for session caches returned by the cache::make methods.
|
||||
*
|
||||
* It differs from the application loader in a couple of noteable ways:
|
||||
* 1. Sessions are always expected to be persistent.
|
||||
* Because of this we don't ever use the persist cache and instead a session array
|
||||
* containing all of the data is maintained by this object.
|
||||
* 1. Sessions are always expected to exist.
|
||||
* Because of this we don't ever use the static acceleration array.
|
||||
* 2. Session data for a loader instance (store + definition) is consolidate into a
|
||||
* single array for storage within the store.
|
||||
* Along with this we embed a lastaccessed time with the data. This way we can
|
||||
@@ -1439,6 +1481,7 @@ class cache_application extends cache implements cache_loader_with_locking {
|
||||
* @todo we should support locking in the session as well. Should be pretty simple to set up.
|
||||
*
|
||||
* @internal don't use me directly.
|
||||
* @method cache_store|cache_is_searchable get_store() Returns the cache store which must implement both cache_is_searchable.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
@@ -1475,6 +1518,11 @@ class cache_session extends cache {
|
||||
*/
|
||||
const KEY_PREFIX = 'sess_';
|
||||
|
||||
/**
|
||||
* This is the key used to track last access.
|
||||
*/
|
||||
const LASTACCESS = '__lastaccess__';
|
||||
|
||||
/**
|
||||
* Override the cache::construct method.
|
||||
*
|
||||
@@ -1488,12 +1536,15 @@ class cache_session extends cache {
|
||||
* @param cache_definition $definition
|
||||
* @param cache_store $store
|
||||
* @param cache_loader|cache_data_source $loader
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(cache_definition $definition, cache_store $store, $loader = null) {
|
||||
// First up copy the loadeduserid to the current user id.
|
||||
$this->currentuserid = self::$loadeduserid;
|
||||
parent::__construct($definition, $store, $loader);
|
||||
|
||||
// This will trigger check tracked user. If this gets removed a call to that will need to be added here in its place.
|
||||
$this->set(self::LASTACCESS, cache::now());
|
||||
|
||||
if ($definition->has_invalidation_events()) {
|
||||
$lastinvalidation = $this->get('lastsessioninvalidation');
|
||||
if ($lastinvalidation === false) {
|
||||
@@ -1542,6 +1593,21 @@ class cache_session extends cache {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session id for the loader.
|
||||
*/
|
||||
protected function set_session_id() {
|
||||
$this->sessionid = preg_replace('#[^a-zA-Z0-9_]#', '_', session_id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the prefix used for all keys.
|
||||
* @return string
|
||||
*/
|
||||
protected function get_key_prefix() {
|
||||
return 'u'.$this->currentuserid.'_'.$this->sessionid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the key turning it into a string (or array is required) suitable to be passed to the cache store.
|
||||
*
|
||||
@@ -1554,17 +1620,18 @@ class cache_session extends cache {
|
||||
* @return string|array String unless the store supports multi-identifiers in which case an array if returned.
|
||||
*/
|
||||
protected function parse_key($key) {
|
||||
if ($key === 'lastaccess') {
|
||||
$key = '__lastaccess__';
|
||||
$prefix = $this->get_key_prefix();
|
||||
if ($key === self::LASTACCESS) {
|
||||
return $key.$prefix;
|
||||
}
|
||||
return 'sess_'.parent::parse_key($key);
|
||||
return $prefix.'_'.parent::parse_key($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that this cache instance is tracking the current user.
|
||||
*/
|
||||
protected function check_tracked_user() {
|
||||
if (isset($_SESSION['USER']->id)) {
|
||||
if (isset($_SESSION['USER']->id) && $_SESSION['USER']->id !== null) {
|
||||
// Get the id of the current user.
|
||||
$new = $_SESSION['USER']->id;
|
||||
} else {
|
||||
@@ -1578,55 +1645,25 @@ class cache_session extends cache {
|
||||
// This way we don't bloat the session.
|
||||
$this->purge();
|
||||
// Update the session id just in case!
|
||||
$this->sessionid = session_id();
|
||||
$this->set_session_id();
|
||||
}
|
||||
self::$loadeduserid = $new;
|
||||
$this->currentuserid = $new;
|
||||
} else if ($new !== $this->currentuserid) {
|
||||
// The current user matches the loaded user but not the user last used by this cache.
|
||||
$this->purge();
|
||||
$this->purge_current_user();
|
||||
$this->currentuserid = $new;
|
||||
// Update the session id just in case!
|
||||
$this->sessionid = session_id();
|
||||
$this->set_session_id();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the session data.
|
||||
*
|
||||
* @param bool $force If true the session data will be loaded from the store again.
|
||||
* @return array An array of session data.
|
||||
* Purges the session cache of all data belonging to the current user.
|
||||
*/
|
||||
protected function get_session_data($force = false) {
|
||||
if ($this->sessionid === null) {
|
||||
$this->sessionid = session_id();
|
||||
}
|
||||
if (is_array($this->session) && !$force) {
|
||||
return $this->session;
|
||||
}
|
||||
$session = parent::get($this->sessionid);
|
||||
if ($session === false) {
|
||||
$session = array();
|
||||
}
|
||||
// We have to write here to ensure that the lastaccess time is recorded.
|
||||
// And also in order to ensure the session entry exists as when we save it on __destruct
|
||||
// $CFG is likely to have already been destroyed.
|
||||
$this->save_session($session);
|
||||
return $this->session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the session data.
|
||||
*
|
||||
* This function also updates the last access time.
|
||||
*
|
||||
* @param array $session
|
||||
* @return bool
|
||||
*/
|
||||
protected function save_session(array $session) {
|
||||
$session['lastaccess'] = time();
|
||||
$this->session = $session;
|
||||
return parent::set($this->sessionid, $this->session);
|
||||
public function purge_current_user() {
|
||||
$keys = $this->get_store()->find_all($this->get_key_prefix());
|
||||
$this->get_store()->delete_many($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1637,7 +1674,7 @@ class cache_session extends cache {
|
||||
* In advanced cases an array may be useful such as in situations requiring the multi-key functionality.
|
||||
* @param int $strictness One of IGNORE_MISSING | MUST_EXIST
|
||||
* @return mixed|false The data from the cache or false if the key did not exist within the cache.
|
||||
* @throws moodle_exception
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function get($key, $strictness = IGNORE_MISSING) {
|
||||
// Check the tracked user.
|
||||
@@ -1645,10 +1682,8 @@ class cache_session extends cache {
|
||||
// 2. Parse the key.
|
||||
$parsedkey = $this->parse_key($key);
|
||||
// 3. Get it from the store.
|
||||
$result = false;
|
||||
$session = $this->get_session_data();
|
||||
if (array_key_exists($parsedkey, $session)) {
|
||||
$result = $session[$parsedkey];
|
||||
$result = $this->get_store()->get($parsedkey);
|
||||
if ($result !== false) {
|
||||
if ($result instanceof cache_ttl_wrapper) {
|
||||
if ($result->has_expired()) {
|
||||
$this->get_store()->delete($parsedkey);
|
||||
@@ -1662,10 +1697,9 @@ class cache_session extends cache {
|
||||
}
|
||||
}
|
||||
// 4. Load if from the loader/datasource if we don't already have it.
|
||||
$setaftervalidation = false;
|
||||
if ($result === false) {
|
||||
if ($this->perfdebug) {
|
||||
cache_helper::record_cache_miss('**static session**', $this->get_definition()->get_id());
|
||||
cache_helper::record_cache_miss($this->storetype, $this->get_definition()->get_id());
|
||||
}
|
||||
if ($this->get_loader() !== false) {
|
||||
// We must pass the original (unparsed) key to the next loader in the chain.
|
||||
@@ -1675,19 +1709,18 @@ class cache_session extends cache {
|
||||
} else if ($this->get_datasource() !== false) {
|
||||
$result = $this->get_datasource()->load_for_cache($key);
|
||||
}
|
||||
$setaftervalidation = ($result !== false);
|
||||
// 5. Set it to the store if we got it from the loader/datasource.
|
||||
if ($result !== false) {
|
||||
$this->set($key, $result);
|
||||
}
|
||||
} else if ($this->perfdebug) {
|
||||
cache_helper::record_cache_hit('**static session**', $this->get_definition()->get_id());
|
||||
cache_helper::record_cache_hit($this->storetype, $this->get_definition()->get_id());
|
||||
}
|
||||
// 5. Validate strictness.
|
||||
if ($strictness === MUST_EXIST && $result === false) {
|
||||
throw new moodle_exception('Requested key did not exist in any cache stores and could not be loaded.');
|
||||
throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
|
||||
}
|
||||
// 6. Set it to the store if we got it from the loader/datasource.
|
||||
if ($setaftervalidation) {
|
||||
$this->set($key, $result);
|
||||
}
|
||||
// 7. Make sure we don't pass back anything that could be a reference.
|
||||
// 6. Make sure we don't pass back anything that could be a reference.
|
||||
// We don't want people modifying the data in the cache.
|
||||
if (!is_scalar($result)) {
|
||||
// If data is an object it will be a reference.
|
||||
@@ -1718,8 +1751,14 @@ class cache_session extends cache {
|
||||
*/
|
||||
public function set($key, $data) {
|
||||
$this->check_tracked_user();
|
||||
$loader = $this->get_loader();
|
||||
if ($loader !== false) {
|
||||
// We have a loader available set it there as well.
|
||||
// We have to let the loader do its own parsing of data as it may be unique.
|
||||
$loader->set($key, $data);
|
||||
}
|
||||
if ($this->perfdebug) {
|
||||
cache_helper::record_cache_set('**static session**', $this->get_definition()->get_id());
|
||||
cache_helper::record_cache_set($this->storetype, $this->get_definition()->get_id());
|
||||
}
|
||||
if (is_object($data) && $data instanceof cacheable_object) {
|
||||
$data = new cache_cached_object($data);
|
||||
@@ -1731,12 +1770,10 @@ class cache_session extends cache {
|
||||
$data = $this->unref($data);
|
||||
}
|
||||
// We dont' support native TTL here as we consolidate data for sessions.
|
||||
if ($this->has_a_ttl()) {
|
||||
if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
|
||||
$data = new cache_ttl_wrapper($data, $this->get_definition()->get_ttl());
|
||||
}
|
||||
$session = $this->get_session_data();
|
||||
$session[$this->parse_key($key)] = $data;
|
||||
return $this->save_session($session);
|
||||
return $this->get_store()->set($this->parse_key($key), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1748,15 +1785,12 @@ class cache_session extends cache {
|
||||
* @return bool True of success, false otherwise.
|
||||
*/
|
||||
public function delete($key, $recurse = true) {
|
||||
$this->check_tracked_user();
|
||||
$parsedkey = $this->parse_key($key);
|
||||
if ($recurse && $this->get_loader() !== false) {
|
||||
// Delete from the bottom of the stack first.
|
||||
$this->get_loader()->delete($key, $recurse);
|
||||
}
|
||||
$session = $this->get_session_data();
|
||||
unset($session[$parsedkey]);
|
||||
return $this->save_session($session);
|
||||
return $this->get_store()->delete($parsedkey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1775,15 +1809,72 @@ class cache_session extends cache {
|
||||
* @return array An array of key value pairs for the items that could be retrieved from the cache.
|
||||
* If MUST_EXIST was used and not all keys existed within the cache then an exception will be thrown.
|
||||
* Otherwise any key that did not exist will have a data value of false within the results.
|
||||
* @throws moodle_exception
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function get_many(array $keys, $strictness = IGNORE_MISSING) {
|
||||
$this->check_tracked_user();
|
||||
$return = array();
|
||||
$parsedkeys = array();
|
||||
$keymap = array();
|
||||
foreach ($keys as $key) {
|
||||
$return[$key] = $this->get($key, $strictness);
|
||||
$parsedkey = $this->parse_key($key);
|
||||
$parsedkeys[$key] = $parsedkey;
|
||||
$keymap[$parsedkey] = $key;
|
||||
}
|
||||
$result = $this->get_store()->get_many($parsedkeys);
|
||||
$return = array();
|
||||
$missingkeys = array();
|
||||
$hasmissingkeys = false;
|
||||
foreach ($result as $parsedkey => $value) {
|
||||
$key = $keymap[$parsedkey];
|
||||
if ($value instanceof cache_ttl_wrapper) {
|
||||
/* @var cache_ttl_wrapper $value */
|
||||
if ($value->has_expired()) {
|
||||
$this->delete($keymap[$parsedkey]);
|
||||
$value = false;
|
||||
} else {
|
||||
$value = $value->data;
|
||||
}
|
||||
}
|
||||
if ($value instanceof cache_cached_object) {
|
||||
/* @var cache_cached_object $value */
|
||||
$value = $value->restore_object();
|
||||
}
|
||||
$return[$key] = $value;
|
||||
if ($value === false) {
|
||||
$hasmissingkeys = true;
|
||||
$missingkeys[$parsedkey] = $key;
|
||||
}
|
||||
}
|
||||
if ($hasmissingkeys) {
|
||||
// We've got missing keys - we've got to check any loaders or data sources.
|
||||
$loader = $this->get_loader();
|
||||
$datasource = $this->get_datasource();
|
||||
if ($loader !== false) {
|
||||
foreach ($loader->get_many($missingkeys) as $key => $value) {
|
||||
if ($value !== false) {
|
||||
$return[$key] = $value;
|
||||
unset($missingkeys[$parsedkeys[$key]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$hasmissingkeys = count($missingkeys) > 0;
|
||||
if ($datasource !== false && $hasmissingkeys) {
|
||||
// We're still missing keys but we've got a datasource.
|
||||
foreach ($datasource->load_many_for_cache($missingkeys) as $key => $value) {
|
||||
if ($value !== false) {
|
||||
$return[$key] = $value;
|
||||
unset($missingkeys[$parsedkeys[$key]]);
|
||||
}
|
||||
}
|
||||
$hasmissingkeys = count($missingkeys) > 0;
|
||||
}
|
||||
}
|
||||
if ($hasmissingkeys && $strictness === MUST_EXIST) {
|
||||
throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
|
||||
}
|
||||
|
||||
return $return;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1795,18 +1886,12 @@ class cache_session extends cache {
|
||||
* @return int The number of items successfully deleted.
|
||||
*/
|
||||
public function delete_many(array $keys, $recurse = true) {
|
||||
$this->check_tracked_user();
|
||||
$parsedkeys = array_map(array($this, 'parse_key'), $keys);
|
||||
if ($recurse && $this->get_loader() !== false) {
|
||||
// Delete from the bottom of the stack first.
|
||||
$this->get_loader()->delete_many($keys, $recurse);
|
||||
}
|
||||
$session = $this->get_session_data();
|
||||
foreach ($parsedkeys as $parsedkey) {
|
||||
unset($session[$parsedkey]);
|
||||
}
|
||||
$this->save_session($session);
|
||||
return count($keys);
|
||||
return $this->get_store()->delete_many($parsedkeys);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1834,8 +1919,15 @@ class cache_session extends cache {
|
||||
*/
|
||||
public function set_many(array $keyvaluearray) {
|
||||
$this->check_tracked_user();
|
||||
$session = $this->get_session_data();
|
||||
$simulatettl = $this->has_a_ttl();
|
||||
$loader = $this->get_loader();
|
||||
if ($loader !== false) {
|
||||
// We have a loader available set it there as well.
|
||||
// We have to let the loader do its own parsing of data as it may be unique.
|
||||
$loader->set_many($keyvaluearray);
|
||||
}
|
||||
$data = array();
|
||||
$definitionid = $this->get_definition()->get_ttl();
|
||||
$simulatettl = $this->has_a_ttl() && !$this->store_supports_native_ttl();
|
||||
foreach ($keyvaluearray as $key => $value) {
|
||||
if (is_object($value) && $value instanceof cacheable_object) {
|
||||
$value = new cache_cached_object($value);
|
||||
@@ -1847,16 +1939,17 @@ class cache_session extends cache {
|
||||
$value = $this->unref($value);
|
||||
}
|
||||
if ($simulatettl) {
|
||||
$value = new cache_ttl_wrapper($value, $this->get_definition()->get_ttl());
|
||||
$value = new cache_ttl_wrapper($value, $definitionid);
|
||||
}
|
||||
$parsedkey = $this->parse_key($key);
|
||||
$session[$parsedkey] = $value;
|
||||
$data[$key] = array(
|
||||
'key' => $this->parse_key($key),
|
||||
'value' => $value
|
||||
);
|
||||
}
|
||||
if ($this->perfdebug) {
|
||||
cache_helper::record_cache_set($this->storetype, $this->get_definition()->get_id());
|
||||
cache_helper::record_cache_set($this->storetype, $definitionid);
|
||||
}
|
||||
$this->save_session($session);
|
||||
return count($keyvaluearray);
|
||||
return $this->get_store()->set_many($data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1865,13 +1958,9 @@ class cache_session extends cache {
|
||||
* @return bool True on success, false otherwise
|
||||
*/
|
||||
public function purge() {
|
||||
// 1. Purge the session object.
|
||||
$this->session = array();
|
||||
// 2. Delete the record for this users session from the store.
|
||||
$this->get_store()->delete($this->sessionid);
|
||||
// 3. Optionally purge any stacked loaders in the same way.
|
||||
$this->get_store()->purge();
|
||||
if ($this->get_loader()) {
|
||||
$this->get_loader()->delete($this->sessionid);
|
||||
$this->get_loader()->purge();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1900,21 +1989,27 @@ class cache_session extends cache {
|
||||
public function has($key, $tryloadifpossible = false) {
|
||||
$this->check_tracked_user();
|
||||
$parsedkey = $this->parse_key($key);
|
||||
$session = $this->get_session_data();
|
||||
$has = false;
|
||||
if ($this->has_a_ttl()) {
|
||||
$store = $this->get_store();
|
||||
if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
|
||||
// The data has a TTL and the store doesn't support it natively.
|
||||
// We must fetch the data and expect a ttl wrapper.
|
||||
if (array_key_exists($parsedkey, $session)) {
|
||||
$data = $session[$parsedkey];
|
||||
$has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
|
||||
}
|
||||
$data = $store->get($parsedkey);
|
||||
$has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
|
||||
} else if (!$this->store_supports_key_awareness()) {
|
||||
// The store doesn't support key awareness, get the data and check it manually... puke.
|
||||
// Either no TTL is set of the store supports its handling natively.
|
||||
$data = $store->get($parsedkey);
|
||||
$has = ($data !== false);
|
||||
} else {
|
||||
$has = array_key_exists($parsedkey, $session);
|
||||
// The store supports key awareness, this is easy!
|
||||
// Either no TTL is set of the store supports its handling natively.
|
||||
/* @var cache_store|cache_is_key_aware $store */
|
||||
$has = $store->has($parsedkey);
|
||||
}
|
||||
if (!$has && $tryloadifpossible) {
|
||||
$result = null;
|
||||
if ($this->get_loader() !== false) {
|
||||
$result = $this->get_loader()->get($key);
|
||||
$result = $this->get_loader()->get($parsedkey);
|
||||
} else if ($this->get_datasource() !== null) {
|
||||
$result = $this->get_datasource()->load_for_cache($key);
|
||||
}
|
||||
@@ -1941,25 +2036,18 @@ class cache_session extends cache {
|
||||
*/
|
||||
public function has_all(array $keys) {
|
||||
$this->check_tracked_user();
|
||||
$session = $this->get_session_data();
|
||||
foreach ($keys as $key) {
|
||||
$has = false;
|
||||
$parsedkey = $this->parse_key($key);
|
||||
if ($this->has_a_ttl()) {
|
||||
// The data has a TTL and the store doesn't support it natively.
|
||||
// We must fetch the data and expect a ttl wrapper.
|
||||
if (array_key_exists($parsedkey, $session)) {
|
||||
$data = $session[$parsedkey];
|
||||
$has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
|
||||
if (($this->has_a_ttl() && !$this->store_supports_native_ttl()) || !$this->store_supports_key_awareness()) {
|
||||
foreach ($keys as $key) {
|
||||
if (!$this->has($key)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$has = array_key_exists($parsedkey, $session);
|
||||
}
|
||||
if (!$has) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
// The cache must be key aware and if support native ttl if it a ttl is set.
|
||||
/* @var cache_store|cache_is_key_aware $store */
|
||||
$store = $this->get_store();
|
||||
return $store->has_all(array_map(array($this, 'parse_key'), $keys));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1976,30 +2064,21 @@ class cache_session extends cache {
|
||||
* @return bool True if the cache has at least one of the given keys
|
||||
*/
|
||||
public function has_any(array $keys) {
|
||||
$this->check_tracked_user();
|
||||
$session = $this->get_session_data();
|
||||
foreach ($keys as $key) {
|
||||
$has = false;
|
||||
$parsedkey = $this->parse_key($key);
|
||||
if ($this->has_a_ttl()) {
|
||||
// The data has a TTL and the store doesn't support it natively.
|
||||
// We must fetch the data and expect a ttl wrapper.
|
||||
if (array_key_exists($parsedkey, $session)) {
|
||||
$data = $session[$parsedkey];
|
||||
$has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
|
||||
if (($this->has_a_ttl() && !$this->store_supports_native_ttl()) || !$this->store_supports_key_awareness()) {
|
||||
foreach ($keys as $key) {
|
||||
if ($this->has($key)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
$has = array_key_exists($parsedkey, $session);
|
||||
}
|
||||
if ($has) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
/* @var cache_store|cache_is_key_aware $store */
|
||||
$store = $this->get_store();
|
||||
return $store->has_any(array_map(array($this, 'parse_key'), $keys));
|
||||
}
|
||||
|
||||
/**
|
||||
* The session loader never uses the persist cache.
|
||||
* The session loader never uses static acceleration.
|
||||
* Instead it stores things in the static $session variable. Shared between all session loaders.
|
||||
*
|
||||
* @return bool
|
||||
|
||||
Vendored
+3
-3
@@ -49,8 +49,7 @@ class cache_disabled extends cache {
|
||||
* @param null $loader Unused.
|
||||
*/
|
||||
public function __construct(cache_definition $definition, cache_store $store, $loader = null) {
|
||||
$this->definition = $definition;
|
||||
$this->store = $store;
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,7 +227,8 @@ class cache_factory_disabled extends cache_factory {
|
||||
* @param array $options An array of options, available options are:
|
||||
* - simplekeys : Set to true if the keys you will use are a-zA-Z0-9_
|
||||
* - simpledata : Set to true if the type of the data you are going to store is scalar, or an array of scalar vars
|
||||
* - persistent : If set to true the cache will persist construction requests.
|
||||
* - staticacceleration : If set to true the cache will hold onto all data passing through it.
|
||||
* - staticaccelerationsize : Sets the max size of the static acceleration array.
|
||||
* @return cache_application|cache_session|cache_request
|
||||
*/
|
||||
public function create_cache_from_params($mode, $component, $area, array $identifiers = array(), array $options = array()) {
|
||||
|
||||
-1
@@ -27,7 +27,6 @@
|
||||
*/
|
||||
|
||||
require_once($CFG->dirroot.'/cache/forms.php');
|
||||
require_once($CFG->dirroot.'/cache/stores/file/lib.php');
|
||||
|
||||
/**
|
||||
* Form for adding a file instance.
|
||||
|
||||
Vendored
+46
-14
@@ -101,6 +101,19 @@ class cachestore_file extends cache_store implements cache_is_key_aware, cache_i
|
||||
*/
|
||||
protected $definition;
|
||||
|
||||
/**
|
||||
* A reference to the global $CFG object.
|
||||
*
|
||||
* You may be asking yourself why on earth this is here, but there is a good reason.
|
||||
* By holding onto a reference of the $CFG object we can be absolutely sure that it won't be destroyed before
|
||||
* we are done with it.
|
||||
* This makes it possible to use a cache within a destructor method for the purposes of
|
||||
* delayed writes. Like how the session mechanisms work.
|
||||
*
|
||||
* @var stdClass
|
||||
*/
|
||||
private $cfg = null;
|
||||
|
||||
/**
|
||||
* Constructs the store instance.
|
||||
*
|
||||
@@ -111,6 +124,13 @@ class cachestore_file extends cache_store implements cache_is_key_aware, cache_i
|
||||
* @param array $configuration
|
||||
*/
|
||||
public function __construct($name, array $configuration = array()) {
|
||||
global $CFG;
|
||||
|
||||
if (isset($CFG)) {
|
||||
// Hold onto a reference of the global $CFG object.
|
||||
$this->cfg = $CFG;
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
if (array_key_exists('path', $configuration) && $configuration['path'] !== '') {
|
||||
$this->custompath = true;
|
||||
@@ -246,7 +266,7 @@ class cachestore_file extends cache_store implements cache_is_key_aware, cache_i
|
||||
$this->definition = $definition;
|
||||
$hash = preg_replace('#[^a-zA-Z0-9]+#', '_', $this->definition->get_id());
|
||||
$this->path = $this->filestorepath.'/'.$hash;
|
||||
make_writable_directory($this->path);
|
||||
make_writable_directory($this->path, false);
|
||||
if ($this->prescan && $definition->get_mode() !== self::MODE_REQUEST) {
|
||||
$this->prescan = false;
|
||||
}
|
||||
@@ -294,11 +314,13 @@ class cachestore_file extends cache_store implements cache_is_key_aware, cache_i
|
||||
return $this->path . '/' . $key . '.cache';
|
||||
} else {
|
||||
// We are using a single subdirectory to achieve 1 level.
|
||||
$subdir = substr($key, 0, 3);
|
||||
// We suffix the subdir so it does not clash with any windows
|
||||
// reserved filenames like 'con'.
|
||||
$subdir = substr($key, 0, 3) . '-cache';
|
||||
$dir = $this->path . '/' . $subdir;
|
||||
if ($create) {
|
||||
// Create the directory. This function does it recursivily!
|
||||
make_writable_directory($dir);
|
||||
make_writable_directory($dir, false);
|
||||
}
|
||||
return $dir . '/' . $key . '.cache';
|
||||
}
|
||||
@@ -330,23 +352,19 @@ class cachestore_file extends cache_store implements cache_is_key_aware, cache_i
|
||||
if (!$readfile) {
|
||||
return false;
|
||||
}
|
||||
// Check the filesize first, likely not needed but important none the less.
|
||||
$filesize = filesize($file);
|
||||
if (!$filesize) {
|
||||
return false;
|
||||
}
|
||||
// Open ensuring the file for writing, truncating it and setting the pointer to the start.
|
||||
// Open ensuring the file for reading in binary format.
|
||||
if (!$handle = fopen($file, 'rb')) {
|
||||
return false;
|
||||
}
|
||||
// Lock it up!
|
||||
// We don't care if this succeeds or not, on some systems it will, on some it won't, meah either way.
|
||||
flock($handle, LOCK_SH);
|
||||
// HACK ALERT
|
||||
// There is a problem when reading from the file during PHPUNIT tests. For one reason or another the filesize is not correct
|
||||
// Doesn't happen during normal operation, just during unit tests.
|
||||
// Read it.
|
||||
$data = fread($handle, $filesize+128);
|
||||
$data = '';
|
||||
// Read the data in 1Mb chunks. Small caches will not loop more than once. We don't use filesize as it may
|
||||
// be cached with a different value than what we need to read from the file.
|
||||
do {
|
||||
$data .= fread($handle, 1048576);
|
||||
} while (!feof($handle));
|
||||
// Unlock it.
|
||||
flock($handle, LOCK_UN);
|
||||
// Return it unserialised.
|
||||
@@ -605,13 +623,27 @@ class cachestore_file extends cache_store implements cache_is_key_aware, cache_i
|
||||
* @throws coding_exception
|
||||
*/
|
||||
protected function ensure_path_exists() {
|
||||
global $CFG;
|
||||
if (!is_writable($this->path)) {
|
||||
if ($this->custompath && !$this->autocreate) {
|
||||
throw new coding_exception('File store path does not exist. It must exist and be writable by the web server.');
|
||||
}
|
||||
$createdcfg = false;
|
||||
if (!isset($CFG)) {
|
||||
// This can only happen during destruction of objects.
|
||||
// A cache is being used within a destructor, php is ending a request and $CFG has
|
||||
// already being cleaned up.
|
||||
// Rebuild $CFG with directory permissions just to complete this write.
|
||||
$CFG = $this->cfg;
|
||||
$createdcfg = true;
|
||||
}
|
||||
if (!make_writable_directory($this->path, false)) {
|
||||
throw new coding_exception('File store path does not exist and can not be created.');
|
||||
}
|
||||
if ($createdcfg) {
|
||||
// We re-created it so we'll clean it up.
|
||||
unset($CFG);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+2
-2
@@ -27,7 +27,6 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot.'/cache/forms.php');
|
||||
require_once($CFG->dirroot.'/cache/stores/memcached/lib.php');
|
||||
|
||||
/**
|
||||
* Form for adding a memcache instance.
|
||||
@@ -50,7 +49,8 @@ class cachestore_memcache_addinstance_form extends cachestore_addinstance_form {
|
||||
$form->addElement('text', 'prefix', get_string('prefix', 'cachestore_memcache'),
|
||||
array('maxlength' => 5, 'size' => 5));
|
||||
$form->addHelpButton('prefix', 'prefix', 'cachestore_memcache');
|
||||
$form->setType('prefix', PARAM_ALPHAEXT);
|
||||
$form->setType('prefix', PARAM_TEXT); // We set to text but we have a rule to limit to alphanumext.
|
||||
$form->setDefault('prefix', 'mdl_');
|
||||
$form->addRule('prefix', get_string('prefixinvalid', 'cachestore_memcache'), 'regex', '#^[a-zA-Z0-9\-_]+$#');
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ $string['prefix'] = 'Key prefix';
|
||||
$string['prefix_help'] = 'This prefix is used for all key names on the memcache server.
|
||||
* If you only have one Moodle instance using this server, you can leave this value default.
|
||||
* Due to key length restrictions, a maximum of 5 characters is permitted.';
|
||||
$string['prefixinvalid'] = 'Invalid prefix. You can only use a-z A-Z 0-9-_.';
|
||||
$string['servers'] = 'Servers';
|
||||
$string['servers_help'] = 'This sets the servers that should be utilised by this memcache adapter.
|
||||
Servers should be defined one per line and consist of a server address and optionally a port and weight.
|
||||
|
||||
Vendored
+2
-2
@@ -132,8 +132,8 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
|
||||
$this->connection = new Memcache;
|
||||
foreach ($this->servers as $server) {
|
||||
$this->connection->addServer($server[0], $server[1], true, $server[2]);
|
||||
// Test the connection to this server.
|
||||
}
|
||||
// Test the connection to the pool of servers.
|
||||
$this->isready = @$this->connection->set($this->parse_key('ping'), 'ping', MEMCACHE_COMPRESSED, 1);
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
|
||||
* Generates an instance of the cache store that can be used for testing.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return false
|
||||
* @return cachestore_memcache|false
|
||||
*/
|
||||
public static function initialise_test_instance(cache_definition $definition) {
|
||||
if (!self::are_requirements_met()) {
|
||||
|
||||
+4
-3
@@ -57,11 +57,12 @@ class cachestore_memcached_addinstance_form extends cachestore_addinstance_form
|
||||
$form->addElement('select', 'serialiser', get_string('useserialiser', 'cachestore_memcached'), $serialiseroptions);
|
||||
$form->addHelpButton('serialiser', 'useserialiser', 'cachestore_memcached');
|
||||
$form->setDefault('serialiser', Memcached::SERIALIZER_PHP);
|
||||
$form->setType('serialiser', PARAM_NUMBER);
|
||||
$form->setType('serialiser', PARAM_INT);
|
||||
|
||||
$form->addElement('text', 'prefix', get_string('prefix', 'cachestore_memcached'), array('size' => 16));
|
||||
$form->setType('prefix', PARAM_ALPHANUM);
|
||||
$form->setType('prefix', PARAM_TEXT); // We set to text but we have a rule to limit to alphanumext.
|
||||
$form->addHelpButton('prefix', 'prefix', 'cachestore_memcached');
|
||||
$form->addRule('prefix', get_string('prefixinvalid', 'cachestore_memcached'), 'regex', '#^[a-zA-Z0-9\-_]+$#');
|
||||
|
||||
$hashoptions = cachestore_memcached::config_get_hash_options();
|
||||
$form->addElement('select', 'hash', get_string('hash', 'cachestore_memcached'), $hashoptions);
|
||||
@@ -74,4 +75,4 @@ class cachestore_memcached_addinstance_form extends cachestore_addinstance_form
|
||||
$form->setDefault('bufferwrites', 0);
|
||||
$form->setType('bufferwrites', PARAM_BOOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ $string['hash_murmur'] = 'Murmur';
|
||||
$string['pluginname'] = 'Memcached';
|
||||
$string['prefix'] = 'Prefix key';
|
||||
$string['prefix_help'] = 'This can be used to create a "domain" for your item keys allowing you to create multiple memcached stores on a single memcached installation. It cannot be longer than 16 characters in order to ensure key length issues are not encountered.';
|
||||
$string['prefixinvalid'] = 'Invalid prefix. You can only use a-z A-Z 0-9-_.';
|
||||
$string['serialiser_igbinary'] = 'The igbinary serializer.';
|
||||
$string['serialiser_json'] = 'The JSON serializer.';
|
||||
$string['serialiser_php'] = 'The default PHP serializer.';
|
||||
|
||||
Vendored
+3
-2
@@ -141,6 +141,7 @@ class cachestore_memcached extends cache_store implements cache_is_configurable
|
||||
}
|
||||
$this->connection->addServers($this->servers);
|
||||
}
|
||||
// Test the connection to the pool of servers.
|
||||
$this->isready = @$this->connection->set("ping", 'ping', 1);
|
||||
}
|
||||
|
||||
@@ -339,7 +340,7 @@ class cachestore_memcached extends cache_store implements cache_is_configurable
|
||||
$options[Memcached::SERIALIZER_JSON] = get_string('serialiser_json', 'cachestore_memcached');
|
||||
}
|
||||
if (Memcached::HAVE_IGBINARY) {
|
||||
$options[Memcached::SERIALIZER_IGBINARY] = get_string('serialiser_php', 'cachestore_memcached');
|
||||
$options[Memcached::SERIALIZER_IGBINARY] = get_string('serialiser_igbinary', 'cachestore_memcached');
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
@@ -444,7 +445,7 @@ class cachestore_memcached extends cache_store implements cache_is_configurable
|
||||
* Generates an instance of the cache store that can be used for testing.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return false
|
||||
* @return cachestore_memcached|false
|
||||
*/
|
||||
public static function initialise_test_instance(cache_definition $definition) {
|
||||
|
||||
|
||||
-1
@@ -35,7 +35,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
// Include the necessary evils.
|
||||
require_once($CFG->dirroot.'/cache/forms.php');
|
||||
require_once($CFG->dirroot.'/cache/stores/mongodb/lib.php');
|
||||
|
||||
/**
|
||||
* The form to add an instance of the MongoDB store to the system.
|
||||
|
||||
Vendored
+85
-20
@@ -190,8 +190,9 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
|
||||
*/
|
||||
public function initialise(cache_definition $definition) {
|
||||
$this->storeid = $definition->generate_definition_hash();
|
||||
$this->store = &self::register_store_id($definition->get_id());
|
||||
$this->store = &self::register_store_id($this->name.'-'.$definition->get_id());
|
||||
$this->ttl = $definition->get_ttl();
|
||||
$this->check_ttl();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,12 +219,14 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
|
||||
* @return mixed The data that was associated with the key, or false if the key did not exist.
|
||||
*/
|
||||
public function get($key) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
if (array_key_exists($key, $this->store)) {
|
||||
if ($this->ttl == 0) {
|
||||
return $this->store[$key];
|
||||
} else if ($this->store[$key][1] >= $maxtime) {
|
||||
if (isset($this->store[$key])) {
|
||||
if ($this->ttl === 0) {
|
||||
return $this->store[$key][0];
|
||||
} else if ($this->store[$key][1] >= (cache::now() - $this->ttl)) {
|
||||
return $this->store[$key][0];
|
||||
} else {
|
||||
// Element is present but has expired.
|
||||
$this->check_ttl();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -240,17 +243,28 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
|
||||
*/
|
||||
public function get_many($keys) {
|
||||
$return = array();
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
$maxtime = 0;
|
||||
if ($this->ttl != 0) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
}
|
||||
|
||||
$hasexpiredelements = false;
|
||||
foreach ($keys as $key) {
|
||||
$return[$key] = false;
|
||||
if (array_key_exists($key, $this->store)) {
|
||||
if (isset($this->store[$key])) {
|
||||
if ($this->ttl == 0) {
|
||||
$return[$key] = $this->store[$key];
|
||||
$return[$key] = $this->store[$key][0];
|
||||
} else if ($this->store[$key][1] >= $maxtime) {
|
||||
$return[$key] = $this->store[$key][0];
|
||||
} else {
|
||||
$hasexpiredelements = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($hasexpiredelements) {
|
||||
// There are some elements that are present but have expired.
|
||||
$this->check_ttl();
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
@@ -262,8 +276,8 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
|
||||
* @return bool True if the operation was a success false otherwise.
|
||||
*/
|
||||
public function set($key, $data) {
|
||||
if ($this->ttl == 0) {
|
||||
$this->store[$key] = $data;
|
||||
if ($this->ttl === 0) {
|
||||
$this->store[$key] = array($data, 0);
|
||||
} else {
|
||||
$this->store[$key] = array($data, cache::now());
|
||||
}
|
||||
@@ -281,8 +295,14 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
|
||||
public function set_many(array $keyvaluearray) {
|
||||
$count = 0;
|
||||
foreach ($keyvaluearray as $pair) {
|
||||
$this->set($pair['key'], $pair['value']);
|
||||
$key = $pair['key'];
|
||||
$data = $pair['value'];
|
||||
$count++;
|
||||
if ($this->ttl === 0) {
|
||||
$this->store[$key] = array($data, 0);
|
||||
} else {
|
||||
$this->store[$key] = array($data, cache::now());
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
@@ -294,11 +314,10 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
if (array_key_exists($key, $this->store)) {
|
||||
if (isset($this->store[$key])) {
|
||||
if ($this->ttl == 0) {
|
||||
return true;
|
||||
} else if ($this->store[$key][1] >= $maxtime) {
|
||||
} else if ($this->store[$key][1] >= (cache::now() - $this->ttl)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -312,9 +331,13 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
|
||||
* @return bool
|
||||
*/
|
||||
public function has_all(array $keys) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
$maxtime = 0;
|
||||
if ($this->ttl != 0) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (!array_key_exists($key, $this->store)) {
|
||||
if (!isset($this->store[$key])) {
|
||||
return false;
|
||||
}
|
||||
if ($this->ttl != 0 && $this->store[$key][1] < $maxtime) {
|
||||
@@ -331,9 +354,13 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
|
||||
* @return bool
|
||||
*/
|
||||
public function has_any(array $keys) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
$maxtime = 0;
|
||||
if ($this->ttl != 0) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (array_key_exists($key, $this->store) && ($this->ttl == 0 || $this->store[$key][1] >= $maxtime)) {
|
||||
if (isset($this->store[$key]) && ($this->ttl == 0 || $this->store[$key][1] >= $maxtime)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -347,6 +374,9 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
|
||||
* @return bool Returns true if the operation was a success, false otherwise.
|
||||
*/
|
||||
public function delete($key) {
|
||||
if (!isset($this->store[$key])) {
|
||||
return false;
|
||||
}
|
||||
unset($this->store[$key]);
|
||||
return true;
|
||||
}
|
||||
@@ -358,6 +388,7 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
|
||||
* @return int The number of items successfully deleted.
|
||||
*/
|
||||
public function delete_many(array $keys) {
|
||||
// The number of items that have been successfully deleted.
|
||||
$count = 0;
|
||||
foreach ($keys as $key) {
|
||||
unset($this->store[$key]);
|
||||
@@ -413,12 +444,37 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes expired elements.
|
||||
* @return int number of removed elements
|
||||
*/
|
||||
protected function check_ttl() {
|
||||
if ($this->ttl === 0) {
|
||||
return 0;
|
||||
}
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
$count = 0;
|
||||
for ($value = reset($this->store); $value !== false; $value = next($this->store)) {
|
||||
if ($value[1] >= $maxtime) {
|
||||
// We know that elements are sorted by ttl so no need to continue.
|
||||
break;
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
if ($count) {
|
||||
// Remove first $count elements as they are expired.
|
||||
$this->store = array_slice($this->store, $count, null, true);
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all of the keys being stored in the cache store instance.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function find_all() {
|
||||
$this->check_ttl();
|
||||
return array_keys($this->store);
|
||||
}
|
||||
|
||||
@@ -426,6 +482,7 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
|
||||
* Finds all of the keys whose keys start with the given prefix.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @return array An array of keys.
|
||||
*/
|
||||
public function find_by_prefix($prefix) {
|
||||
$return = array();
|
||||
@@ -436,4 +493,12 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This store supports native TTL handling.
|
||||
* @return bool
|
||||
*/
|
||||
public function store_supports_native_ttl() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+50
-18
@@ -86,7 +86,7 @@ abstract class static_data_store extends cache_store {
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cachestore_static extends static_data_store implements cache_is_key_aware {
|
||||
class cachestore_static extends static_data_store implements cache_is_key_aware, cache_is_searchable {
|
||||
|
||||
/**
|
||||
* The name of the store
|
||||
@@ -133,7 +133,8 @@ class cachestore_static extends static_data_store implements cache_is_key_aware
|
||||
*/
|
||||
public static function get_supported_features(array $configuration = array()) {
|
||||
return self::SUPPORTS_DATA_GUARANTEE +
|
||||
self::SUPPORTS_NATIVE_TTL;
|
||||
self::SUPPORTS_NATIVE_TTL +
|
||||
self::IS_SEARCHABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,11 +214,10 @@ class cachestore_static extends static_data_store implements cache_is_key_aware
|
||||
* @return mixed The data that was associated with the key, or false if the key did not exist.
|
||||
*/
|
||||
public function get($key) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
if (array_key_exists($key, $this->store)) {
|
||||
if (isset($this->store[$key])) {
|
||||
if ($this->ttl == 0) {
|
||||
return $this->store[$key];
|
||||
} else if ($this->store[$key][1] >= $maxtime) {
|
||||
return $this->store[$key][0];
|
||||
} else if ($this->store[$key][1] >= (cache::now() - $this->ttl)) {
|
||||
return $this->store[$key][0];
|
||||
}
|
||||
}
|
||||
@@ -235,12 +235,15 @@ class cachestore_static extends static_data_store implements cache_is_key_aware
|
||||
*/
|
||||
public function get_many($keys) {
|
||||
$return = array();
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
if ($this->ttl != 0) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$return[$key] = false;
|
||||
if (array_key_exists($key, $this->store)) {
|
||||
if (isset($this->store[$key])) {
|
||||
if ($this->ttl == 0) {
|
||||
$return[$key] = $this->store[$key];
|
||||
$return[$key] = $this->store[$key][0];
|
||||
} else if ($this->store[$key][1] >= $maxtime) {
|
||||
$return[$key] = $this->store[$key][0];
|
||||
}
|
||||
@@ -258,7 +261,7 @@ class cachestore_static extends static_data_store implements cache_is_key_aware
|
||||
*/
|
||||
public function set($key, $data) {
|
||||
if ($this->ttl == 0) {
|
||||
$this->store[$key] = $data;
|
||||
$this->store[$key][0] = $data;
|
||||
} else {
|
||||
$this->store[$key] = array($data, cache::now());
|
||||
}
|
||||
@@ -289,11 +292,10 @@ class cachestore_static extends static_data_store implements cache_is_key_aware
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
if (array_key_exists($key, $this->store)) {
|
||||
if (isset($this->store[$key])) {
|
||||
if ($this->ttl == 0) {
|
||||
return true;
|
||||
} else if ($this->store[$key][1] >= $maxtime) {
|
||||
} else if ($this->store[$key][1] >= (cache::now() - $this->ttl)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -307,9 +309,12 @@ class cachestore_static extends static_data_store implements cache_is_key_aware
|
||||
* @return bool
|
||||
*/
|
||||
public function has_all(array $keys) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
if ($this->ttl != 0) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (!array_key_exists($key, $this->store)) {
|
||||
if (!isset($this->store[$key])) {
|
||||
return false;
|
||||
}
|
||||
if ($this->ttl != 0 && $this->store[$key][1] < $maxtime) {
|
||||
@@ -326,9 +331,12 @@ class cachestore_static extends static_data_store implements cache_is_key_aware
|
||||
* @return bool
|
||||
*/
|
||||
public function has_any(array $keys) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
if ($this->ttl != 0) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (array_key_exists($key, $this->store) && ($this->ttl == 0 || $this->store[$key][1] >= $maxtime)) {
|
||||
if (isset($this->store[$key]) && ($this->ttl == 0 || $this->store[$key][1] >= $maxtime)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -408,4 +416,28 @@ class cachestore_static extends static_data_store implements cache_is_key_aware
|
||||
public function my_name() {
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all of the keys being stored in the cache store instance.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function find_all() {
|
||||
return array_keys($this->store);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all of the keys whose keys start with the given prefix.
|
||||
*
|
||||
* @param string $prefix
|
||||
*/
|
||||
public function find_by_prefix($prefix) {
|
||||
$return = array();
|
||||
foreach ($this->find_all() as $key) {
|
||||
if (strpos($key, $prefix) === 0) {
|
||||
$return[] = $key;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+369
-50
@@ -119,6 +119,23 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for cache keys that would break on windows.
|
||||
*/
|
||||
public function test_windows_nasty_keys() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance->phpunit_add_definition('phpunit/windowskeytest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'windowskeytest',
|
||||
'simplekeys' => true,
|
||||
'simpledata' => true
|
||||
));
|
||||
$cache = cache::make('phpunit', 'windowskeytest');
|
||||
$this->assertTrue($cache->set('contest', 'test data 1'));
|
||||
$this->assertEquals('test data 1', $cache->get('contest'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the default application cache
|
||||
*/
|
||||
@@ -132,8 +149,8 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'test_default_application_cache',
|
||||
'persistent' => true,
|
||||
'persistentmaxsize' => 1
|
||||
'staticacceleration' => true,
|
||||
'staticaccelerationsize' => 1
|
||||
));
|
||||
$cache = cache::make('phpunit', 'test_default_application_cache');
|
||||
$this->assertInstanceOf('cache_application', $cache);
|
||||
@@ -172,7 +189,7 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'nostoretest2',
|
||||
'persistent' => true
|
||||
'staticacceleration' => true
|
||||
));
|
||||
$instance->phpunit_remove_stores();
|
||||
|
||||
@@ -191,42 +208,44 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
* @param cache_loader $cache
|
||||
*/
|
||||
protected function run_on_cache(cache_loader $cache) {
|
||||
$key = 'testkey';
|
||||
$datascalar = 'test data';
|
||||
$dataarray = array('test' => 'data', 'part' => 'two');
|
||||
$key = 'contestkey';
|
||||
$datascalars = array('test data', null);
|
||||
$dataarray = array('contest' => 'data', 'part' => 'two');
|
||||
$dataobject = (object)$dataarray;
|
||||
|
||||
$this->assertTrue($cache->purge());
|
||||
foreach ($datascalars as $datascalar) {
|
||||
$this->assertTrue($cache->purge());
|
||||
|
||||
// Check all read methods.
|
||||
$this->assertFalse($cache->get($key));
|
||||
$this->assertFalse($cache->has($key));
|
||||
$result = $cache->get_many(array($key));
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertFalse(reset($result));
|
||||
$this->assertFalse($cache->has_any(array($key)));
|
||||
$this->assertFalse($cache->has_all(array($key)));
|
||||
// Check all read methods.
|
||||
$this->assertFalse($cache->get($key));
|
||||
$this->assertFalse($cache->has($key));
|
||||
$result = $cache->get_many(array($key));
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertFalse(reset($result));
|
||||
$this->assertFalse($cache->has_any(array($key)));
|
||||
$this->assertFalse($cache->has_all(array($key)));
|
||||
|
||||
// Set the data.
|
||||
$this->assertTrue($cache->set($key, $datascalar));
|
||||
// Setting it more than once should be permitted.
|
||||
$this->assertTrue($cache->set($key, $datascalar));
|
||||
// Set the data.
|
||||
$this->assertTrue($cache->set($key, $datascalar));
|
||||
// Setting it more than once should be permitted.
|
||||
$this->assertTrue($cache->set($key, $datascalar));
|
||||
|
||||
// Recheck the read methods.
|
||||
$this->assertEquals($datascalar, $cache->get($key));
|
||||
$this->assertTrue($cache->has($key));
|
||||
$result = $cache->get_many(array($key));
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertEquals($datascalar, reset($result));
|
||||
$this->assertTrue($cache->has_any(array($key)));
|
||||
$this->assertTrue($cache->has_all(array($key)));
|
||||
// Recheck the read methods.
|
||||
$this->assertEquals($datascalar, $cache->get($key));
|
||||
$this->assertTrue($cache->has($key));
|
||||
$result = $cache->get_many(array($key));
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertEquals($datascalar, reset($result));
|
||||
$this->assertTrue($cache->has_any(array($key)));
|
||||
$this->assertTrue($cache->has_all(array($key)));
|
||||
|
||||
// Delete it.
|
||||
$this->assertTrue($cache->delete($key));
|
||||
// Delete it.
|
||||
$this->assertTrue($cache->delete($key));
|
||||
|
||||
// Check its gone.
|
||||
$this->assertFalse($cache->get($key));
|
||||
$this->assertFalse($cache->has($key));
|
||||
// Check its gone.
|
||||
$this->assertFalse($cache->get($key));
|
||||
$this->assertFalse($cache->has($key));
|
||||
}
|
||||
|
||||
// Test arrays.
|
||||
$this->assertTrue($cache->set($key, $dataarray));
|
||||
@@ -244,11 +263,13 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
$this->assertEquals('blue_ptc_wfc', $result->property2);
|
||||
|
||||
// Test set many.
|
||||
$cache->set_many(array('key1' => 'data1', 'key2' => 'data2'));
|
||||
$cache->set_many(array('key1' => 'data1', 'key2' => 'data2', 'key3' => null));
|
||||
$this->assertEquals('data1', $cache->get('key1'));
|
||||
$this->assertEquals('data2', $cache->get('key2'));
|
||||
$this->assertEquals(null, $cache->get('key3'));
|
||||
$this->assertTrue($cache->delete('key1'));
|
||||
$this->assertTrue($cache->delete('key2'));
|
||||
$this->assertTrue($cache->delete('key3'));
|
||||
|
||||
$cache->set_many(array(
|
||||
'key1' => array(1, 2, 3),
|
||||
@@ -265,14 +286,17 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
// Test delete many.
|
||||
$this->assertTrue($cache->set('key1', 'data1'));
|
||||
$this->assertTrue($cache->set('key2', 'data2'));
|
||||
$this->assertTrue($cache->set('key3', null));
|
||||
|
||||
$this->assertEquals('data1', $cache->get('key1'));
|
||||
$this->assertEquals('data2', $cache->get('key2'));
|
||||
$this->assertEquals(null, $cache->get('key3'));
|
||||
|
||||
$this->assertEquals(2, $cache->delete_many(array('key1', 'key2')));
|
||||
$this->assertEquals(3, $cache->delete_many(array('key1', 'key2', 'key3')));
|
||||
|
||||
$this->assertFalse($cache->get('key1'));
|
||||
$this->assertFalse($cache->get('key2'));
|
||||
$this->assertFalse($cache->get('key3'));
|
||||
|
||||
// Quick reference test.
|
||||
$obj = new stdClass;
|
||||
@@ -826,7 +850,7 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
// OK data added, data invalidated, and invalidation time has been set.
|
||||
// Now we need to manually add back the data and adjust the invalidation time.
|
||||
$hash = md5(cache_store::MODE_APPLICATION.'/phpunit/eventinvalidationtest/'.$CFG->wwwroot.'phpunit');
|
||||
$timefile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/las/lastinvalidation-$hash.cache";
|
||||
$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.
|
||||
@@ -834,7 +858,7 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
file_put_contents($timefile, $timecont);
|
||||
$this->assertTrue(file_exists($timefile));
|
||||
|
||||
$datafile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/tes/testkey1-$hash.cache";
|
||||
$datafile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/tes-cache/testkey1-$hash.cache";
|
||||
$datacont = serialize("test data 1");
|
||||
make_writable_directory(dirname($datafile));
|
||||
file_put_contents($datafile, $datacont);
|
||||
@@ -883,11 +907,11 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
'crazyevent'
|
||||
)
|
||||
));
|
||||
$instance->phpunit_add_definition('phpunit/eventpurgetestpersistent', array(
|
||||
$instance->phpunit_add_definition('phpunit/eventpurgetestaccelerated', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'eventpurgetestpersistent',
|
||||
'persistent' => true,
|
||||
'area' => 'eventpurgetestaccelerated',
|
||||
'staticacceleration' => true,
|
||||
'invalidationevents' => array(
|
||||
'crazyevent'
|
||||
)
|
||||
@@ -906,8 +930,8 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
$this->assertFalse($cache->get('testkey1'));
|
||||
$this->assertFalse($cache->get('testkey2'));
|
||||
|
||||
// Now test the persistent cache.
|
||||
$cache = cache::make('phpunit', 'eventpurgetestpersistent');
|
||||
// Now test the static acceleration array.
|
||||
$cache = cache::make('phpunit', 'eventpurgetestaccelerated');
|
||||
$this->assertTrue($cache->set('testkey1', 'test data 1'));
|
||||
$this->assertEquals('test data 1', $cache->get('testkey1'));
|
||||
$this->assertTrue($cache->set('testkey2', 'test data 2'));
|
||||
@@ -934,11 +958,11 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
'crazyevent'
|
||||
)
|
||||
));
|
||||
$instance->phpunit_add_definition('phpunit/eventpurgetestpersistent', array(
|
||||
$instance->phpunit_add_definition('phpunit/eventpurgetestaccelerated', array(
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'eventpurgetestpersistent',
|
||||
'persistent' => true,
|
||||
'area' => 'eventpurgetestaccelerated',
|
||||
'staticacceleration' => true,
|
||||
'invalidationevents' => array(
|
||||
'crazyevent'
|
||||
)
|
||||
@@ -957,8 +981,8 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
$this->assertFalse($cache->get('testkey1'));
|
||||
$this->assertFalse($cache->get('testkey2'));
|
||||
|
||||
// Now test the persistent cache.
|
||||
$cache = cache::make('phpunit', 'eventpurgetestpersistent');
|
||||
// Now test the static acceleration array.
|
||||
$cache = cache::make('phpunit', 'eventpurgetestaccelerated');
|
||||
$this->assertTrue($cache->set('testkey1', 'test data 1'));
|
||||
$this->assertEquals('test data 1', $cache->get('testkey1'));
|
||||
$this->assertTrue($cache->set('testkey2', 'test data 2'));
|
||||
@@ -1085,9 +1109,9 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that multiple loaders work ok.
|
||||
* Test that multiple application loaders work ok.
|
||||
*/
|
||||
public function test_multiple_loaders() {
|
||||
public function test_multiple_application_loaders() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance->phpunit_add_file_store('phpunittest1');
|
||||
$instance->phpunit_add_file_store('phpunittest2');
|
||||
@@ -1132,6 +1156,93 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
$this->assertFalse($result['a']);
|
||||
$this->assertEquals('B', $result['b']);
|
||||
$this->assertFalse($result['c']);
|
||||
|
||||
// Test non-recursive deletes.
|
||||
$this->assertTrue($cache->set('test', 'test'));
|
||||
$this->assertSame('test', $cache->get('test'));
|
||||
$this->assertTrue($cache->delete('test', false));
|
||||
// We should still have it on a deeper loader.
|
||||
$this->assertSame('test', $cache->get('test'));
|
||||
// Test non-recusive with many functions.
|
||||
$this->assertSame(3, $cache->set_many(array(
|
||||
'one' => 'one',
|
||||
'two' => 'two',
|
||||
'three' => 'three'
|
||||
)));
|
||||
$this->assertSame('one', $cache->get('one'));
|
||||
$this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three')));
|
||||
$this->assertSame(3, $cache->delete_many(array('one', 'two', 'three'), false));
|
||||
$this->assertSame('one', $cache->get('one'));
|
||||
$this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that multiple application loaders work ok.
|
||||
*/
|
||||
public function test_multiple_session_loaders() {
|
||||
/* @var cache_config_phpunittest $instance */
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance->phpunit_add_session_store('phpunittest1');
|
||||
$instance->phpunit_add_session_store('phpunittest2');
|
||||
$instance->phpunit_add_definition('phpunit/multi_loader', array(
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'multi_loader'
|
||||
));
|
||||
$instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest1', 3);
|
||||
$instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest2', 2);
|
||||
|
||||
$cache = cache::make('phpunit', 'multi_loader');
|
||||
$this->assertInstanceOf('cache_session', $cache);
|
||||
$this->assertFalse($cache->get('test'));
|
||||
$this->assertTrue($cache->set('test', 'test'));
|
||||
$this->assertEquals('test', $cache->get('test'));
|
||||
$this->assertTrue($cache->delete('test'));
|
||||
$this->assertFalse($cache->get('test'));
|
||||
$this->assertTrue($cache->set('test', 'test'));
|
||||
$this->assertTrue($cache->purge());
|
||||
$this->assertFalse($cache->get('test'));
|
||||
|
||||
// Test the many commands.
|
||||
$this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C')));
|
||||
$result = $cache->get_many(array('a', 'b', 'c'));
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertCount(3, $result);
|
||||
$this->assertArrayHasKey('a', $result);
|
||||
$this->assertArrayHasKey('b', $result);
|
||||
$this->assertArrayHasKey('c', $result);
|
||||
$this->assertEquals('A', $result['a']);
|
||||
$this->assertEquals('B', $result['b']);
|
||||
$this->assertEquals('C', $result['c']);
|
||||
$this->assertEquals($result, $cache->get_many(array('a', 'b', 'c')));
|
||||
$this->assertEquals(2, $cache->delete_many(array('a', 'c')));
|
||||
$result = $cache->get_many(array('a', 'b', 'c'));
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertCount(3, $result);
|
||||
$this->assertArrayHasKey('a', $result);
|
||||
$this->assertArrayHasKey('b', $result);
|
||||
$this->assertArrayHasKey('c', $result);
|
||||
$this->assertFalse($result['a']);
|
||||
$this->assertEquals('B', $result['b']);
|
||||
$this->assertFalse($result['c']);
|
||||
|
||||
// Test non-recursive deletes.
|
||||
$this->assertTrue($cache->set('test', 'test'));
|
||||
$this->assertSame('test', $cache->get('test'));
|
||||
$this->assertTrue($cache->delete('test', false));
|
||||
// We should still have it on a deeper loader.
|
||||
$this->assertSame('test', $cache->get('test'));
|
||||
// Test non-recusive with many functions.
|
||||
$this->assertSame(3, $cache->set_many(array(
|
||||
'one' => 'one',
|
||||
'two' => 'two',
|
||||
'three' => 'three'
|
||||
)));
|
||||
$this->assertSame('one', $cache->get('one'));
|
||||
$this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three')));
|
||||
$this->assertSame(3, $cache->delete_many(array('one', 'two', 'three'), false));
|
||||
$this->assertSame('one', $cache->get('one'));
|
||||
$this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three')));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1272,8 +1383,8 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'test_application_locking',
|
||||
'persistent' => true,
|
||||
'persistentmaxsize' => 1,
|
||||
'staticacceleration' => true,
|
||||
'staticaccelerationsize' => 1,
|
||||
'requirelockingread' => true,
|
||||
'requirelockingwrite' => true
|
||||
));
|
||||
@@ -1288,4 +1399,212 @@ class cache_phpunit_tests extends advanced_testcase {
|
||||
$this->assertTrue($cache->delete('a'));
|
||||
$this->assertFalse($cache->has('a'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the default stores all support searching.
|
||||
*/
|
||||
public function test_defaults_support_searching() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/search1', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'search1',
|
||||
'requiresearchable' => true
|
||||
));
|
||||
$instance->phpunit_add_definition('phpunit/search2', array(
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'search2',
|
||||
'requiresearchable' => true
|
||||
));
|
||||
$instance->phpunit_add_definition('phpunit/search3', array(
|
||||
'mode' => cache_store::MODE_REQUEST,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'search3',
|
||||
'requiresearchable' => true
|
||||
));
|
||||
$factory = cache_factory::instance();
|
||||
|
||||
// Test application cache is searchable.
|
||||
$definition = $factory->create_definition('phpunit', 'search1');
|
||||
$this->assertInstanceOf('cache_definition', $definition);
|
||||
$this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE);
|
||||
$cache = $factory->create_cache($definition);
|
||||
$this->assertInstanceOf('cache_application', $cache);
|
||||
$this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements());
|
||||
|
||||
// Test session cache is searchable.
|
||||
$definition = $factory->create_definition('phpunit', 'search2');
|
||||
$this->assertInstanceOf('cache_definition', $definition);
|
||||
$this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE);
|
||||
$cache = $factory->create_cache($definition);
|
||||
$this->assertInstanceOf('cache_session', $cache);
|
||||
$this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements());
|
||||
|
||||
// Test request cache is searchable.
|
||||
$definition = $factory->create_definition('phpunit', 'search3');
|
||||
$this->assertInstanceOf('cache_definition', $definition);
|
||||
$this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE);
|
||||
$cache = $factory->create_cache($definition);
|
||||
$this->assertInstanceOf('cache_request', $cache);
|
||||
$this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements());
|
||||
}
|
||||
|
||||
public function test_static_acceleration() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance->phpunit_add_definition('phpunit/accelerated', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'accelerated',
|
||||
'staticacceleration' => true,
|
||||
'staticaccelerationsize' => 3,
|
||||
));
|
||||
$instance->phpunit_add_definition('phpunit/accelerated2', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'accelerated2',
|
||||
'staticacceleration' => true,
|
||||
'staticaccelerationsize' => 3,
|
||||
));
|
||||
$instance->phpunit_add_definition('phpunit/accelerated3', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'accelerated3',
|
||||
'staticacceleration' => true,
|
||||
'staticaccelerationsize' => 3,
|
||||
));
|
||||
$instance->phpunit_add_definition('phpunit/accelerated4', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'accelerated4',
|
||||
'staticacceleration' => true,
|
||||
'staticaccelerationsize' => 4,
|
||||
));
|
||||
$cache = cache::make('phpunit', 'accelerated');
|
||||
$this->assertInstanceOf('cache_phpunit_application', $cache);
|
||||
|
||||
// Set and get three elements.
|
||||
$this->assertTrue($cache->set('a', 'A'));
|
||||
$this->assertTrue($cache->set('b', 'B'));
|
||||
$this->assertTrue($cache->set('c', 'C'));
|
||||
$this->assertEquals('A', $cache->get('a'));
|
||||
$this->assertEquals(array('b' => 'B', 'c' => 'C'), $cache->get_many(array('b', 'c')));
|
||||
|
||||
// Make sure all items are in static acceleration array.
|
||||
$this->assertEquals('A', $cache->phpunit_get_directly_from_staticaccelerationarray('a'));
|
||||
$this->assertEquals('B', $cache->phpunit_get_directly_from_staticaccelerationarray('b'));
|
||||
$this->assertEquals('C', $cache->phpunit_get_directly_from_staticaccelerationarray('c'));
|
||||
|
||||
// Add new value and make sure it is in cache and it is in array.
|
||||
$this->assertTrue($cache->set('d', 'D'));
|
||||
$this->assertEquals('D', $cache->phpunit_get_directly_from_staticaccelerationarray('d'));
|
||||
$this->assertEquals('D', $cache->get('d'));
|
||||
|
||||
// Now the least recent accessed item (a) is no longer in acceleration array.
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('a'));
|
||||
$this->assertEquals('B', $cache->phpunit_get_directly_from_staticaccelerationarray('b'));
|
||||
$this->assertEquals('C', $cache->phpunit_get_directly_from_staticaccelerationarray('c'));
|
||||
|
||||
// Adding and deleting element.
|
||||
$this->assertTrue($cache->set('a', 'A'));
|
||||
$this->assertTrue($cache->delete('a'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('a'));
|
||||
$this->assertFalse($cache->has('a'));
|
||||
|
||||
// Make sure "purge" deletes from the array as well.
|
||||
$cache->purge();
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('a'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('b'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('c'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('d'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('e'));
|
||||
|
||||
// Check that the array holds the last accessed items by get/set.
|
||||
$this->assertTrue($cache->set('a', 'A'));
|
||||
$this->assertTrue($cache->set('b', 'B'));
|
||||
$this->assertTrue($cache->set('c', 'C'));
|
||||
$this->assertTrue($cache->set('d', 'D'));
|
||||
$this->assertTrue($cache->set('e', 'E'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('a'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('b'));
|
||||
$this->assertEquals('C', $cache->phpunit_get_directly_from_staticaccelerationarray('c'));
|
||||
$this->assertEquals('D', $cache->phpunit_get_directly_from_staticaccelerationarray('d'));
|
||||
$this->assertEquals('E', $cache->phpunit_get_directly_from_staticaccelerationarray('e'));
|
||||
|
||||
/** @var cache_phpunit_application $cache */
|
||||
$cache = cache::make('phpunit', 'accelerated2');
|
||||
$this->assertInstanceOf('cache_phpunit_application', $cache);
|
||||
|
||||
// Check that the array holds the last accessed items by get/set.
|
||||
$this->assertTrue($cache->set('a', 'A'));
|
||||
$this->assertTrue($cache->set('b', 'B'));
|
||||
$this->assertTrue($cache->set('c', 'C'));
|
||||
$this->assertTrue($cache->set('d', 'D'));
|
||||
$this->assertTrue($cache->set('e', 'E'));
|
||||
// Current keys in the array: c, d, e.
|
||||
$this->assertEquals('C', $cache->phpunit_get_directly_from_staticaccelerationarray('c'));
|
||||
$this->assertEquals('D', $cache->phpunit_get_directly_from_staticaccelerationarray('d'));
|
||||
$this->assertEquals('E', $cache->phpunit_get_directly_from_staticaccelerationarray('e'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('a'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('b'));
|
||||
|
||||
$this->assertEquals('A', $cache->get('a'));
|
||||
// Current keys in the array: d, e, a.
|
||||
$this->assertEquals('D', $cache->phpunit_get_directly_from_staticaccelerationarray('d'));
|
||||
$this->assertEquals('E', $cache->phpunit_get_directly_from_staticaccelerationarray('e'));
|
||||
$this->assertEquals('A', $cache->phpunit_get_directly_from_staticaccelerationarray('a'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('b'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('c'));
|
||||
|
||||
// Current keys in the array: d, e, a.
|
||||
$this->assertEquals(array('c' => 'C'), $cache->get_many(array('c')));
|
||||
// Current keys in the array: e, a, c.
|
||||
$this->assertEquals('E', $cache->phpunit_get_directly_from_staticaccelerationarray('e'));
|
||||
$this->assertEquals('A', $cache->phpunit_get_directly_from_staticaccelerationarray('a'));
|
||||
$this->assertEquals('C', $cache->phpunit_get_directly_from_staticaccelerationarray('c'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('b'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('d'));
|
||||
|
||||
|
||||
$cache = cache::make('phpunit', 'accelerated3');
|
||||
$this->assertInstanceOf('cache_phpunit_application', $cache);
|
||||
|
||||
// Check that the array holds the last accessed items by get/set.
|
||||
$this->assertTrue($cache->set('a', 'A'));
|
||||
$this->assertTrue($cache->set('b', 'B'));
|
||||
$this->assertTrue($cache->set('c', 'C'));
|
||||
$this->assertTrue($cache->set('d', 'D'));
|
||||
$this->assertTrue($cache->set('e', 'E'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('a'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('b'));
|
||||
$this->assertEquals('C', $cache->phpunit_get_directly_from_staticaccelerationarray('c'));
|
||||
$this->assertEquals('D', $cache->phpunit_get_directly_from_staticaccelerationarray('d'));
|
||||
$this->assertEquals('E', $cache->phpunit_get_directly_from_staticaccelerationarray('e'));
|
||||
|
||||
$this->assertTrue($cache->set('b', 'B2'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('a'));
|
||||
$this->assertEquals('B2', $cache->phpunit_get_directly_from_staticaccelerationarray('b'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('c'));
|
||||
$this->assertEquals('D', $cache->phpunit_get_directly_from_staticaccelerationarray('d'));
|
||||
$this->assertEquals('E', $cache->phpunit_get_directly_from_staticaccelerationarray('e'));
|
||||
|
||||
$this->assertEquals(2, $cache->set_many(array('b' => 'B3', 'c' => 'C3')));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('a'));
|
||||
$this->assertEquals('B3', $cache->phpunit_get_directly_from_staticaccelerationarray('b'));
|
||||
$this->assertEquals('C3', $cache->phpunit_get_directly_from_staticaccelerationarray('c'));
|
||||
$this->assertFalse($cache->phpunit_get_directly_from_staticaccelerationarray('d'));
|
||||
$this->assertEquals('E', $cache->phpunit_get_directly_from_staticaccelerationarray('e'));
|
||||
|
||||
$cache = cache::make('phpunit', 'accelerated4');
|
||||
$this->assertInstanceOf('cache_phpunit_application', $cache);
|
||||
$this->assertTrue($cache->set('a', 'A'));
|
||||
$this->assertTrue($cache->set('a', 'A'));
|
||||
$this->assertTrue($cache->set('a', 'A'));
|
||||
$this->assertTrue($cache->set('a', 'A'));
|
||||
$this->assertTrue($cache->set('a', 'A'));
|
||||
$this->assertTrue($cache->set('a', 'A'));
|
||||
$this->assertTrue($cache->set('a', 'A'));
|
||||
$this->assertEquals('A', $cache->phpunit_get_directly_from_staticaccelerationarray('a'));
|
||||
$this->assertEquals('A', $cache->get('a'));
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+52
@@ -86,6 +86,24 @@ class cache_config_phpunittest extends cache_config_writer {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcefully adds a session store.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function phpunit_add_session_store($name) {
|
||||
$this->configstores[$name] = array(
|
||||
'name' => $name,
|
||||
'plugin' => 'session',
|
||||
'configuration' => array(),
|
||||
'features' => 14,
|
||||
'modes' => 2,
|
||||
'default' => true,
|
||||
'class' => 'cachestore_session',
|
||||
'lock' => 'cachelock_file_default',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcefully injects a definition => store mapping.
|
||||
*
|
||||
@@ -218,6 +236,24 @@ class cache_phpunit_application extends cache_application {
|
||||
return get_class($this->get_store());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the interfaces the cache store implements.
|
||||
* @return array
|
||||
*/
|
||||
public function phpunit_get_store_implements() {
|
||||
return class_implements($this->get_store());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given key directly from the static acceleration array.
|
||||
*
|
||||
* @param string $key
|
||||
* @return false|mixed
|
||||
*/
|
||||
public function phpunit_get_directly_from_staticaccelerationarray($key) {
|
||||
$key = $this->parse_key($key);
|
||||
return $this->get_from_persist_cache($key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,6 +273,14 @@ class cache_phpunit_session extends cache_session {
|
||||
public function phpunit_get_store_class() {
|
||||
return get_class($this->get_store());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the interfaces the cache store implements.
|
||||
* @return array
|
||||
*/
|
||||
public function phpunit_get_store_implements() {
|
||||
return class_implements($this->get_store());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,6 +300,14 @@ class cache_phpunit_request extends cache_request {
|
||||
public function phpunit_get_store_class() {
|
||||
return get_class($this->get_store());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the interfaces the cache store implements.
|
||||
* @return array
|
||||
*/
|
||||
public function phpunit_get_store_implements() {
|
||||
return class_implements($this->get_store());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+10
-2
@@ -1,8 +1,16 @@
|
||||
This files describes API changes in /cache/stores/* - cache store plugins.
|
||||
Information provided here is intended especially for developers.
|
||||
|
||||
=== 2.5 ===
|
||||
=== 2.4.7 ===
|
||||
* All cache instances are recorded and subsequent requests are given a reference to the original instance.
|
||||
* The persistent option for the cache definition has been deprecated. Please use the staticacceleration option instead.
|
||||
* There is a new static acceleration option. If enabled data passing through the cache is held onto.
|
||||
* The persistentmaxsize option has been renamed to staticaccelerationsize. It does the same thing.
|
||||
* cache_definition::should_be_persistent has been deprecated. Please call cache_definition::use_static_acceleration instead.
|
||||
* cache_definition::get_persistent_max_size has been deprecated. Please call cache_definition::get_static_acceleration_size instead.
|
||||
|
||||
=== 2.4.2 ===
|
||||
* cleanup method renamed to instance_deleted.
|
||||
It is now called when the store is deleted as all comments suggested anyway.
|
||||
* instance_created method added.
|
||||
It is called when the store is created for the very first time.
|
||||
It is called when the store is created for the very first time.
|
||||
|
||||
+2
-2
@@ -106,8 +106,8 @@ $PAGE->navbar->add($pagetitle);
|
||||
|
||||
$PAGE->set_title($course->shortname.': '.get_string('calendar', 'calendar').': '.$pagetitle);
|
||||
$PAGE->set_heading($course->fullname);
|
||||
$PAGE->set_button(calendar_preferences_button($course));
|
||||
$PAGE->set_pagelayout('standard');
|
||||
$PAGE->set_button(calendar_preferences_button($course));
|
||||
|
||||
$renderer = $PAGE->get_renderer('core_calendar');
|
||||
$calendar->add_sidecalendar_blocks($renderer);
|
||||
@@ -125,7 +125,7 @@ switch($action) {
|
||||
$weekend = intval($CFG->calendar_weekend);
|
||||
}
|
||||
|
||||
$authtoken = sha1($USER->id . $USER->password . $CFG->calendar_exportsalt);
|
||||
$authtoken = sha1($USER->id . $DB->get_field('user', 'password', array('id'=>$USER->id)). $CFG->calendar_exportsalt);
|
||||
// Let's populate some vars to let "common tasks" be somewhat smart...
|
||||
// If today it's weekend, give the "next week" option
|
||||
$allownextweek = $weekend & (1 << $now['wday']);
|
||||
|
||||
+14
-6
@@ -110,6 +110,16 @@ define('CALENDAR_IMPORT_EVENT_UPDATED', 1);
|
||||
*/
|
||||
define('CALENDAR_IMPORT_EVENT_INSERTED', 2);
|
||||
|
||||
/**
|
||||
* CALENDAR_SUBSCRIPTION_UPDATE - Used to represent update action for subscriptions in various forms.
|
||||
*/
|
||||
define('CALENDAR_SUBSCRIPTION_UPDATE', 1);
|
||||
|
||||
/**
|
||||
* CALENDAR_SUBSCRIPTION_REMOVE - Used to represent remove action for subscriptions in various forms.
|
||||
*/
|
||||
define('CALENDAR_SUBSCRIPTION_REMOVE', 2);
|
||||
|
||||
/**
|
||||
* Return the days of the week
|
||||
*
|
||||
@@ -356,7 +366,7 @@ function calendar_get_mini($courses, $groups, $users, $cal_month = false, $cal_y
|
||||
} else if(isset($typesbyday[$day]['startuser'])) {
|
||||
$class .= ' calendar_event_user';
|
||||
}
|
||||
$cell = html_writer::link($dayhref, $day, array('aria-controls' => $popupid.'_panel', 'id' => $popupid));
|
||||
$cell = html_writer::link($dayhref, $day, array('id' => $popupid));
|
||||
} else {
|
||||
$cell = $day;
|
||||
}
|
||||
@@ -399,7 +409,7 @@ function calendar_get_mini($courses, $groups, $users, $cal_month = false, $cal_y
|
||||
if(! isset($eventsbyday[$day])) {
|
||||
$class .= ' eventnone';
|
||||
$popupid = calendar_get_popup(true, false);
|
||||
$cell = html_writer::link('#', $day, array('aria-controls' => $popupid.'_panel', 'id' => $popupid));
|
||||
$cell = html_writer::link('#', $day, array('id' => $popupid));
|
||||
}
|
||||
$cell = get_accesshide($today.' ').$cell;
|
||||
}
|
||||
@@ -2851,11 +2861,9 @@ function calendar_process_subscription_row($subscriptionid, $pollinterval, $acti
|
||||
// Fetch the subscription from the database making sure it exists.
|
||||
$sub = $DB->get_record('event_subscriptions', array('id' => $subscriptionid), '*', MUST_EXIST);
|
||||
|
||||
$strupdate = get_string('update');
|
||||
$strremove = get_string('remove');
|
||||
// Update or remove the subscription, based on action.
|
||||
switch ($action) {
|
||||
case $strupdate:
|
||||
case CALENDAR_SUBSCRIPTION_UPDATE:
|
||||
// Skip updating file subscriptions.
|
||||
if (empty($sub->url)) {
|
||||
break;
|
||||
@@ -2866,7 +2874,7 @@ function calendar_process_subscription_row($subscriptionid, $pollinterval, $acti
|
||||
// Update the events.
|
||||
return "<p>".get_string('subscriptionupdated', 'calendar', $sub->name)."</p>" . calendar_update_subscription_events($subscriptionid);
|
||||
|
||||
case $strremove:
|
||||
case CALENDAR_SUBSCRIPTION_REMOVE:
|
||||
calendar_delete_subscription($subscriptionid);
|
||||
return get_string('subscriptionremoved', 'calendar', $sub->name);
|
||||
break;
|
||||
|
||||
@@ -33,7 +33,7 @@ $courseid = optional_param('course', SITEID, PARAM_INT);
|
||||
// Used for processing subscription actions.
|
||||
$subscriptionid = optional_param('id', 0, PARAM_INT);
|
||||
$pollinterval = optional_param('pollinterval', 0, PARAM_INT);
|
||||
$action = optional_param('action', '', PARAM_ALPHA);
|
||||
$action = optional_param('action', '', PARAM_INT);
|
||||
|
||||
$url = new moodle_url('/calendar/managesubscriptions.php');
|
||||
if ($courseid != SITEID) {
|
||||
|
||||
@@ -814,9 +814,11 @@ class core_calendar_renderer extends plugin_renderer_base {
|
||||
$html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'course', 'value' => $courseid));
|
||||
$html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'id', 'value' => $subscription->id));
|
||||
if (!empty($subscription->url)) {
|
||||
$html .= html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'action', 'value' => get_string('update')));
|
||||
$html .= html_writer::tag('button', get_string('update'), array('type' => 'submit', 'name' => 'action',
|
||||
'value' => CALENDAR_SUBSCRIPTION_UPDATE));
|
||||
}
|
||||
$html .= html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'action', 'value' => get_string('remove')));
|
||||
$html .= html_writer::tag('button', get_string('remove'), array('type' => 'submit', 'name' => 'action',
|
||||
'value' => CALENDAR_SUBSCRIPTION_REMOVE));
|
||||
$html .= html_writer::end_tag('div');
|
||||
$html .= html_writer::end_tag('form');
|
||||
return $html;
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Calendar lib unit tests
|
||||
*
|
||||
* @package core_calendar
|
||||
* @copyright 2013 Dan Poltawski <dan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/calendar/lib.php');
|
||||
|
||||
/**
|
||||
* Unit tests for calendar lib
|
||||
*
|
||||
* @package core_calendar
|
||||
* @copyright 2013 Dan Poltawski <dan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_calendar_lib_testcase extends advanced_testcase {
|
||||
|
||||
public function test_calendar_get_course_cached() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Setup some test courses.
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
$course2 = $this->getDataGenerator()->create_course();
|
||||
$course3 = $this->getDataGenerator()->create_course();
|
||||
|
||||
// Load courses into cache.
|
||||
$coursecache = null;
|
||||
calendar_get_course_cached($coursecache, $course1->id);
|
||||
calendar_get_course_cached($coursecache, $course2->id);
|
||||
calendar_get_course_cached($coursecache, $course3->id);
|
||||
|
||||
// Verify the cache.
|
||||
$this->assertArrayHasKey($course1->id, $coursecache);
|
||||
$cachedcourse1 = $coursecache[$course1->id];
|
||||
$this->assertEquals($course1->id, $cachedcourse1->id);
|
||||
$this->assertEquals($course1->shortname, $cachedcourse1->shortname);
|
||||
$this->assertEquals($course1->fullname, $cachedcourse1->fullname);
|
||||
|
||||
$this->assertArrayHasKey($course2->id, $coursecache);
|
||||
$cachedcourse2 = $coursecache[$course2->id];
|
||||
$this->assertEquals($course2->id, $cachedcourse2->id);
|
||||
$this->assertEquals($course2->shortname, $cachedcourse2->shortname);
|
||||
$this->assertEquals($course2->fullname, $cachedcourse2->fullname);
|
||||
|
||||
$this->assertArrayHasKey($course3->id, $coursecache);
|
||||
$cachedcourse3 = $coursecache[$course3->id];
|
||||
$this->assertEquals($course3->id, $cachedcourse3->id);
|
||||
$this->assertEquals($course3->shortname, $cachedcourse3->shortname);
|
||||
$this->assertEquals($course3->fullname, $cachedcourse3->fullname);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -153,7 +153,7 @@ if (!empty($CFG->enablecalendarexport)) {
|
||||
echo $OUTPUT->single_button(new moodle_url('/calendar/managesubscriptions.php', array('course'=>$courseid)), get_string('managesubscriptions', 'calendar'));
|
||||
}
|
||||
if (isloggedin()) {
|
||||
$authtoken = sha1($USER->id . $USER->password . $CFG->calendar_exportsalt);
|
||||
$authtoken = sha1($USER->id . $DB->get_field('user', 'password', array('id'=>$USER->id)) . $CFG->calendar_exportsalt);
|
||||
$link = new moodle_url('/calendar/export_execute.php', array('preset_what'=>'all', 'preset_time'=>'recentupcoming', 'userid' => $USER->id, 'authtoken'=>$authtoken));
|
||||
$icon = html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('i/ical'), 'height'=>'14', 'width'=>'36', 'alt'=>get_string('ical', 'calendar'), 'title'=>get_string('quickdownloadcalendar', 'calendar')));
|
||||
echo html_writer::tag('a', $icon, array('href'=>$link));
|
||||
|
||||
+1
@@ -48,6 +48,7 @@ YUI.add('moodle-calendar-eventmanager', function(Y) {
|
||||
width : Math.floor(constraint.get('offsetWidth')*0.9)+"px"
|
||||
});
|
||||
panel.render(td);
|
||||
node.setAttribute('aria-controls', panel.get('id'));
|
||||
panel.get('boundingBox').addClass('calendar-event-panel');
|
||||
panel.get('boundingBox').setAttribute('aria-live', 'off');
|
||||
this.on('showevent', panel.show, panel);
|
||||
|
||||
@@ -86,6 +86,7 @@ $search .= html_writer::start_tag('div');
|
||||
$search .= html_writer::label(get_string('searchcohort', 'cohort'), 'cohort_search_q'); // No : in form labels!
|
||||
$search .= html_writer::empty_tag('input', array('id'=>'cohort_search_q', 'type'=>'text', 'name'=>'search', 'value'=>$searchquery));
|
||||
$search .= html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('search', 'cohort')));
|
||||
$search .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'contextid', 'value'=>$contextid));
|
||||
$search .= html_writer::end_tag('div');
|
||||
$search .= html_writer::end_tag('form');
|
||||
echo $search;
|
||||
|
||||
+13
-33
@@ -59,11 +59,6 @@ M.core_comment = {
|
||||
}, this);
|
||||
}
|
||||
scope.toggle_textarea(false);
|
||||
CommentHelper.confirmoverlay = new Y.Overlay({
|
||||
bodyContent: '<div class="comment-delete-confirm"><a href="#" id="confirmdelete-'+this.client_id+'">'+M.str.moodle.yes+'</a> <a href="#" id="canceldelete-'+this.client_id+'">'+M.str.moodle.no+'</a></div>',
|
||||
visible: false
|
||||
});
|
||||
CommentHelper.confirmoverlay.render(document.body);
|
||||
},
|
||||
post: function() {
|
||||
var ta = Y.one('#dlg-content-'+this.client_id);
|
||||
@@ -238,7 +233,6 @@ bodyContent: '<div class="comment-delete-confirm"><a href="#" id="confirmdelete-
|
||||
dodelete: function(id) { // note: delete is a reserved word in javascript, chrome and safary do not like it at all here!
|
||||
var scope = this;
|
||||
var params = {'commentid': id};
|
||||
scope.cancel_delete();
|
||||
function remove_dom(type, anim, cmt) {
|
||||
cmt.remove();
|
||||
}
|
||||
@@ -294,37 +288,23 @@ bodyContent: '<div class="comment-delete-confirm"><a href="#" id="confirmdelete-
|
||||
if (commentid[1]) {
|
||||
Y.Event.purgeElement('#'+theid, false, 'click');
|
||||
}
|
||||
node.on('click', function(e, node) {
|
||||
node.on('click', function(e) {
|
||||
e.preventDefault();
|
||||
var width = CommentHelper.confirmoverlay.bodyNode.getStyle('width');
|
||||
var re = new RegExp("(\\d+).*", "i");
|
||||
var result = width.match(re);
|
||||
if (result[1]) {
|
||||
width = Number(result[1]);
|
||||
} else {
|
||||
width = 0;
|
||||
if (commentid[1]) {
|
||||
scope.dodelete(commentid[1]);
|
||||
}
|
||||
//CommentHelper.confirmoverlay.set('xy', [e.pageX-(width/2), e.pageY]);
|
||||
CommentHelper.confirmoverlay.set('xy', [e.pageX-width-5, e.pageY]);
|
||||
CommentHelper.confirmoverlay.set('visible', true);
|
||||
Y.one('#canceldelete-'+scope.client_id).on('click', function(e) {
|
||||
e.preventDefault();
|
||||
scope.cancel_delete();
|
||||
});
|
||||
Y.Event.purgeElement('#confirmdelete-'+scope.client_id, false, 'click');
|
||||
Y.one('#confirmdelete-'+scope.client_id).on('click', function(e) {
|
||||
e.preventDefault();
|
||||
if (commentid[1]) {
|
||||
scope.dodelete(commentid[1]);
|
||||
}
|
||||
});
|
||||
}, scope, node);
|
||||
});
|
||||
// Also handle space/enter key.
|
||||
node.on('key', function(e) {
|
||||
e.preventDefault();
|
||||
if (commentid[1]) {
|
||||
scope.dodelete(commentid[1]);
|
||||
}
|
||||
}, '13,32');
|
||||
// 13 and 32 are the keycodes for space and enter.
|
||||
}
|
||||
);
|
||||
},
|
||||
cancel_delete: function() {
|
||||
CommentHelper.confirmoverlay.set('visible', false);
|
||||
},
|
||||
register_pagination: function() {
|
||||
var scope = this;
|
||||
// page buttons
|
||||
@@ -374,7 +354,7 @@ bodyContent: '<div class="comment-delete-confirm"><a href="#" id="confirmdelete-
|
||||
if (ta) {
|
||||
//toggle_textarea.apply(ta, [false]);
|
||||
//// reset textarea size
|
||||
ta.on('click', function() {
|
||||
ta.on('focus', function() {
|
||||
this.toggle_textarea(true);
|
||||
}, this);
|
||||
//ta.onkeypress = function() {
|
||||
|
||||
+4
-1
@@ -487,7 +487,10 @@ $CFG->admin = 'admin';
|
||||
// $CFG->debugusers = '2';
|
||||
//
|
||||
// Prevent theme caching
|
||||
// $CFG->themerev = -1; // NOT FOR PRODUCTION SERVERS!
|
||||
// $CFG->themedesignermode = true; // NOT FOR PRODUCTION SERVERS!
|
||||
//
|
||||
// Prevent JS caching
|
||||
// $CFG->cachejs = false; // NOT FOR PRODUCTION SERVERS!
|
||||
//
|
||||
// Prevent core_string_manager on-disk cache
|
||||
// $CFG->langstringcache = false; // NOT FOR PRODUCTION SERVERS!
|
||||
|
||||
+14
-10
@@ -196,7 +196,7 @@ class course_edit_form extends moodleform {
|
||||
enrol_course_edit_form($mform, $course, $context);
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
$mform->addElement('header','', get_string('groups', 'group'));
|
||||
$mform->addElement('header', '', get_string('groupsettingsheader', 'group'));
|
||||
|
||||
$choices = array();
|
||||
$choices[NOGROUPS] = get_string('groupsnone', 'group');
|
||||
@@ -328,19 +328,23 @@ class course_edit_form extends moodleform {
|
||||
|
||||
/// perform some extra moodle validation
|
||||
function validation($data, $files) {
|
||||
global $DB, $CFG;
|
||||
global $DB;
|
||||
|
||||
$errors = parent::validation($data, $files);
|
||||
if ($foundcourses = $DB->get_records('course', array('shortname'=>$data['shortname']))) {
|
||||
if (!empty($data['id'])) {
|
||||
unset($foundcourses[$data['id']]);
|
||||
|
||||
// Add field validation check for duplicate shortname.
|
||||
if ($course = $DB->get_record('course', array('shortname' => $data['shortname']), '*', IGNORE_MULTIPLE)) {
|
||||
if (empty($data['id']) || $course->id != $data['id']) {
|
||||
$errors['shortname'] = get_string('shortnametaken', '', $course->fullname);
|
||||
}
|
||||
if (!empty($foundcourses)) {
|
||||
foreach ($foundcourses as $foundcourse) {
|
||||
$foundcoursenames[] = $foundcourse->fullname;
|
||||
}
|
||||
|
||||
// Add field validation check for duplicate idnumber.
|
||||
if (!empty($data['idnumber']) && (empty($data['id']) || $this->course->idnumber != $data['idnumber'])) {
|
||||
if ($course = $DB->get_record('course', array('idnumber' => $data['idnumber']), '*', IGNORE_MULTIPLE)) {
|
||||
if (empty($data['id']) || $course->id != $data['id']) {
|
||||
$errors['idnumber'] = get_string('idnumbertaken', 'error');
|
||||
}
|
||||
$foundcoursenamestring = implode(',', $foundcoursenames);
|
||||
$errors['shortname']= get_string('shortnametaken', '', $foundcoursenamestring);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,10 @@ class editsection_form extends moodleform {
|
||||
$course = $this->_customdata['course'];
|
||||
|
||||
$elementgroup = array();
|
||||
$elementgroup[] = $mform->createElement('text', 'name', '', array('size' => '30'));
|
||||
$elementgroup[] = $mform->createElement('text', 'name', '', array('size' => '30', 'maxlength' => '255'));
|
||||
$elementgroup[] = $mform->createElement('checkbox', 'usedefaultname', '', get_string('sectionusedefaultname'));
|
||||
$mform->addGroup($elementgroup, 'name_group', get_string('sectionname'), ' ', false);
|
||||
$mform->addGroupRule('name_group', array('name' => array(array(get_string('maximumchars', '', 255), 'maxlength', 255))));
|
||||
|
||||
$mform->setDefault('usedefaultname', true);
|
||||
$mform->setType('name', PARAM_TEXT);
|
||||
|
||||
@@ -715,9 +715,9 @@ class core_course_external extends external_api {
|
||||
"users" (int) Include users (default to 0 that is equal to no),
|
||||
"role_assignments" (int) Include role assignments (default to 0 that is equal to no),
|
||||
"comments" (int) Include user comments (default to 0 that is equal to no),
|
||||
"completion_information" (int) Include user course completion information (default to 0 that is equal to no),
|
||||
"userscompletion" (int) Include user course completion information (default to 0 that is equal to no),
|
||||
"logs" (int) Include course logs (default to 0 that is equal to no),
|
||||
"histories" (int) Include histories (default to 0 that is equal to no)'
|
||||
"grade_histories" (int) Include histories (default to 0 that is equal to no)'
|
||||
),
|
||||
'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
|
||||
)
|
||||
@@ -779,9 +779,9 @@ class core_course_external extends external_api {
|
||||
'users' => 0,
|
||||
'role_assignments' => 0,
|
||||
'comments' => 0,
|
||||
'completion_information' => 0,
|
||||
'userscompletion' => 0,
|
||||
'logs' => 0,
|
||||
'histories' => 0
|
||||
'grade_histories' => 0
|
||||
);
|
||||
|
||||
$backupsettings = array();
|
||||
@@ -979,7 +979,7 @@ class core_course_external extends external_api {
|
||||
);
|
||||
|
||||
if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
|
||||
throw new moodle_exception('invalidextparam', 'webservice', '', $option['deletecontent']);
|
||||
throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']);
|
||||
}
|
||||
|
||||
// Context validation.
|
||||
|
||||
@@ -515,7 +515,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
$o.= html_writer::tag('div', '', array('class' => 'left side'));
|
||||
$o.= html_writer::tag('div', '', array('class' => 'right side'));
|
||||
$o.= html_writer::start_tag('div', array('class' => 'content'));
|
||||
$o.= $this->output->heading(get_string('orphanedactivities'), 3, 'sectionname');
|
||||
$o.= $this->output->heading(get_string('orphanedactivitiesinsectionno', '', $sectionno), 3, 'sectionname');
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user