Compare commits
521 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22984eaecc | |||
| ed00c49c6f | |||
| 7a4476b834 | |||
| cd827ebe23 | |||
| b76287f846 | |||
| 2231902a45 | |||
| 01403722a8 | |||
| e1c1dfc748 | |||
| 32ded92858 | |||
| 14d9570a54 | |||
| 821ee3f6d3 | |||
| b3261d7e47 | |||
| ac7c3878f6 | |||
| acf717ab77 | |||
| ddd7021adb | |||
| 05b03a44f4 | |||
| ab62531857 | |||
| a33058ebb3 | |||
| dea05d9284 | |||
| 821342fac5 | |||
| b73d600ac5 | |||
| 5b401c9055 | |||
| 79a4c28dd5 | |||
| 25c337f3d8 | |||
| a3af71d840 | |||
| 993674fd6f | |||
| 84ac02e9f3 | |||
| d42e0c1b05 | |||
| 6ba2b1502b | |||
| 225873e47e | |||
| 758823c80e | |||
| 5185c03fa7 | |||
| 79d4f46efd | |||
| 290e0f7de5 | |||
| d49ba78dbd | |||
| b891579e99 | |||
| e43abfe8b8 | |||
| efd261ab7d | |||
| ee02ba0623 | |||
| 54f6d895a8 | |||
| 1a0c9fd279 | |||
| 82066dbce7 | |||
| 9aca8183fe | |||
| 5a1b6a9d50 | |||
| c252deeda9 | |||
| d640a60f96 | |||
| cf6c8a5acd | |||
| cddd41f5fd | |||
| 98aa5b1ac3 | |||
| bb2179cbb7 | |||
| 96006635fc | |||
| 75b5b6d5b9 | |||
| d54ab79285 | |||
| 21f1221056 | |||
| 551680435a | |||
| dbab6200d5 | |||
| a2267a3c5b | |||
| c13f7f9a6b | |||
| acfb9efa25 | |||
| 420983c00d | |||
| 4414c75992 | |||
| fd29b873cb | |||
| 61bc167f53 | |||
| 1ca90a1695 | |||
| 8e2798ff18 | |||
| 0ac84efc5a | |||
| a39608dd0d | |||
| df9a771afd | |||
| 01ab57bd28 | |||
| cec29305f2 | |||
| 85477f7928 | |||
| 8ead95fabe | |||
| 176eb618fc | |||
| 5c4359353f | |||
| 468ee496ca | |||
| 82990d7a3f | |||
| 6804470b3f | |||
| 413b603923 | |||
| 6c557376fc | |||
| 5060d6edc2 | |||
| bbf242efb0 | |||
| 2ecd91a0f2 | |||
| ba997dd6a0 | |||
| 5642f070db | |||
| 9c77b530df | |||
| 4191c518b2 | |||
| 1130c3c417 | |||
| f123859059 | |||
| ecc79c2241 | |||
| 18a4d9349c | |||
| 93692e074a | |||
| 157e786818 | |||
| 70a6cc1bd1 | |||
| 6c241fb61e | |||
| fa6dc6a983 | |||
| f85895edb5 | |||
| cbf63fe289 | |||
| 93e5e866c7 | |||
| 902e58214b | |||
| 2a8cb44f05 | |||
| 4d80c59921 | |||
| 6816b2e4d8 | |||
| c33ce32022 | |||
| 4aca8df6ab | |||
| 1f0d1abf15 | |||
| aaf1110d92 | |||
| f4a5746e57 | |||
| 752dafa55c | |||
| a242d3e3ba | |||
| 16a42b3f11 | |||
| 28b3bf9d3e | |||
| 2acc32ab82 | |||
| ec05eeadb2 | |||
| 15e8eee42e | |||
| c88858d3bd | |||
| a1d6fd2a13 | |||
| 300a3bd363 | |||
| 8c732857bc | |||
| e33c67eb35 | |||
| d1667406d7 | |||
| 45ef3ffd66 | |||
| 37551217f2 | |||
| d1f1b69a57 | |||
| c4a024d9fc | |||
| e42fd050f9 | |||
| 8044f8a71a | |||
| a89035fcb3 | |||
| 17d3aa1dff | |||
| cc5e92929a | |||
| ff962e0e48 | |||
| babb99fbd9 | |||
| ee5424df55 | |||
| fb6126adb2 | |||
| eb7930a5f4 | |||
| 2166675df5 | |||
| ec31b667b2 | |||
| a757acbc40 | |||
| 25e17b9b5d | |||
| 8688e7aff5 | |||
| f82ba2ed50 | |||
| a1dff55d44 | |||
| b0f7f0c808 | |||
| eca105902f | |||
| 1f4d891995 | |||
| 534a7a869e | |||
| 14a019dfee | |||
| 99b6bd212a | |||
| 75a69bae10 | |||
| 08707e95d2 | |||
| 870f2a938b | |||
| 5a6729743f | |||
| 806a85f19d | |||
| 4e6a3cedf6 | |||
| e32b77b7a3 | |||
| 938d0763f6 | |||
| 11312f9ea4 | |||
| 938dc74b3b | |||
| 9b983fb849 | |||
| 9a065268c6 | |||
| 6f8a609708 | |||
| d22e6dc827 | |||
| 33b875ccd0 | |||
| 8aadb679dc | |||
| 276b1bcda8 | |||
| 05abfdab8d | |||
| 30b0cfafc3 | |||
| c4bece6ed7 | |||
| a92ed6b506 | |||
| c52dbb8dfc | |||
| 619b23000a | |||
| f651fc4c8a | |||
| 182561953b | |||
| bd1220dbe5 | |||
| a3eb28f51d | |||
| 83ce477051 | |||
| 535836fdbd | |||
| c7f15300f3 | |||
| 5716a2c454 | |||
| 373b0fe431 | |||
| b9ce8595cb | |||
| ccc8e283a8 | |||
| da3b20ce14 | |||
| 9a279bae03 | |||
| ed815ecf42 | |||
| 2689b415bf | |||
| 9c22c9c0e8 | |||
| 18e0228d64 | |||
| 89e28a26f4 | |||
| 4ff8b8354f | |||
| 4bf24f8869 | |||
| fd31818cd9 | |||
| 9fa3fc8fa6 | |||
| 598064e4d9 | |||
| a96a428c7d | |||
| 4e64550116 | |||
| f330e8146f | |||
| 5b0902eb06 | |||
| 9aa9e5f1e6 | |||
| c718c85046 | |||
| fe6dc7ff41 | |||
| 9a1d611089 | |||
| 393e43991f | |||
| e72e2edba5 | |||
| 783f714bbe | |||
| d986d964b3 | |||
| 7f1103b12d | |||
| ef1938e622 | |||
| 15fd9288ae | |||
| 26b09187b8 | |||
| f3e7a7f813 | |||
| 5a9347a0d3 | |||
| 738c06f7f2 | |||
| 25cfc3c9f2 | |||
| 5dd88c28d7 | |||
| 90426f3906 | |||
| 357073866e | |||
| 3e144d2a0b | |||
| 879dad536b | |||
| d80294e172 | |||
| 256926ba71 | |||
| 4423aca97f | |||
| a5a9583f4d | |||
| fd4c40c893 | |||
| 15ceddf963 | |||
| d1479874a9 | |||
| 44ef640fb0 | |||
| 04a448a2ab | |||
| b00782a63b | |||
| 45008c77e8 | |||
| 2612f3771d | |||
| 5f72b91962 | |||
| 565a4974e3 | |||
| bbe8f2b9a7 | |||
| 5724c6a382 | |||
| b07642e882 | |||
| b77d0239b9 | |||
| 654c27eece | |||
| f0f4003c9a | |||
| 6a5085ce04 | |||
| 2700ab11c7 | |||
| c9585b3ed9 | |||
| f4b63de27b | |||
| 9ab2003217 | |||
| 970ffe0417 | |||
| 7b24d50738 | |||
| d377bf6b78 | |||
| 1126438d1a | |||
| 6805a7ae33 | |||
| 50d41b6804 | |||
| 75a54d45b6 | |||
| 63e098d9e9 | |||
| f752683a47 | |||
| f14cb06317 | |||
| f4d5e50005 | |||
| 63492d72ff | |||
| 6059688bb8 | |||
| e5da091061 | |||
| cab24c1838 | |||
| dde44877bc | |||
| 7db4a93901 | |||
| 5e5de409b5 | |||
| a5d8388b33 | |||
| f74f0c5c10 | |||
| 84d08b1651 | |||
| 71433c086b | |||
| a449ad1ae0 | |||
| e3f5f18813 | |||
| d2a670ec68 | |||
| 75baf79c47 | |||
| a8265dc500 | |||
| 999f67d464 | |||
| 7ff7e29b85 | |||
| c847b6d725 | |||
| cd227ab8ec | |||
| 476c26b2db | |||
| 1ac49204f8 | |||
| 53dde4e6a8 | |||
| 261c5a765f | |||
| 56ff846bbb | |||
| 71fda60d39 | |||
| 62064c3c86 | |||
| 9c4055943e | |||
| 2dc0582f5e | |||
| a8dd761083 | |||
| 8c82cc367c | |||
| 7556fc7c1d | |||
| 0c595030cc | |||
| 47ee1ee0d8 | |||
| 3c36b02b17 | |||
| 220732372a | |||
| 9691a7b331 | |||
| 6bd1059042 | |||
| a6e3b62bbb | |||
| 0788bf7d62 | |||
| 04658e6111 | |||
| 6604114571 | |||
| 2d553d9a11 | |||
| 64ff6f4e12 | |||
| d6efe5c428 | |||
| 9a01bf5dac | |||
| 2ad7c452fa | |||
| dabe4c6a8a | |||
| 68b632949d | |||
| 24ec2b70d4 | |||
| c85a85e1d6 | |||
| 47890d6b75 | |||
| 184523e238 | |||
| 61e908f935 | |||
| 3f86f7028c | |||
| 269569f55a | |||
| 0e253a518f | |||
| b10488d134 | |||
| e4f5c4127b | |||
| cf15e751c2 | |||
| 492e9c9d54 | |||
| 094c79b550 | |||
| c2707068a0 | |||
| e260a4f35e | |||
| 2b2f40f423 | |||
| 7961c01b98 | |||
| 98b0636874 | |||
| 4b28673d1f | |||
| d349c4d0da | |||
| 06544c7721 | |||
| 0255dcd2f4 | |||
| 1e1576a246 | |||
| 8e8c8d86cc | |||
| 035ec42e9f | |||
| b10116c72c | |||
| fabbaee93a | |||
| 041ecef9fe | |||
| 977a542c89 | |||
| 9306db49a4 | |||
| dc4ab86218 | |||
| 8f1c6d1220 | |||
| e76269f9cb | |||
| afb0530bb6 | |||
| 31fcc3c3ab | |||
| b3228c00da | |||
| 1fec48ab2e | |||
| 6183e9ddd3 | |||
| 1543ab4674 | |||
| 3b1275cae4 | |||
| 7f6770c339 | |||
| 4e17f43808 | |||
| f9c16916dc | |||
| 86c3578bce | |||
| 701377f9ab | |||
| 5a6d92259b | |||
| 2f96412939 | |||
| 71a15d2c3b | |||
| c5470497c3 | |||
| ccf8697cd9 | |||
| 73b73ac55c | |||
| 30df6463d7 | |||
| 8682e0f076 | |||
| aa7b52f84b | |||
| 3add914c7c | |||
| e6bd5804b9 | |||
| b2882f8ac3 | |||
| 5169acea00 | |||
| e133eec60b | |||
| 094c860a2e | |||
| d3f01844da | |||
| 7f2a97e632 | |||
| 2a512be9f6 | |||
| 3b643e9cad | |||
| cc446ad1ec | |||
| 7251f154ef | |||
| e6e07e0960 | |||
| 51d52d273c | |||
| 67c44ebd2e | |||
| eeaf8e0e62 | |||
| 651a6bf5fa | |||
| 7135dc455a | |||
| c6b033233f | |||
| cd7201ad49 | |||
| 15a4027603 | |||
| d6eb0134af | |||
| 7505c8ce76 | |||
| aeb959f07c | |||
| 90e5c2b889 | |||
| 84a9a24b96 | |||
| 78255acbf9 | |||
| 34af3ee683 | |||
| ae422c191c | |||
| 8e77498176 | |||
| 89fb92a551 | |||
| a624a36856 | |||
| 56cfcc65cb | |||
| efb7b7d668 | |||
| 2f5e4bc739 | |||
| f21f69f0b8 | |||
| fda8d5d13d | |||
| ea12d48932 | |||
| 383af5dab9 | |||
| f1cf68fedc | |||
| 7fdc181bfe | |||
| 0422d333a8 | |||
| f857f28d7a | |||
| c777c77434 | |||
| e2d5d5457f | |||
| 4802739d19 | |||
| 01087d1e45 | |||
| f30469004a | |||
| e34df5a1c4 | |||
| dd3e9a0c25 | |||
| f246b1c05c | |||
| 2288e60615 | |||
| f951911682 | |||
| ad262b981f | |||
| da4ffac0c9 | |||
| 702c4b4781 | |||
| fedba1fcc4 | |||
| de96ddee74 | |||
| 7b6a902ccd | |||
| a0a03baad8 | |||
| e37073a24b | |||
| 9f59264afa | |||
| 7f4be41ed2 | |||
| 3dec6b78ce | |||
| e3e41170fe | |||
| 7c6be5b319 | |||
| 0613e56e69 | |||
| a3065e8220 | |||
| f496b55189 | |||
| 135461b4a9 | |||
| 8253f41405 | |||
| cf0b79a80f | |||
| 1a8dc02878 | |||
| 5b3ec84495 | |||
| 0608b35a7b | |||
| abb5f229b1 | |||
| e2fd22fadd | |||
| 18c824af1f | |||
| 37ce725987 | |||
| 8ac6793716 | |||
| 6022706b47 | |||
| 250ea6a145 | |||
| 039096d1b1 | |||
| 0ca3442925 | |||
| 1a4626d9fa | |||
| fa0b5768cc | |||
| 7b33c5e9ec | |||
| d394a22ee6 | |||
| 83fe3c44c0 | |||
| 282c4224da | |||
| 1312197a82 | |||
| 5dc6fd3b23 | |||
| b49b76f397 | |||
| a4f4656110 | |||
| 83bdb872dc | |||
| 39a83aa250 | |||
| e70853386d | |||
| 451d514d84 | |||
| 72f5a6e119 | |||
| 7f624852e4 | |||
| 8abe8980d6 | |||
| 431c7ba150 | |||
| 9b632e17d8 | |||
| b731ea9798 | |||
| 472b1b095e | |||
| 2cddf32f7e | |||
| 9b898bf8b4 | |||
| a87d3a64bb | |||
| 55cd43d397 | |||
| 9c1462ecfe | |||
| e22aba78ca | |||
| d952f227d0 | |||
| d80cec9c2b | |||
| 77fe56108a | |||
| 03550b7b8f | |||
| 274ccc948b | |||
| bc8d5da483 | |||
| 94bf3e9c78 | |||
| e92bd648db | |||
| e4bb1cc603 | |||
| c2f12aa742 | |||
| 454af7cf52 | |||
| f73e64f166 | |||
| 18a738f88f | |||
| 493a30cdc2 | |||
| 65edc59a51 | |||
| 28b9209af5 | |||
| 8b2c91e627 | |||
| 8256d18453 | |||
| ab11164395 | |||
| 29933a945f | |||
| c7b384ce66 | |||
| 665e256961 | |||
| 879532b41f | |||
| 4e83a79cd5 | |||
| 3ecba892e3 | |||
| b92f4ba9f1 | |||
| 6a3a83af93 | |||
| 6e3a070030 | |||
| 5d701a706c | |||
| 612e4d9d02 | |||
| df6f010a86 | |||
| bf4904c32b | |||
| 2821f78e6c | |||
| f904215fa7 | |||
| 635b605eab | |||
| 08dd41b304 | |||
| 04cd0cb2ca | |||
| 5cacda33bb | |||
| 864ef22534 | |||
| f008a03c60 | |||
| 8831cc14c7 | |||
| ebb87a1e63 | |||
| 93d9b19607 | |||
| 92d04a7913 | |||
| a5cdde3c79 | |||
| d073f5daf0 | |||
| 3972ef0a32 | |||
| 01b63287ef | |||
| b3259b51bb | |||
| b683a48f69 | |||
| 944888a509 | |||
| 21ac04e12e | |||
| ab9b65b6a7 |
@@ -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/>.
|
||||
|
||||
/**
|
||||
* Template configuraton file for github actions CI/CD.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 onwards Eloy Lafuente (stronk7) {@link https://stronk7.com}
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
// This cannot be used out from a github actions workflow, so just exit.
|
||||
getenv('GITHUB_WORKFLOW') || die; // phpcs:ignore moodle.Files.MoodleInternal.MoodleInternalGlobalState
|
||||
|
||||
unset($CFG);
|
||||
global $CFG;
|
||||
$CFG = new stdClass();
|
||||
|
||||
$CFG->dbtype = getenv('dbtype');
|
||||
$CFG->dblibrary = 'native';
|
||||
$CFG->dbhost = '127.0.0.1';
|
||||
$CFG->dbname = 'test';
|
||||
$CFG->dbuser = 'test';
|
||||
$CFG->dbpass = 'test';
|
||||
$CFG->prefix = 'm_';
|
||||
$CFG->dboptions = ['dbcollation' => 'utf8mb4_bin'];
|
||||
|
||||
$host = 'localhost';
|
||||
$CFG->wwwroot = "http://{$host}";
|
||||
$CFG->dataroot = realpath(dirname(__DIR__)) . '/moodledata';
|
||||
$CFG->admin = 'admin';
|
||||
$CFG->directorypermissions = 0777;
|
||||
|
||||
// Debug options - possible to be controlled by flag in future.
|
||||
$CFG->debug = (E_ALL | E_STRICT); // DEBUG_DEVELOPER.
|
||||
$CFG->debugdisplay = 1;
|
||||
$CFG->debugstringids = 1; // Add strings=1 to url to get string ids.
|
||||
$CFG->perfdebug = 15;
|
||||
$CFG->debugpageinfo = 1;
|
||||
$CFG->allowthemechangeonurl = 1;
|
||||
$CFG->passwordpolicy = 0;
|
||||
$CFG->cronclionly = 0;
|
||||
$CFG->pathtophp = getenv('pathtophp');
|
||||
|
||||
$CFG->phpunit_dataroot = realpath(dirname(__DIR__)) . '/phpunitdata';
|
||||
$CFG->phpunit_prefix = 't_';
|
||||
|
||||
define('TEST_EXTERNAL_FILES_HTTP_URL', 'http://localhost:8080');
|
||||
define('TEST_EXTERNAL_FILES_HTTPS_URL', 'http://localhost:8080');
|
||||
|
||||
define('TEST_SESSION_REDIS_HOST', 'localhost');
|
||||
define('TEST_CACHESTORE_REDIS_TESTSERVERS', 'localhost');
|
||||
|
||||
// TODO: add others (solr, mongodb, memcached, ldap...).
|
||||
|
||||
// Too much for now: define('PHPUNIT_LONGTEST', true); // Only leaves a few tests out and they are run later by CI.
|
||||
|
||||
require_once(__DIR__ . '/lib/setup.php');
|
||||
@@ -0,0 +1,104 @@
|
||||
name: Core
|
||||
|
||||
on: [push]
|
||||
|
||||
env:
|
||||
php: 7.2
|
||||
|
||||
jobs:
|
||||
Grunt:
|
||||
runs-on: ubuntu-18.04
|
||||
|
||||
steps:
|
||||
- name: Checking out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Configuring node & npm
|
||||
shell: bash -l {0}
|
||||
run: nvm install
|
||||
|
||||
- name: Installing node stuff
|
||||
run: npm install
|
||||
|
||||
- name: Running grunt
|
||||
run: npx grunt
|
||||
|
||||
- name: Looking for uncommitted changes
|
||||
# Add all files to the git index and then run diff --cached to see all changes.
|
||||
# This ensures that we get the status of all files, including new files.
|
||||
# We ignore npm-shrinkwrap.json to make the tasks immune to npm changes.
|
||||
run: |
|
||||
git add .
|
||||
git reset -- npm-shrinkwrap.json
|
||||
git diff --cached --exit-code
|
||||
|
||||
PHPUnit:
|
||||
runs-on: ${{ matrix.os }}
|
||||
services:
|
||||
exttests:
|
||||
image: moodlehq/moodle-exttests
|
||||
ports:
|
||||
- 8080:80
|
||||
redis:
|
||||
image: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
php: 7.0
|
||||
db: mysqli
|
||||
- os: ubuntu-18.04
|
||||
php: 7.2
|
||||
db: pgsql
|
||||
|
||||
steps:
|
||||
- name: Setting up DB mysql
|
||||
if: ${{ matrix.db == 'mysqli' }}
|
||||
uses: johanmeiring/mysql-action@tmpfs-patch
|
||||
with:
|
||||
collation server: utf8mb4_bin
|
||||
mysql version: 5.7
|
||||
mysql database: test
|
||||
mysql user: test
|
||||
mysql password: test
|
||||
use tmpfs: true
|
||||
|
||||
- name: Setting up DB pgsql
|
||||
if: ${{ matrix.db == 'pgsql' }}
|
||||
uses: m4nu56/postgresql-action@v1
|
||||
with:
|
||||
postgresql version: 9.6
|
||||
postgresql db: test
|
||||
postgresql user: test
|
||||
postgresql password: test
|
||||
|
||||
- name: Configuring git vars
|
||||
uses: rlespinasse/github-slug-action@v3.x
|
||||
|
||||
- name: Setting up PHP ${{ matrix.php }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: mysqli, zip, gd, xmlrpc, soap
|
||||
coverage: none
|
||||
|
||||
- name: Checking out code from ${{ env.GITHUB_REF_SLUG }}
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setting up PHPUnit
|
||||
env:
|
||||
dbtype: ${{ matrix.db }}
|
||||
run: |
|
||||
echo "pathtophp=$(which php)" >> $GITHUB_ENV # Inject installed pathtophp to env. The template config needs it.
|
||||
cp .github/workflows/config-template.php config.php
|
||||
mkdir ../moodledata
|
||||
sudo locale-gen en_AU.UTF-8
|
||||
php admin/tool/phpunit/cli/init.php --no-composer-self-update
|
||||
|
||||
- name: Running PHPUnit tests
|
||||
env:
|
||||
dbtype: ${{ matrix.db }}
|
||||
run: vendor/bin/phpunit -v
|
||||
+44
-76
@@ -4,19 +4,16 @@
|
||||
|
||||
sudo: required
|
||||
|
||||
# We currently disable Travis notifications entirely until https://github.com/travis-ci/travis-ci/issues/4976
|
||||
# is fixed.
|
||||
notifications:
|
||||
email: false
|
||||
email:
|
||||
if: env(MOODLE_EMAIL) != no
|
||||
|
||||
language: php
|
||||
|
||||
dist: trusty
|
||||
|
||||
php:
|
||||
# We only run the highest and lowest supported versions to reduce the load on travis-ci.org.
|
||||
- 7.2
|
||||
- 7.0
|
||||
services:
|
||||
- docker
|
||||
|
||||
addons:
|
||||
postgresql: "9.6"
|
||||
@@ -29,49 +26,50 @@ addons:
|
||||
# services:
|
||||
# - redis-server
|
||||
|
||||
env:
|
||||
# Although we want to run these jobs and see failures as quickly as possible, we also want to get the slowest job to
|
||||
# start first so that the total run time is not too high.
|
||||
#
|
||||
# We only run MySQL on PHP 7.2, so run that first.
|
||||
# CI Tests should be second-highest in priority as these only take <= 60 seconds to run under normal circumstances.
|
||||
# Postgres is significantly is pretty reasonable in its run-time.
|
||||
|
||||
# Run unit tests on MySQL
|
||||
- DB=mysqli TASK=PHPUNIT
|
||||
|
||||
# Run CI Tests without running PHPUnit.
|
||||
- DB=none TASK=CITEST
|
||||
|
||||
# Run unit tests on Postgres
|
||||
- DB=pgsql TASK=PHPUNIT
|
||||
|
||||
# Perform an upgrade test too.
|
||||
- DB=pgsql TASK=UPGRADE
|
||||
|
||||
matrix:
|
||||
jobs:
|
||||
# Enable fast finish.
|
||||
# This will fail the build if a single job fails (except those in allow_failures).
|
||||
# It will not stop the jobs from running.
|
||||
fast_finish: true
|
||||
|
||||
include:
|
||||
# Run grunt/npm install on highest version ('node' is an alias for the latest node.js version.)
|
||||
- php: 7.2
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='lts/carbon'
|
||||
# First all the lowest php ones (7.0)
|
||||
- php: 7.0
|
||||
env: DB=none TASK=CITEST
|
||||
- php: 7.0
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='lts/carbon'
|
||||
|
||||
exclude:
|
||||
# MySQL - it's just too slow.
|
||||
# Exclude it on all versions except for 7.2
|
||||
|
||||
- env: DB=mysqli TASK=PHPUNIT
|
||||
- if: env(MOODLE_DATABASE) = "pgsql" OR env(MOODLE_DATABASE) = "all" OR env(MOODLE_DATABASE) IS NOT present
|
||||
php: 7.0
|
||||
env: DB=pgsql TASK=PHPUNIT
|
||||
|
||||
- if: env(MOODLE_DATABASE) = "mysqli" OR env(MOODLE_DATABASE) = "all"
|
||||
php: 7.0
|
||||
env: DB=mysqli TASK=PHPUNIT
|
||||
|
||||
- if: env(MOODLE_PHP) = "all"
|
||||
php: 7.2
|
||||
env: DB=none TASK=CITEST
|
||||
- if: env(MOODLE_PHP) = "all"
|
||||
php: 7.2
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='lts/carbon'
|
||||
|
||||
- if: env(MOODLE_PHP) = "all" AND (env(MOODLE_DATABASE) = "pgsql" OR env(MOODLE_DATABASE) = "all" OR env(MOODLE_DATABASE) IS NOT present)
|
||||
php: 7.2
|
||||
env: DB=pgsql TASK=PHPUNIT
|
||||
|
||||
- if: env(MOODLE_PHP) = "all" AND (env(MOODLE_DATABASE) = "mysqli" OR env(MOODLE_DATABASE) = "all")
|
||||
php: 7.2
|
||||
env: DB=mysqli TASK=PHPUNIT
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
- $HOME/.npm
|
||||
|
||||
before_install:
|
||||
- docker run -d -p 127.0.0.1:8080:80 --name exttests moodlehq/moodle-exttests
|
||||
|
||||
install:
|
||||
- >
|
||||
if [ "$DB" = 'mysqli' ];
|
||||
@@ -122,7 +120,7 @@ install:
|
||||
before_script:
|
||||
- phpenv config-rm xdebug.ini
|
||||
- >
|
||||
if [ "$TASK" = 'PHPUNIT' -o "$TASK" = 'UPGRADE' ];
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
then
|
||||
# Copy generic configuration in place.
|
||||
cp config-dist.php config.php ;
|
||||
@@ -181,6 +179,11 @@ before_script:
|
||||
-e "/require_once/i \\\$CFG->phpunit_dataroot = '\/home\/travis\/roots\/phpunit';" \
|
||||
-e "/require_once/i \\\$CFG->phpunit_prefix = 'p_';" \
|
||||
config.php ;
|
||||
# Enable test external resources
|
||||
sed -i \
|
||||
-e "/require_once/i \\define('TEST_EXTERNAL_FILES_HTTP_URL', 'http://127.0.0.1:8080');" \
|
||||
-e "/require_once/i \\define('TEST_EXTERNAL_FILES_HTTPS_URL', 'http://127.0.0.1:8080');" \
|
||||
config.php ;
|
||||
|
||||
# Initialise PHPUnit for Moodle.
|
||||
php admin/tool/phpunit/cli/init.php
|
||||
@@ -220,32 +223,6 @@ before_script:
|
||||
export phpcmd=`which php`;
|
||||
fi
|
||||
|
||||
########################################################################
|
||||
# Upgrade test
|
||||
########################################################################
|
||||
- >
|
||||
if [ "$TASK" = 'UPGRADE' ];
|
||||
then
|
||||
# We need the official upstream.
|
||||
git remote add upstream https://github.com/moodle/moodle.git;
|
||||
|
||||
# Checkout 30 STABLE branch (the first version compatible with PHP 7.x)
|
||||
git fetch upstream MOODLE_30_STABLE;
|
||||
git checkout MOODLE_30_STABLE;
|
||||
|
||||
# Perform the upgrade
|
||||
php admin/cli/install_database.php --agree-license --adminpass=Password --adminemail=admin@example.com --fullname="Upgrade test" --shortname=Upgrade;
|
||||
|
||||
# Return to the previous commit
|
||||
git checkout -;
|
||||
|
||||
# Perform the upgrade
|
||||
php admin/cli/upgrade.php --non-interactive --allow-unstable ;
|
||||
|
||||
# The local_ci repository can be used to check upgrade savepoints.
|
||||
git clone https://github.com/moodlehq/moodle-local_ci.git local/ci ;
|
||||
fi
|
||||
|
||||
script:
|
||||
- >
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
@@ -271,19 +248,10 @@ script:
|
||||
git diff --cached --exit-code ;
|
||||
fi
|
||||
|
||||
########################################################################
|
||||
# Upgrade test
|
||||
########################################################################
|
||||
after_script:
|
||||
- >
|
||||
if [ "$TASK" = 'UPGRADE' ];
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
then
|
||||
cp local/ci/check_upgrade_savepoints/check_upgrade_savepoints.php ./check_upgrade_savepoints.php
|
||||
result=`php check_upgrade_savepoints.php`;
|
||||
# Check if there are problems
|
||||
count=`echo "$result" | grep -P "ERROR|WARN" | wc -l` ;
|
||||
if (($count > 0));
|
||||
then
|
||||
echo "$result"
|
||||
exit 1 ;
|
||||
fi
|
||||
EXTTESTS_HITS=$(docker logs exttests 2>&1 | grep -Fv -e 'AH00558' -e '[pid 1]' | wc -l)
|
||||
echo -e "\nTest local resources number of hits: ${EXTTESTS_HITS}.\n"
|
||||
fi
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ MOODLE TRADEMARKS POLICY
|
||||
========================
|
||||
|
||||
The word "Moodle" is trademarked in many countries around the world. The word
|
||||
was originally an acronym: Modular Object-Oriented Dynamic Learning
|
||||
was originally coined as an acronym: Modular Object-Oriented Dynamic Learning
|
||||
Environment. The trademark is owned by Martin Dougiamas, Founder and Director
|
||||
of Moodle.
|
||||
|
||||
|
||||
@@ -2451,6 +2451,7 @@
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.0.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_74" message="unsupportedphpversion74" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
@@ -2632,6 +2633,7 @@
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.1.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_74" message="unsupportedphpversion74" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
@@ -2813,6 +2815,7 @@
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.1.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_80" message="unsupportedphpversion80" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
@@ -2980,4 +2983,552 @@
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.9" requires="3.5">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="10.2.29" />
|
||||
<VENDOR name="mysql" version="5.6" />
|
||||
<VENDOR name="postgres" version="9.5" />
|
||||
<VENDOR name="mssql" version="11.0" />
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.2.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_80" message="unsupportedphpversion80" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="mbstringrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.10" requires="3.5">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="10.2.29" />
|
||||
<VENDOR name="mysql" version="5.7" />
|
||||
<VENDOR name="postgres" version="9.6" />
|
||||
<VENDOR name="mssql" version="11.0" />
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.2.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_80" message="unsupportedphpversion80" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="mbstringrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.11" requires="3.6">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="10.2.29" />
|
||||
<VENDOR name="mysql" version="5.7" />
|
||||
<VENDOR name="postgres" version="9.6" />
|
||||
<VENDOR name="mssql" version="14.0" />
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.3.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="mbstringrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
<PHP_EXTENSION name="sodium" level="optional"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_max_input_vars" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
@@ -98,7 +98,10 @@ $options = array(
|
||||
GETREMOTEADDR_SKIP_HTTP_CLIENT_IP => 'HTTP_X_FORWARDED_FOR, REMOTE_ADDR',
|
||||
GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR => 'HTTP_CLIENT, REMOTE_ADDR',
|
||||
GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR|GETREMOTEADDR_SKIP_HTTP_CLIENT_IP => 'REMOTE_ADDR');
|
||||
$temp->add(new admin_setting_configselect('getremoteaddrconf', new lang_string('getremoteaddrconf', 'admin'), new lang_string('configgetremoteaddrconf', 'admin'), 0, $options));
|
||||
$temp->add(new admin_setting_configselect('getremoteaddrconf', new lang_string('getremoteaddrconf', 'admin'),
|
||||
new lang_string('configgetremoteaddrconf', 'admin'),
|
||||
GETREMOTEADDR_SKIP_DEFAULT, $options));
|
||||
$temp->add(new admin_setting_configtext('reverseproxyignore', new lang_string('reverseproxyignore', 'admin'), new lang_string('configreverseproxyignore', 'admin'), ''));
|
||||
|
||||
$temp->add(new admin_setting_heading('webproxy', new lang_string('webproxy', 'admin'), new lang_string('webproxyinfo', 'admin')));
|
||||
$temp->add(new admin_setting_configtext('proxyhost', new lang_string('proxyhost', 'admin'), new lang_string('configproxyhost', 'admin'), '', PARAM_HOST));
|
||||
|
||||
@@ -48,7 +48,6 @@ class behat_admin extends behat_base {
|
||||
* @param TableNode $table
|
||||
*/
|
||||
public function i_set_the_following_administration_settings_values(TableNode $table) {
|
||||
|
||||
if (!$data = $table->getRowsHash()) {
|
||||
return;
|
||||
}
|
||||
@@ -57,17 +56,12 @@ class behat_admin extends behat_base {
|
||||
|
||||
// We expect admin block to be visible, otherwise go to homepage.
|
||||
if (!$this->getSession()->getPage()->find('css', '.block_settings')) {
|
||||
$this->getSession()->visit($this->locate_path('/'));
|
||||
$this->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
|
||||
$this->execute('behat_forms::i_am_on_homepage');
|
||||
}
|
||||
|
||||
// Search by label.
|
||||
$searchbox = $this->find_field(get_string('searchinsettings', 'admin'));
|
||||
$searchbox->setValue($label);
|
||||
$submitsearch = $this->find('css', 'form.adminsearchform input[type=submit]');
|
||||
$submitsearch->press();
|
||||
|
||||
$this->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
|
||||
$this->execute('behat_forms::i_set_the_field_to', [get_string('searchinsettings', 'admin'), $label]);
|
||||
$this->execute("behat_general::i_click_on_in_the", [get_string('search', 'admin'), 'button', '.block_settings', 'css_element']);
|
||||
|
||||
// Admin settings does not use the same DOM structure than other moodle forms
|
||||
// but we also need to use lib/behat/form_field/* to deal with the different moodle form elements.
|
||||
@@ -83,35 +77,17 @@ class behat_admin extends behat_base {
|
||||
"@id=//span[contains(normalize-space(.), $label)]/preceding-sibling::label[1]/@for]";
|
||||
$fieldnode = $this->find('xpath', $fieldxpath, $exception);
|
||||
|
||||
$formfieldtypenode = $this->find('xpath', $fieldxpath . "/ancestor::div[@class='form-setting']" .
|
||||
"/child::div[contains(concat(' ', @class, ' '), ' form-')]/child::*/parent::div");
|
||||
|
||||
} catch (ElementNotFoundException $e) {
|
||||
|
||||
// Multi element settings, interacting only the first one.
|
||||
$fieldxpath = "//*[label[normalize-space(.)= $label]|span[normalize-space(.)= $label]]/" .
|
||||
"ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' form-item ')]" .
|
||||
"/descendant::div[@class='form-group']/descendant::*[self::input | self::textarea | self::select]" .
|
||||
"[not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]";
|
||||
$fieldnode = $this->find('xpath', $fieldxpath);
|
||||
|
||||
// It is the same one that contains the type.
|
||||
$formfieldtypenode = $fieldnode;
|
||||
}
|
||||
|
||||
// Getting the class which contains the field type.
|
||||
$classes = explode(' ', $formfieldtypenode->getAttribute('class'));
|
||||
foreach ($classes as $class) {
|
||||
if (substr($class, 0, 5) == 'form-') {
|
||||
$type = substr($class, 5);
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiating the appropiate field type.
|
||||
$field = behat_field_manager::get_field_instance($type, $fieldnode, $this->getSession());
|
||||
$field->set_value($value);
|
||||
|
||||
$this->find_button(get_string('savechanges'))->press();
|
||||
$this->execute('behat_forms::i_set_the_field_with_xpath_to', [$fieldxpath, $value]);
|
||||
$this->execute("behat_general::i_click_on", [get_string('savechanges'), 'button']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -164,6 +164,8 @@ if ($options['install']) {
|
||||
mtrace("Acceptance tests site installed");
|
||||
}
|
||||
|
||||
// Note: Do not build the themes here. This is done during the 'enable' stage.
|
||||
|
||||
} else if ($options['drop']) {
|
||||
// Ensure no tests are running.
|
||||
test_lock::acquire('behat');
|
||||
@@ -182,6 +184,10 @@ if ($options['install']) {
|
||||
// Enable test mode.
|
||||
behat_util::start_test_mode($options['add-core-features-to-theme'], $options['optimize-runs'], $parallel, $run);
|
||||
|
||||
// Themes are only built in the 'enable' command.
|
||||
behat_util::build_themes();
|
||||
mtrace("Testing environment themes built");
|
||||
|
||||
// This is only displayed once for parallel install.
|
||||
if (empty($run)) {
|
||||
// Notify user that 2.5 profile has been converted to 3.5.
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
@tool_behat
|
||||
Feature: Behat steps for interacting with form work
|
||||
In order to test my Moodle code
|
||||
As a developer
|
||||
I need the Behat steps for form elements to work reliably
|
||||
|
||||
@javascript
|
||||
Scenario: Test fields in containers
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
When I log in as "admin"
|
||||
And I am on "Course 1" course homepage
|
||||
# Just get to any form.
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I set the field "Course full name" in the "General" "fieldset" to "Frog"
|
||||
And I set the following fields in the "Appearance" "fieldset" to these values:
|
||||
| Show activity reports | Yes |
|
||||
| Number of announcements | 1 |
|
||||
Then the field "Show activity reports" in the "Appearance" "fieldset" matches value "Yes"
|
||||
And the field "Show activity reports" in the "Appearance" "fieldset" does not match value "No"
|
||||
And the following fields in the "region-main" "region" match these values:
|
||||
| Course full name | Frog |
|
||||
| Number of announcements | 1 |
|
||||
And the following fields in the "region-main" "region" do not match these values:
|
||||
| Course full name | Course 1 |
|
||||
| Number of announcements | 5 |
|
||||
@@ -0,0 +1,30 @@
|
||||
@tool_behat
|
||||
Feature: Verify that the inplace editable field works as expected
|
||||
In order to use behat step definitions
|
||||
As a test write
|
||||
I need to ensure that the inplace editable works in forms
|
||||
|
||||
Background:
|
||||
Given the following "course" exists:
|
||||
| fullname | Course 1 |
|
||||
| shortname | C1 |
|
||||
And the following "activities" exist:
|
||||
| activity | course | name | idnumber |
|
||||
| forum | C1 | My first forum | forum1 |
|
||||
| assign | C1 | My first assignment | assign1 |
|
||||
| quiz | C1 | My first quiz | quiz1 |
|
||||
And I log in as "admin"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
|
||||
@javascript
|
||||
Scenario: Using an inplace editable updates the name of an activity
|
||||
When I set the field "Edit title" in the "My first assignment" "activity" to "Coursework submission"
|
||||
Then I should see "Coursework submission"
|
||||
And I should not see "My first assignment"
|
||||
But I should see "My first forum"
|
||||
And I should see "My first quiz"
|
||||
And I set the field "Edit title" in the "Coursework submission" "activity" to "My first assignment"
|
||||
And I should not see "Coursework submission"
|
||||
But I should see "My first assignment"
|
||||
And I should see "My first forum"
|
||||
And I should see "My first quiz"
|
||||
@@ -0,0 +1,42 @@
|
||||
@tool_behat
|
||||
Feature: Verify that keyboard steps work as expected
|
||||
In order to use behat step definitions
|
||||
As a test writer
|
||||
I need to verify that the keyboard steps work as expected
|
||||
|
||||
@javascript
|
||||
Scenario: Typing keys into a field causes them to be input
|
||||
Given the following "users" exist:
|
||||
| username | email | firstname | lastname | password |
|
||||
| saffronr | saffron.rutledge@example.com | Saffron | Rutledge | flowerpower |
|
||||
Given I click on "Log in" "link"
|
||||
And I click on "Username" "field"
|
||||
When I type "saffronr"
|
||||
And I press the tab key
|
||||
And I type "flowerpower"
|
||||
And I press enter
|
||||
Then I should see "You are logged in as Saffron Rutledge"
|
||||
|
||||
@javascript
|
||||
Scenario: Using tab changes focus to the next or previous field
|
||||
Given I click on "Log in" "link"
|
||||
And I click on "Username" "field"
|
||||
And the focused element is "Username" "field"
|
||||
When I press the tab key
|
||||
Then the focused element is "Password" "field"
|
||||
|
||||
And I press the shift tab key
|
||||
And the focused element is "Username" "field"
|
||||
|
||||
@javascript
|
||||
Scenario: The escape key can be used to close a dialogue
|
||||
Given the following "course" exists:
|
||||
| fullname | C1|
|
||||
| shortname | C1 |
|
||||
And I log in as "admin"
|
||||
And I am on "C1" course homepage
|
||||
And I navigate to course participants
|
||||
And I press "Enrol users"
|
||||
And "Enrol users" "dialogue" should be visible
|
||||
When I press the escape key
|
||||
Then "Enrol users" "dialogue" should not be visible
|
||||
@@ -222,6 +222,48 @@ class api {
|
||||
}
|
||||
}
|
||||
|
||||
// Clean the legacy role assignments which are stale.
|
||||
$paramsclean['usercontext'] = CONTEXT_USER;
|
||||
$paramsclean['component'] = 'tool_cohortroles';
|
||||
$sql = 'SELECT DISTINCT(ra.id), ra.roleid, ra.userid, ra.contextid, ctx.instanceid
|
||||
FROM {role_assignments} ra
|
||||
JOIN {context} ctx ON ctx.id = ra.contextid AND ctx.contextlevel = :usercontext
|
||||
JOIN {cohort_members} cm ON cm.userid = ctx.instanceid
|
||||
LEFT JOIN {tool_cohortroles} tc ON tc.cohortid = cm.cohortid
|
||||
AND tc.userid = ra.userid
|
||||
AND tc.roleid = ra.roleid
|
||||
WHERE ra.component = :component
|
||||
AND tc.id is null';
|
||||
if ($candidatelegacyassignments = $DB->get_records_sql($sql, $paramsclean)) {
|
||||
$sql = 'SELECT DISTINCT(ra.id)
|
||||
FROM {role_assignments} ra
|
||||
JOIN {context} ctx ON ctx.id = ra.contextid AND ctx.contextlevel = :usercontext
|
||||
JOIN {cohort_members} cm ON cm.userid = ctx.instanceid
|
||||
JOIN {tool_cohortroles} tc ON tc.cohortid = cm.cohortid AND tc.userid = ra.userid
|
||||
WHERE ra.component = :component';
|
||||
if ($currentvalidroleassignments = $DB->get_records_sql($sql, $paramsclean)) {
|
||||
foreach ($candidatelegacyassignments as $candidate) {
|
||||
if (!array_key_exists($candidate->id, $currentvalidroleassignments)) {
|
||||
role_unassign($candidate->roleid, $candidate->userid, $candidate->contextid, 'tool_cohortroles');
|
||||
$rolesremoved[] = array(
|
||||
'useridassignedto' => $candidate->userid,
|
||||
'useridassignedover' => $candidate->instanceid,
|
||||
'roleid' => $candidate->roleid
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($candidatelegacyassignments as $candidate) {
|
||||
role_unassign($candidate->roleid, $candidate->userid, $candidate->contextid, 'tool_cohortroles');
|
||||
$rolesremoved[] = array(
|
||||
'useridassignedto' => $candidate->userid,
|
||||
'useridassignedover' => $candidate->instanceid,
|
||||
'roleid' => $candidate->roleid
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array('rolesadded' => $rolesadded, 'rolesremoved' => $rolesremoved);
|
||||
}
|
||||
|
||||
|
||||
@@ -103,5 +103,4 @@ class cohort_role_assignment extends persistent {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,8 +50,6 @@ class tool_cohortroles_api_testcase extends advanced_testcase {
|
||||
* Setup function- we will create a course and add an assign instance to it.
|
||||
*/
|
||||
protected function setUp() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Create some users.
|
||||
@@ -133,14 +131,91 @@ class tool_cohortroles_api_testcase extends advanced_testcase {
|
||||
|
||||
public function test_delete_cohort_role_assignment() {
|
||||
$this->setAdminUser();
|
||||
$params = (object) array(
|
||||
// Create a cohort role assigment.
|
||||
$params = (object) [
|
||||
'userid' => $this->userassignto->id,
|
||||
'roleid' => $this->roleid,
|
||||
'cohortid' => $this->cohort->id
|
||||
);
|
||||
$result = api::create_cohort_role_assignment($params);
|
||||
$worked = api::delete_cohort_role_assignment($result->get('id'));
|
||||
$this->assertTrue($worked);
|
||||
];
|
||||
$cohortroleassignment = api::create_cohort_role_assignment($params);
|
||||
$sync = api::sync_all_cohort_roles();
|
||||
$rolesadded = [
|
||||
[
|
||||
'useridassignedto' => $this->userassignto->id,
|
||||
'useridassignedover' => $this->userassignover->id,
|
||||
'roleid' => $this->roleid
|
||||
]
|
||||
];
|
||||
$expected = [
|
||||
'rolesadded' => $rolesadded,
|
||||
'rolesremoved' => []
|
||||
];
|
||||
$this->assertEquals($sync, $expected);
|
||||
|
||||
// Delete the cohort role assigment and confirm the roles are removed.
|
||||
$result = api::delete_cohort_role_assignment($cohortroleassignment->get('id'));
|
||||
$this->assertTrue($result);
|
||||
$sync = api::sync_all_cohort_roles();
|
||||
$expected = [
|
||||
'rolesadded' => [],
|
||||
'rolesremoved' => $rolesadded
|
||||
];
|
||||
$this->assertEquals($expected, $sync);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test case verifying that syncing won't remove role assignments if they are valid for another cohort role assignment.
|
||||
*/
|
||||
public function test_delete_cohort_role_assignment_cohorts_having_same_members() {
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create 2 cohorts, with a 1 user (user1) present in both,
|
||||
// and user2 and user3 members of 1 cohort each.
|
||||
$cohort1 = $this->getDataGenerator()->create_cohort();
|
||||
$cohort2 = $this->getDataGenerator()->create_cohort();
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$user3 = $this->getDataGenerator()->create_user();
|
||||
cohort_add_member($cohort1->id, $user1->id);
|
||||
cohort_add_member($cohort1->id, $user2->id);
|
||||
cohort_add_member($cohort2->id, $user1->id);
|
||||
cohort_add_member($cohort2->id, $user3->id);
|
||||
|
||||
// And a role and a user to assign that role to.
|
||||
$user4 = $this->getDataGenerator()->create_user(); // A cohort manager, for example.
|
||||
$roleid = create_role('Role 1', 'myrole', 'test');
|
||||
|
||||
// Assign the role for user4 in both cohorts.
|
||||
$params = (object) [
|
||||
'userid' => $user4->id,
|
||||
'roleid' => $roleid,
|
||||
'cohortid' => $cohort1->id
|
||||
];
|
||||
$cohort1roleassignment = api::create_cohort_role_assignment($params);
|
||||
$params->cohortid = $cohort2->id;
|
||||
$cohort2roleassignment = api::create_cohort_role_assignment($params);
|
||||
|
||||
$sync = api::sync_all_cohort_roles();
|
||||
|
||||
// There is no guarantee about the order of roles assigned.
|
||||
// so confirm we have 3 role assignments, and they are for the users 1, 2 and 3.
|
||||
$this->assertCount(3, $sync['rolesadded']);
|
||||
$addedusers = array_column($sync['rolesadded'], 'useridassignedover');
|
||||
$this->assertContains($user1->id, $addedusers);
|
||||
$this->assertContains($user2->id, $addedusers);
|
||||
$this->assertContains($user3->id, $addedusers);
|
||||
|
||||
// Remove the role assignment for user4/cohort1.
|
||||
// Verify only 1 role is unassigned as the others are still valid for the other cohort role assignment.
|
||||
$result = api::delete_cohort_role_assignment($cohort1roleassignment->get('id'));
|
||||
$this->assertTrue($result);
|
||||
|
||||
$sync = api::sync_all_cohort_roles();
|
||||
|
||||
$this->assertCount(0, $sync['rolesadded']);
|
||||
$this->assertCount(1, $sync['rolesremoved']);
|
||||
$removedusers = array_column($sync['rolesremoved'], 'useridassignedover');
|
||||
$this->assertContains($user2->id, $removedusers);
|
||||
}
|
||||
|
||||
public function test_list_cohort_role_assignments() {
|
||||
|
||||
@@ -1 +1 @@
|
||||
define(["jquery","core/ajax","core/notification","core/str","core/modal_factory","core/modal_events","core/templates"],function(a,b,c,d,e,f,g){function h(a){var e={methodname:"tool_dataprivacy_contact_dpo",args:{message:a}},f="success";b.call([e])[0].then(function(a){return a.result?d.get_string("requestsubmitted","tool_dataprivacy"):(f="error",a.warnings.join("<br>"))}).done(function(a){c.addNotification({message:a,type:f})}).fail(c.exception)}var i={CANCEL_REQUEST:'[data-action="cancel"]',CONTACT_DPO:'[data-action="contactdpo"]'},j=function(){this.registerEvents()};return j.prototype.registerEvents=function(){a(i.CANCEL_REQUEST).click(function(g){g.preventDefault();var h=a(this).data("requestid"),i=[{key:"cancelrequest",component:"tool_dataprivacy"},{key:"cancelrequestconfirmation",component:"tool_dataprivacy"}];d.get_strings(i).then(function(a){var d=a[0],g=a[1];return e.create({title:d,body:g,type:e.types.SAVE_CANCEL}).then(function(a){return a.setSaveButtonText(d),a.getRoot().on(f.save,function(){var a={requestid:h},d={methodname:"tool_dataprivacy_cancel_data_request",args:a};b.call([d])[0].done(function(a){a.result?window.location.reload():c.addNotification({message:a.warnings[0].message,type:"error"})}).fail(c.exception)}),a.getRoot().on(f.hidden,function(){a.destroy()}),a})}).done(function(a){a.show()}).fail(c.exception)}),a(i.CONTACT_DPO).click(function(b){b.preventDefault();var i=a(this).data("replytoemail"),j=[{key:"contactdataprotectionofficer",component:"tool_dataprivacy"},{key:"send",component:"tool_dataprivacy"}],k="";d.get_strings(j).then(function(a){var b=a[0];k=a[1];var c={replytoemail:i};return e.create({title:b,body:g.render("tool_dataprivacy/contact_dpo",c),type:e.types.SAVE_CANCEL,large:!0})}).done(function(b){b.setSaveButtonText(k),b.getRoot().on(f.save,function(b){var c=a("#message").val().trim();0===c.length?(b.preventDefault(),a('[data-region="messageinput"]').addClass("has-danger notifyproblem"),a("#id_error_message").removeAttr("hidden")):h(c)}),b.getRoot().on(f.hidden,function(){b.destroy()}),b.show()}).fail(c.exception)})},{init:function(){return new j}}});
|
||||
define(["jquery","core/ajax","core/notification","core/str","core/modal_factory","core/modal_events","core/templates","core/pending"],function(a,b,c,d,e,f,g,h){function i(a){var e={methodname:"tool_dataprivacy_contact_dpo",args:{message:a}},f="success";b.call([e])[0].then(function(a){return a.result?d.get_string("requestsubmitted","tool_dataprivacy"):(f="error",a.warnings.join("<br>"))}).done(function(a){c.addNotification({message:a,type:f})}).fail(c.exception)}var j={CANCEL_REQUEST:'[data-action="cancel"]',CONTACT_DPO:'[data-action="contactdpo"]'},k=function(){this.registerEvents()};return k.prototype.registerEvents=function(){a(j.CANCEL_REQUEST).click(function(g){g.preventDefault();var h=a(this).data("requestid"),i=[{key:"cancelrequest",component:"tool_dataprivacy"},{key:"cancelrequestconfirmation",component:"tool_dataprivacy"}];d.get_strings(i).then(function(a){var d=a[0],g=a[1];return e.create({title:d,body:g,type:e.types.SAVE_CANCEL}).then(function(a){return a.setSaveButtonText(d),a.getRoot().on(f.save,function(){var a={requestid:h},d={methodname:"tool_dataprivacy_cancel_data_request",args:a};b.call([d])[0].done(function(a){a.result?window.location.reload():c.addNotification({message:a.warnings[0].message,type:"error"})}).fail(c.exception)}),a.getRoot().on(f.hidden,function(){a.destroy()}),a})}).done(function(a){a.show()}).fail(c.exception)}),a(j.CONTACT_DPO).click(function(b){var j=new h("dataprivacy/crud:initModal:contactdpo");b.preventDefault();var k=a(this).data("replytoemail"),l=[{key:"contactdataprotectionofficer",component:"tool_dataprivacy"},{key:"send",component:"tool_dataprivacy"}],m="";d.get_strings(l).then(function(a){var b=a[0];m=a[1];var c={replytoemail:k};return e.create({title:b,body:g.render("tool_dataprivacy/contact_dpo",c),type:e.types.SAVE_CANCEL,large:!0})}).then(function(b){b.setSaveButtonText(m),b.show(),b.getRoot().on(f.save,function(b){var c=a("#message").val().trim();0===c.length?(b.preventDefault(),a('[data-region="messageinput"]').addClass("has-danger notifyproblem"),a("#id_error_message").removeAttr("hidden")):i(c)}),b.getRoot().on(f.hidden,function(){b.destroy()})}).then(j.resolve)["catch"](c.exception)})},{init:function(){return new k}}});
|
||||
@@ -28,8 +28,9 @@ define([
|
||||
'core/str',
|
||||
'core/modal_factory',
|
||||
'core/modal_events',
|
||||
'core/templates'],
|
||||
function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates) {
|
||||
'core/templates',
|
||||
'core/pending'],
|
||||
function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, Pending) {
|
||||
|
||||
/**
|
||||
* List of action selectors.
|
||||
@@ -118,6 +119,7 @@ function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates) {
|
||||
});
|
||||
|
||||
$(ACTIONS.CONTACT_DPO).click(function(e) {
|
||||
var pendingPromise = new Pending('dataprivacy/crud:initModal:contactdpo');
|
||||
e.preventDefault();
|
||||
|
||||
var replyToEmail = $(this).data('replytoemail');
|
||||
@@ -146,9 +148,12 @@ function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates) {
|
||||
type: ModalFactory.types.SAVE_CANCEL,
|
||||
large: true
|
||||
});
|
||||
}).done(function(modal) {
|
||||
}).then(function(modal) {
|
||||
modal.setSaveButtonText(sendButtonText);
|
||||
|
||||
// Show the modal!
|
||||
modal.show();
|
||||
|
||||
// Handle send event.
|
||||
modal.getRoot().on(ModalEvents.save, function(e) {
|
||||
var message = $('#message').val().trim();
|
||||
@@ -169,9 +174,9 @@ function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates) {
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
// Show the modal!
|
||||
modal.show();
|
||||
}).fail(Notification.exception);
|
||||
return;
|
||||
}).then(pendingPromise.resolve)
|
||||
.catch(Notification.exception);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -276,5 +276,28 @@ function xmldb_tool_dataprivacy_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2018051415, 'tool', 'dataprivacy');
|
||||
}
|
||||
|
||||
if ($oldversion < 2018051416) {
|
||||
|
||||
// Define field commentsformat to be added to tool_dataprivacy_request.
|
||||
$table = new xmldb_table('tool_dataprivacy_request');
|
||||
$field = new xmldb_field('commentsformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'comments');
|
||||
|
||||
// Conditionally launch add field commentsformat.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Define field dpocommentformat to be added to tool_dataprivacy_request.
|
||||
$field = new xmldb_field('dpocommentformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'dpocomment');
|
||||
|
||||
// Conditionally launch add field dpocommentformat.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Dataprivacy savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2018051416, 'tool', 'dataprivacy');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -337,6 +337,11 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
|
||||
$datarequest = api::create_data_request($student->id, api::DATAREQUEST_TYPE_EXPORT);
|
||||
|
||||
$requestid = $datarequest->get('id');
|
||||
|
||||
// Login as a user without DPO role.
|
||||
$this->setUser($teacher);
|
||||
$this->expectException(required_capability_exception::class);
|
||||
api::approve_data_request($requestid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -87,7 +87,7 @@ Feature: Data export from the privacy API
|
||||
And I reload the page
|
||||
And I should see "Download ready" in the "Export all of my personal data" "table_row"
|
||||
And I open the action menu in "Victim User 1" "table_row"
|
||||
And following "Download" should download between "1" and "130000" bytes
|
||||
And following "Download" should download between "1" and "144000" bytes
|
||||
|
||||
And the following config values are set as admin:
|
||||
| privacyrequestexpiry | 1 | tool_dataprivacy |
|
||||
|
||||
@@ -46,7 +46,7 @@ Feature: Manage data requests
|
||||
And I open the action menu in "John Doe" "table_row"
|
||||
And I should see "View the request"
|
||||
But I should not see "Mark as complete"
|
||||
And I press key "27" in ".moodle-actionmenu" "css_element"
|
||||
And I press the escape key
|
||||
And I open the action menu in "Jane Doe" "table_row"
|
||||
And I choose "Mark as complete" in the open action menu
|
||||
And I should see "Do you really want to mark this user enquiry as complete?"
|
||||
|
||||
@@ -10,16 +10,12 @@ Feature: Manage data storage purposes
|
||||
And I open the action menu in "region-main" "region"
|
||||
And I choose "Purposes" in the open action menu
|
||||
And I press "Add purpose"
|
||||
And I set the field "Name" to "Purpose 1"
|
||||
And I set the field "Description" to "Purpose 1 description"
|
||||
And I click on ".form-autocomplete-downarrow" "css_element" in the "Lawful bases" "form_row"
|
||||
And I click on "Contract (GDPR Art. 6.1(b))" "list_item"
|
||||
And I click on "Legal obligation (GDPR Art 6.1(c))" "list_item"
|
||||
And I press key "27" in the field "Lawful bases"
|
||||
And I click on ".form-autocomplete-downarrow" "css_element" in the "Sensitive personal data processing reasons" "form_row"
|
||||
And I click on "Explicit consent (GDPR Art. 9.2(a))" "list_item"
|
||||
And I press key "27" in the field "Sensitive personal data processing reasons"
|
||||
And I set the field "retentionperiodnumber" to "2"
|
||||
And I set the following fields to these values:
|
||||
| Name | Purpose 1 |
|
||||
| Description | Purpose 1 description |
|
||||
| Lawful bases | Contract (GDPR Art. 6.1(b)),Legal obligation (GDPR Art 6.1(c)) |
|
||||
| Sensitive personal data processing reasons | Explicit consent (GDPR Art. 9.2(a)) |
|
||||
| retentionperiodnumber | 2 |
|
||||
When I press "Save"
|
||||
Then I should see "Purpose 1" in the "List of data purposes" "table"
|
||||
And I should see "Contract (GDPR Art. 6.1(b))" in the "Purpose 1" "table_row"
|
||||
@@ -31,14 +27,13 @@ Feature: Manage data storage purposes
|
||||
Scenario: Update a data storage purpose
|
||||
Given I open the action menu in "Purpose 1" "table_row"
|
||||
And I choose "Edit" in the open action menu
|
||||
And I set the field "Name" to "Purpose 1 edited"
|
||||
And I set the field "Description" to "Purpose 1 description edited"
|
||||
And I click on "Legal obligation (GDPR Art 6.1(c))" "text" in the ".form-autocomplete-selection" "css_element"
|
||||
And I click on ".form-autocomplete-downarrow" "css_element" in the "Lawful bases" "form_row"
|
||||
And I click on "Vital interests (GDPR Art. 6.1(d))" "list_item"
|
||||
And I press key "27" in the field "Lawful bases"
|
||||
And I set the field "retentionperiodnumber" to "3"
|
||||
And I click on "protected" "checkbox"
|
||||
And I set the following fields to these values:
|
||||
| Name | Purpose 1 edited |
|
||||
| Description | Purpose 1 description edited |
|
||||
| Lawful bases | Contract (GDPR Art. 6.1(b)), Vital interests (GDPR Art. 6.1(d)) |
|
||||
| Sensitive personal data processing reasons | Explicit consent (GDPR Art. 9.2(a)) |
|
||||
| retentionperiodnumber | 3 |
|
||||
| protected | 1 |
|
||||
When I press "Save changes"
|
||||
Then I should see "Purpose 1 edited" in the "List of data purposes" "table"
|
||||
And I should see "Purpose 1 description edited" in the "Purpose 1 edited" "table_row"
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$plugin->version = 2018051415;
|
||||
$plugin->version = 2018051416;
|
||||
$plugin->requires = 2018050800; // Moodle 3.5dev (Build 2018031600) and upwards.
|
||||
$plugin->component = 'tool_dataprivacy';
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["core/yui"],function(a){var b=function(b,c,d,e,f){this.yuiDialogue=null;var g=this;"undefined"==typeof f&&(f=!1),a.use("moodle-core-notification","timers",function(){var h="480px";f&&(h="800px"),g.yuiDialogue=new M.core.dialogue({headerContent:b,bodyContent:c,draggable:!0,visible:!1,center:!0,modal:!0,width:h}),g.yuiDialogue.after("visibleChange",function(b){b.newVal?"undefined"!=typeof d&&a.soon(function(){d(g),g.yuiDialogue.centerDialogue()}):"undefined"!=typeof e&&a.soon(function(){e(g)})}),g.yuiDialogue.show()})};return b.prototype.close=function(){this.yuiDialogue.hide(),this.yuiDialogue.destroy()},b.prototype.getContent=function(){return this.yuiDialogue.bodyNode.getDOMNode()},b});
|
||||
define(["core/yui"],function(a){var b=function(b,c,d,e,f){M.util.js_pending("tool_lp/dialogue:dialogue"),this.yuiDialogue=null;var g=this;"undefined"==typeof f&&(f=!1),a.use("moodle-core-notification","timers",function(){var h="480px";f&&(h="800px"),g.yuiDialogue=new M.core.dialogue({headerContent:b,bodyContent:c,draggable:!0,visible:!1,center:!0,modal:!0,width:h}),g.yuiDialogue.before("visibleChange",function(){M.util.js_pending("tool_lp/dialogue:before:visibleChange")}),g.yuiDialogue.after("visibleChange",function(b){b.newVal?"undefined"!=typeof d?a.soon(function(){d(g),g.yuiDialogue.centerDialogue(),M.util.js_complete("tool_lp/dialogue:before:visibleChange")}):M.util.js_complete("tool_lp/dialogue:before:visibleChange"):"undefined"!=typeof e?a.soon(function(){e(g),M.util.js_complete("tool_lp/dialogue:before:visibleChange")}):M.util.js_complete("tool_lp/dialogue:before:visibleChange")}),g.yuiDialogue.show(),M.util.js_complete("tool_lp/dialogue:dialogue")})};return b.prototype.close=function(){this.yuiDialogue.hide(),this.yuiDialogue.destroy()},b.prototype.getContent=function(){return this.yuiDialogue.bodyNode.getDOMNode()},b});
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/notification","core/templates","tool_lp/dialogue","tool_lp/event_base","core/str"],function(a,b,c,d,e,f){var g=function(a){e.prototype.constructor.apply(this,[]),this._ratingOptions=a};return g.prototype=Object.create(e.prototype),g.prototype._popup=null,g.prototype._ratingOptions=null,g.prototype._afterRender=function(){var b=this._find('[data-action="rate"]'),c=this._find('[name="rating"]'),d=this._find('[name="comment"]');this._find('[data-action="cancel"]').click(function(a){a.preventDefault(),this._trigger("cancelled"),this.close()}.bind(this)),c.change(function(){var c=a(this);c.val()?b.prop("disabled",!1):b.prop("disabled",!0)}).change(),b.click(function(a){a.preventDefault();var b=c.val();b&&(this._trigger("rated",{rating:b,note:d.val()}),this.close())}.bind(this))},g.prototype.close=function(){this._popup.close(),this._popup=null},g.prototype.display=function(){return a.when(f.get_string("rate","tool_lp"),this._render()).then(function(a,b){return this._popup=new d(a,b[0],this._afterRender.bind(this)),this._popup}.bind(this))["catch"](b.exception)},g.prototype._find=function(b){return a(this._popup.getContent()).find(b)},g.prototype._render=function(){var a={cangrade:this._canGrade,ratings:this._ratingOptions};return c.render("tool_lp/competency_grader",a)},g});
|
||||
define(["jquery","core/notification","core/templates","tool_lp/dialogue","tool_lp/event_base","core/str"],function(a,b,c,d,e,f){var g=function(a){e.prototype.constructor.apply(this,[]),this._ratingOptions=a};return g.prototype=Object.create(e.prototype),g.prototype._popup=null,g.prototype._ratingOptions=null,g.prototype._afterRender=function(){var b=this._find('[data-action="rate"]'),c=this._find('[name="rating"]'),d=this._find('[name="comment"]');this._find('[data-action="cancel"]').click(function(a){a.preventDefault(),this._trigger("cancelled"),this.close()}.bind(this)),c.change(function(){var c=a(this);c.val()?b.prop("disabled",!1):b.prop("disabled",!0)}).change(),b.click(function(a){a.preventDefault();var b=c.val();b&&(this._trigger("rated",{rating:b,note:d.val()}),this.close())}.bind(this))},g.prototype.close=function(){this._popup.close(),this._popup=null},g.prototype.display=function(){return M.util.js_pending("tool_lp/grade_dialogue:display"),a.when(f.get_string("rate","tool_lp"),this._render()).then(function(a,b){return this._popup=new d(a,b[0],function(){this._afterRender(),M.util.js_complete("tool_lp/grade_dialogue:display")}.bind(this)),this._popup}.bind(this))["catch"](b.exception)},g.prototype._find=function(b){return a(this._popup.getContent()).find(b)},g.prototype._render=function(){var a={cangrade:this._canGrade,ratings:this._ratingOptions};return c.render("tool_lp/competency_grader",a)},g});
|
||||
@@ -1 +1 @@
|
||||
define(["jquery","core/notification","core/ajax","core/log","tool_lp/grade_dialogue","tool_lp/event_base","tool_lp/scalevalues"],function(a,b,c,d,e,f,g){var h=function(b,c,d,e,g,h,i){f.prototype.constructor.apply(this,[]);var j=a(b);if(!j.length)throw new Error("Could not find the trigger");this._scaleId=c,this._competencyId=d,this._userId=e,this._planId=g,this._courseId=h,this._chooseStr=i,this._setUp(),j.click(function(a){a.preventDefault(),this._dialogue.display()}.bind(this)),this._planId?(this._methodName="core_competency_grade_competency_in_plan",this._args={competencyid:this._competencyId,planid:this._planId}):this._courseId?(this._methodName="core_competency_grade_competency_in_course",this._args={competencyid:this._competencyId,courseid:this._courseId,userid:this._userId}):(this._methodName="core_competency_grade_competency",this._args={userid:this._userId,competencyid:this._competencyId})};return h.prototype=Object.create(f.prototype),h.prototype._setUp=function(){var a=[],d=this,f=g.get_values(d._scaleId);f.then(function(b){a.push({value:"",name:d._chooseStr});for(var c=0;c<b.length;c++){var e=b[c];a.push({value:e.id,name:e.name})}return a}).then(function(a){return new e(a)}).then(function(a){return a.on("rated",function(a,e){var f=d._args;f.grade=e.rating,f.note=e.note,c.call([{methodname:d._methodName,args:f,done:function(a){d._trigger("competencyupdated",{args:f,evidence:a})},fail:b.exception}])}),a}).then(function(a){d._dialogue=a}).fail(b.exception)},h.prototype._scaleId=null,h.prototype._competencyId=null,h.prototype._userId=null,h.prototype._planId=null,h.prototype._courseId=null,h.prototype._chooseStr=null,h.prototype._dialogue=null,h});
|
||||
define(["jquery","core/notification","core/ajax","core/log","tool_lp/grade_dialogue","tool_lp/event_base","tool_lp/scalevalues"],function(a,b,c,d,e,f,g){var h=function(b,c,d,e,g,h,i){f.prototype.constructor.apply(this,[]);var j=a(b);if(!j.length)throw new Error("Could not find the trigger");this._scaleId=c,this._competencyId=d,this._userId=e,this._planId=g,this._courseId=h,this._chooseStr=i,this._setUp(),j.click(function(a){a.preventDefault(),this._dialogue.display()}.bind(this)),this._planId?(this._methodName="core_competency_grade_competency_in_plan",this._args={competencyid:this._competencyId,planid:this._planId}):this._courseId?(this._methodName="core_competency_grade_competency_in_course",this._args={competencyid:this._competencyId,courseid:this._courseId,userid:this._userId}):(this._methodName="core_competency_grade_competency",this._args={userid:this._userId,competencyid:this._competencyId})};return h.prototype=Object.create(f.prototype),h.prototype._setUp=function(){var a=[],d=this;M.util.js_pending("tool_lp/grade_user_competency_inline:_setUp");var f=g.get_values(d._scaleId);f.then(function(b){a.push({value:"",name:d._chooseStr});for(var c=0;c<b.length;c++){var e=b[c];a.push({value:e.id,name:e.name})}return a}).then(function(a){return new e(a)}).then(function(a){return a.on("rated",function(a,e){var f=d._args;f.grade=e.rating,f.note=e.note,c.call([{methodname:d._methodName,args:f,done:function(a){d._trigger("competencyupdated",{args:f,evidence:a})},fail:b.exception}])}),a}).then(function(a){d._dialogue=a,M.util.js_complete("tool_lp/grade_user_competency_inline:_setUp")}).fail(b.exception)},h.prototype._scaleId=null,h.prototype._competencyId=null,h.prototype._userId=null,h.prototype._planId=null,h.prototype._courseId=null,h.prototype._chooseStr=null,h.prototype._dialogue=null,h});
|
||||
@@ -35,6 +35,8 @@ define(['core/yui'], function(Y) {
|
||||
* @param {Boolean} wide Specify we want an extra wide dialogue (the size is standard, but wider than the default).
|
||||
*/
|
||||
var dialogue = function(title, content, afterShow, afterHide, wide) {
|
||||
M.util.js_pending('tool_lp/dialogue:dialogue');
|
||||
|
||||
this.yuiDialogue = null;
|
||||
var parent = this;
|
||||
|
||||
@@ -59,6 +61,10 @@ define(['core/yui'], function(Y) {
|
||||
width: width
|
||||
});
|
||||
|
||||
parent.yuiDialogue.before('visibleChange', function() {
|
||||
M.util.js_pending('tool_lp/dialogue:before:visibleChange');
|
||||
});
|
||||
|
||||
parent.yuiDialogue.after('visibleChange', function(e) {
|
||||
if (e.newVal) {
|
||||
// Delay the callback call to the next tick, otherwise it can happen that it is
|
||||
@@ -67,18 +73,25 @@ define(['core/yui'], function(Y) {
|
||||
Y.soon(function() {
|
||||
afterShow(parent);
|
||||
parent.yuiDialogue.centerDialogue();
|
||||
M.util.js_complete('tool_lp/dialogue:before:visibleChange');
|
||||
});
|
||||
} else {
|
||||
M.util.js_complete('tool_lp/dialogue:before:visibleChange');
|
||||
}
|
||||
} else {
|
||||
if ((typeof afterHide !== 'undefined')) {
|
||||
Y.soon(function() {
|
||||
afterHide(parent);
|
||||
M.util.js_complete('tool_lp/dialogue:before:visibleChange');
|
||||
});
|
||||
} else {
|
||||
M.util.js_complete('tool_lp/dialogue:before:visibleChange');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
parent.yuiDialogue.show();
|
||||
M.util.js_complete('tool_lp/dialogue:dialogue');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ define(['jquery',
|
||||
* @return {Promise}
|
||||
*/
|
||||
Grade.prototype.display = function() {
|
||||
M.util.js_pending('tool_lp/grade_dialogue:display');
|
||||
return $.when(
|
||||
Str.get_string('rate', 'tool_lp'),
|
||||
this._render()
|
||||
@@ -110,7 +111,10 @@ define(['jquery',
|
||||
this._popup = new Dialogue(
|
||||
title,
|
||||
templateResult[0],
|
||||
this._afterRender.bind(this)
|
||||
function() {
|
||||
this._afterRender();
|
||||
M.util.js_complete('tool_lp/grade_dialogue:display');
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
return this._popup;
|
||||
|
||||
@@ -94,6 +94,7 @@ define(['jquery',
|
||||
var options = [],
|
||||
self = this;
|
||||
|
||||
M.util.js_pending('tool_lp/grade_user_competency_inline:_setUp');
|
||||
var promise = ScaleValues.get_values(self._scaleId);
|
||||
promise.then(function(scalevalues) {
|
||||
options.push({
|
||||
@@ -134,6 +135,7 @@ define(['jquery',
|
||||
.then(function(dialogue) {
|
||||
self._dialogue = dialogue;
|
||||
|
||||
M.util.js_complete('tool_lp/grade_user_competency_inline:_setUp');
|
||||
return;
|
||||
})
|
||||
.fail(notification.exception);
|
||||
|
||||
@@ -30,7 +30,7 @@ Feature: Manage plearning plan
|
||||
And I click on ".template-userplans" "css_element" in the "Science template" "table_row"
|
||||
And I open the autocomplete suggestions list
|
||||
And I click on "Admin User" item in the autocomplete list
|
||||
And I press key "27" in the field "Select users to create learning plans for"
|
||||
And I press the escape key
|
||||
When I click on "Create learning plans" "button"
|
||||
Then I should see "A learning plan was created"
|
||||
And I should see "Admin User" in the "Science template" "table_row"
|
||||
@@ -53,9 +53,7 @@ Feature: Manage plearning plan
|
||||
And I follow "Home"
|
||||
And I navigate to "Competencies > Learning plan templates" in site administration
|
||||
And I click on ".template-cohorts" "css_element" in the "Science template cohort" "table_row"
|
||||
And I click on ".form-autocomplete-downarrow" "css_element"
|
||||
And I click on "cohort plan" item in the autocomplete list
|
||||
And I press key "27" in the field "Select cohorts to sync"
|
||||
And I set the field "Select cohorts to sync" to "cohort plan"
|
||||
When I click on "Add cohorts" "button"
|
||||
Then I should see "2 learning plans were created."
|
||||
And I follow "Learning plan templates"
|
||||
|
||||
@@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
use \tool_monitor\privacy\provider;
|
||||
use \core_privacy\local\request\approved_contextlist;
|
||||
use \core_privacy\local\request\approved_userlist;
|
||||
use \core_privacy\tests\provider_testcase;
|
||||
|
||||
/**
|
||||
* Privacy test for the event monitor
|
||||
@@ -37,7 +38,7 @@ use \core_privacy\local\request\approved_userlist;
|
||||
* @copyright 2018 Adrian Greeve <adriangreeve.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_monitor_privacy_testcase extends advanced_testcase {
|
||||
class tool_monitor_privacy_testcase extends provider_testcase {
|
||||
|
||||
/**
|
||||
* Set up method.
|
||||
|
||||
@@ -123,9 +123,12 @@ class issuer extends persistent {
|
||||
$mform->addElement('checkbox', 'showonloginpage', get_string('issuershowonloginpage', 'tool_oauth2'));
|
||||
$mform->addHelpButton('showonloginpage', 'issuershowonloginpage', 'tool_oauth2');
|
||||
|
||||
// Require confirmation email for new accounts.
|
||||
$mform->addElement('advcheckbox', 'requireconfirmation', get_string('issuerrequireconfirmation', 'tool_oauth2'));
|
||||
$mform->addHelpButton('requireconfirmation', 'issuerrequireconfirmation', 'tool_oauth2');
|
||||
$showrequireconfirm = $this->_customdata['showrequireconfirm'] ?? false;
|
||||
if ($showrequireconfirm) {
|
||||
// Require confirmation email for new accounts.
|
||||
$mform->addElement('advcheckbox', 'requireconfirmation', get_string('issuerrequireconfirmation', 'tool_oauth2'));
|
||||
$mform->addHelpButton('requireconfirmation', 'issuerrequireconfirmation', 'tool_oauth2');
|
||||
}
|
||||
|
||||
$mform->addElement('hidden', 'sortorder');
|
||||
$mform->setType('sortorder', PARAM_INT);
|
||||
|
||||
@@ -58,7 +58,25 @@ if ($action == 'edit') {
|
||||
$PAGE->navbar->add(get_string('createnewissuer', 'tool_oauth2'));
|
||||
}
|
||||
|
||||
$mform = new \tool_oauth2\form\issuer(null, ['persistent' => $issuer]);
|
||||
if (!empty($issuerid)) {
|
||||
// Show the "Require confirmation email" checkbox for trusted issuers like Google, Facebook and Microsoft.
|
||||
$likefacebook = $DB->sql_like('url', ':facebook');
|
||||
$likegoogle = $DB->sql_like('url', ':google');
|
||||
$likemicrosoft = $DB->sql_like('url', ':microsoft');
|
||||
$params = [
|
||||
'issuerid' => $issuerid,
|
||||
'facebook' => '%facebook%',
|
||||
'google' => '%google%',
|
||||
'microsoft' => '%microsoft%',
|
||||
];
|
||||
$select = "issuerid = :issuerid AND ($likefacebook OR $likegoogle OR $likemicrosoft)";
|
||||
// We're querying from the oauth2_endpoint table because the base URLs of FB and Microsoft can be empty in the issuer table.
|
||||
$showrequireconfirm = $DB->record_exists_select('oauth2_endpoint', $select, $params);
|
||||
} else {
|
||||
$showrequireconfirm = optional_param('showrequireconfirm', false, PARAM_BOOL);
|
||||
}
|
||||
|
||||
$mform = new \tool_oauth2\form\issuer(null, ['persistent' => $issuer, 'showrequireconfirm' => $showrequireconfirm]);
|
||||
}
|
||||
|
||||
if ($mform && $mform->is_cancelled()) {
|
||||
@@ -157,21 +175,31 @@ if ($mform && $mform->is_cancelled()) {
|
||||
$issuers = core\oauth2\api::get_all_issuers();
|
||||
echo $renderer->issuers_table($issuers);
|
||||
|
||||
// Google template.
|
||||
$docs = 'admin/tool/oauth2/issuers/google';
|
||||
$params = ['action' => 'edittemplate', 'type' => 'google', 'sesskey' => sesskey(), 'docslink' => $docs];
|
||||
$params = ['action' => 'edittemplate', 'type' => 'google', 'sesskey' => sesskey(), 'docslink' => $docs,
|
||||
'showrequireconfirm' => true];
|
||||
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', $params);
|
||||
echo $renderer->single_button($addurl, get_string('createnewgoogleissuer', 'tool_oauth2'));
|
||||
|
||||
// Microsoft template.
|
||||
$docs = 'admin/tool/oauth2/issuers/microsoft';
|
||||
$params = ['action' => 'edittemplate', 'type' => 'microsoft', 'sesskey' => sesskey(), 'docslink' => $docs];
|
||||
$params = ['action' => 'edittemplate', 'type' => 'microsoft', 'sesskey' => sesskey(), 'docslink' => $docs,
|
||||
'showrequireconfirm' => true];
|
||||
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', $params);
|
||||
echo $renderer->single_button($addurl, get_string('createnewmicrosoftissuer', 'tool_oauth2'));
|
||||
|
||||
// Facebook template.
|
||||
$docs = 'admin/tool/oauth2/issuers/facebook';
|
||||
$params = ['action' => 'edittemplate', 'type' => 'microsoft', 'sesskey' => sesskey(), 'docslink' => $docs];
|
||||
$params = ['action' => 'edittemplate', 'type' => 'facebook', 'sesskey' => sesskey(), 'docslink' => $docs];
|
||||
$params = ['action' => 'edittemplate', 'type' => 'facebook', 'sesskey' => sesskey(), 'docslink' => $docs,
|
||||
'showrequireconfirm' => true];
|
||||
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', $params);
|
||||
echo $renderer->single_button($addurl, get_string('createnewfacebookissuer', 'tool_oauth2'));
|
||||
|
||||
// Generic issuer.
|
||||
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', ['action' => 'edit']);
|
||||
echo $renderer->single_button($addurl, get_string('createnewissuer', 'tool_oauth2'));
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
}
|
||||
|
||||
@@ -131,11 +131,12 @@ if ($execute) {
|
||||
tool_phpunit_problem('Can not create configuration file');
|
||||
}
|
||||
}
|
||||
$configdir = escapeshellarg($configdir);
|
||||
// no cleanup of path - this is tricky because we can not use escapeshellarg and friends for escaping,
|
||||
// this is from admin user so PARAM_PATH must be enough
|
||||
chdir($CFG->dirroot);
|
||||
passthru("php $CFG->admin/tool/phpunit/cli/util.php --run -c $configdir $testclass $testpath", $code);
|
||||
$configdir = escapeshellarg($configdir);
|
||||
$cleanclass = escapeshellarg($testclass);
|
||||
$cleanpath = escapeshellarg($testpath);
|
||||
|
||||
passthru("php $CFG->admin/tool/phpunit/cli/util.php --run -c $configdir $cleanclass $cleanpath", $code);
|
||||
chdir($oldcwd);
|
||||
|
||||
echo '</pre>';
|
||||
@@ -146,7 +147,7 @@ if ($execute) {
|
||||
}
|
||||
|
||||
echo $OUTPUT->box_start('generalbox boxwidthwide boxaligncenter');
|
||||
echo '<form method="get" action="webrunner.php">';
|
||||
echo '<form method="post" action="webrunner.php">';
|
||||
echo '<fieldset class="invisiblefieldset">';
|
||||
echo '<label for="testpath">Test one file</label> ';
|
||||
echo '<input type="text" id="testpath" name="testpath" value="'.s($testpath).'" size="50" /> (all test cases from webrunner.xml if empty)';
|
||||
|
||||
@@ -1 +1 @@
|
||||
define(["jquery","core/ajax","core/notification"],function(a,b,c){return{list:function(b,c){var d=[],e=a(b),f=a(b).data("originaloptionsjson"),g=e.val();a.each(f,function(b,e){return""!==a.trim(c)&&e.label.toLocaleLowerCase().indexOf(c.toLocaleLowerCase())===-1||(a.inArray(e.value,g)>-1||(d.push(e),!0))});var h=new a.Deferred;return h.resolve(d),h.promise()},processResults:function(b,c){var d=[];return a.each(c,function(a,b){d.push({value:b.value,label:b.label})}),d},transport:function(a,b,d){this.list(a,b).then(d)["catch"](c.exception)}}});
|
||||
define(["jquery","core/ajax","core/notification"],function(a,b,c){return{list:function(b,c){var d=[],e=a(b),f=a(b).data("originaloptionsjson"),g=e.val();a.each(f,function(b,e){return""!==c.trim()&&e.label.toLocaleLowerCase().indexOf(c.toLocaleLowerCase())===-1||(a.inArray(e.value,g)>-1||(d.push(e),!0))});var h=new a.Deferred;return h.resolve(d),h.promise()},processResults:function(b,c){var d=[];return a.each(c,function(a,b){d.push({value:b.value,label:b.label})}),d},transport:function(a,b,d){this.list(a,b).then(d)["catch"](c.exception)}}});
|
||||
@@ -41,7 +41,7 @@ define(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notificat
|
||||
var selectedFilters = el.val();
|
||||
$.each(originalOptions, function(index, option) {
|
||||
// Skip option if it does not contain the query string.
|
||||
if ($.trim(query) !== '' && option.label.toLocaleLowerCase().indexOf(query.toLocaleLowerCase()) === -1) {
|
||||
if (query.trim() !== '' && option.label.toLocaleLowerCase().indexOf(query.toLocaleLowerCase()) === -1) {
|
||||
return true;
|
||||
}
|
||||
// Skip filters that have already been selected.
|
||||
|
||||
@@ -643,6 +643,6 @@ class acceptances_table extends \table_sql {
|
||||
}
|
||||
return ''; // User agreed by themselves.
|
||||
}
|
||||
return null;
|
||||
return parent::other_cols($column, $row);
|
||||
}
|
||||
}
|
||||
@@ -838,36 +838,31 @@ class tool_uploadcourse_course {
|
||||
unset($method['delete']);
|
||||
unset($method['disable']);
|
||||
|
||||
if (!empty($instance) && $todelete) {
|
||||
if ($todelete) {
|
||||
// Remove the enrolment method.
|
||||
foreach ($instances as $instance) {
|
||||
if ($instance->enrol == $enrolmethod) {
|
||||
$plugin = $enrolmentplugins[$instance->enrol];
|
||||
$plugin->delete_instance($instance);
|
||||
break;
|
||||
}
|
||||
if ($instance) {
|
||||
$plugin = $enrolmentplugins[$instance->enrol];
|
||||
$plugin->delete_instance($instance);
|
||||
}
|
||||
} else if (!empty($instance) && $todisable) {
|
||||
// Disable the enrolment.
|
||||
foreach ($instances as $instance) {
|
||||
if ($instance->enrol == $enrolmethod) {
|
||||
$plugin = $enrolmentplugins[$instance->enrol];
|
||||
$plugin->update_status($instance, ENROL_INSTANCE_DISABLED);
|
||||
$enrol_updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$plugin = $enrolmentplugins[$instance->enrol];
|
||||
$plugin->update_status($instance, ENROL_INSTANCE_DISABLED);
|
||||
$enrol_updated = true;
|
||||
} else {
|
||||
$plugin = null;
|
||||
|
||||
$status = ($todisable) ? ENROL_INSTANCE_DISABLED : ENROL_INSTANCE_ENABLED;
|
||||
|
||||
if (empty($instance)) {
|
||||
$plugin = $enrolmentplugins[$enrolmethod];
|
||||
$instance = new stdClass();
|
||||
$instance->id = $plugin->add_default_instance($course);
|
||||
$instanceid = $plugin->add_default_instance($course);
|
||||
$instance = $DB->get_record('enrol', ['id' => $instanceid]);
|
||||
$instance->roleid = $plugin->get_config('roleid');
|
||||
$instance->status = ENROL_INSTANCE_ENABLED;
|
||||
$plugin->update_status($instance, $status);
|
||||
} else {
|
||||
$plugin = $enrolmentplugins[$instance->enrol];
|
||||
$plugin->update_status($instance, ENROL_INSTANCE_ENABLED);
|
||||
$plugin->update_status($instance, $status);
|
||||
}
|
||||
|
||||
// Now update values.
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
@tool @tool_uploadcourse @_file_upload
|
||||
Feature: An admin can update courses enrolments using a CSV file
|
||||
In order to update courses enrolments using a CSV file
|
||||
As an admin
|
||||
I need to be able to upload a CSV file with enrolment methods for the courses
|
||||
|
||||
Background:
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And I log in as "admin"
|
||||
|
||||
@javascript
|
||||
Scenario: Creating enrolment method by enable it
|
||||
Given I am on "Course 1" course homepage
|
||||
And I navigate to "Users > Enrolment methods" in current page administration
|
||||
And I click on "Delete" "link" in the "Guest access" "table_row"
|
||||
And I click on "Continue" "button"
|
||||
And I should not see "Guest access" in the "generaltable" "table"
|
||||
And I navigate to "Courses > Upload courses" in site administration
|
||||
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_enable.csv" file to "File" filemanager
|
||||
And I set the field "Upload mode" to "Only update existing courses"
|
||||
And I set the field "Update mode" to "Update with CSV data only"
|
||||
And I set the field "Allow deletes" to "Yes"
|
||||
And I click on "Preview" "button"
|
||||
When I click on "Upload courses" "button"
|
||||
Then I should see "Course updated"
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to "Users > Enrolment methods" in current page administration
|
||||
And "Disable" "icon" should exist in the "Guest access" "table_row"
|
||||
|
||||
@javascript
|
||||
Scenario: Creating enrolment method by disabling it
|
||||
Given I am on "Course 1" course homepage
|
||||
And I navigate to "Users > Enrolment methods" in current page administration
|
||||
And I click on "Delete" "link" in the "Guest access" "table_row"
|
||||
And I click on "Continue" "button"
|
||||
And I should not see "Guest access" in the "generaltable" "table"
|
||||
And I navigate to "Courses > Upload courses" in site administration
|
||||
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_disable.csv" file to "File" filemanager
|
||||
And I set the field "Upload mode" to "Only update existing courses"
|
||||
And I set the field "Update mode" to "Update with CSV data only"
|
||||
And I set the field "Allow deletes" to "Yes"
|
||||
And I click on "Preview" "button"
|
||||
When I click on "Upload courses" "button"
|
||||
Then I should see "Course updated"
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to "Users > Enrolment methods" in current page administration
|
||||
And "Enable" "icon" should exist in the "Guest access" "table_row"
|
||||
|
||||
@javascript
|
||||
Scenario: Enabling enrolment method
|
||||
Given I navigate to "Courses > Upload courses" in site administration
|
||||
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_enable.csv" file to "File" filemanager
|
||||
And I set the field "Upload mode" to "Only update existing courses"
|
||||
And I set the field "Update mode" to "Update with CSV data only"
|
||||
And I set the field "Allow deletes" to "Yes"
|
||||
And I click on "Preview" "button"
|
||||
When I click on "Upload courses" "button"
|
||||
Then I should see "Course updated"
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to "Users > Enrolment methods" in current page administration
|
||||
And "Disable" "icon" should exist in the "Guest access" "table_row"
|
||||
|
||||
@javascript
|
||||
Scenario: Disable an enrolment method
|
||||
Given I am on "Course 1" course homepage
|
||||
And I navigate to "Users > Enrolment methods" in current page administration
|
||||
And I click on "Enable" "link" in the "Guest access" "table_row"
|
||||
And "Disable" "icon" should exist in the "Guest access" "table_row"
|
||||
And I navigate to "Courses > Upload courses" in site administration
|
||||
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_disable.csv" file to "File" filemanager
|
||||
And I set the field "Upload mode" to "Only update existing courses"
|
||||
And I set the field "Update mode" to "Update with CSV data only"
|
||||
And I set the field "Allow deletes" to "Yes"
|
||||
And I click on "Preview" "button"
|
||||
When I click on "Upload courses" "button"
|
||||
Then I should see "Course updated"
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to "Users > Enrolment methods" in current page administration
|
||||
And "Enable" "icon" should exist in the "Guest access" "table_row"
|
||||
|
||||
@javascript
|
||||
Scenario: Delete an enrolment method
|
||||
Given I navigate to "Courses > Upload courses" in site administration
|
||||
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_delete.csv" file to "File" filemanager
|
||||
And I set the field "Upload mode" to "Only update existing courses"
|
||||
And I set the field "Update mode" to "Update with CSV data only"
|
||||
And I set the field "Allow deletes" to "Yes"
|
||||
And I click on "Preview" "button"
|
||||
When I click on "Upload courses" "button"
|
||||
Then I should see "Course updated"
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to "Users > Enrolment methods" in current page administration
|
||||
And I should not see "Guest access" in the "generaltable" "table"
|
||||
|
||||
@javascript
|
||||
Scenario: Delete an unexistent enrolment method (nothing should change)
|
||||
Given I am on "Course 1" course homepage
|
||||
And I navigate to "Users > Enrolment methods" in current page administration
|
||||
And I click on "Delete" "link" in the "Guest access" "table_row"
|
||||
And I click on "Continue" "button"
|
||||
And I should not see "Guest access" in the "generaltable" "table"
|
||||
And I navigate to "Courses > Upload courses" in site administration
|
||||
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_delete.csv" file to "File" filemanager
|
||||
And I set the field "Upload mode" to "Only update existing courses"
|
||||
And I set the field "Update mode" to "Update with CSV data only"
|
||||
And I set the field "Allow deletes" to "Yes"
|
||||
And I click on "Preview" "button"
|
||||
When I click on "Upload courses" "button"
|
||||
Then I should see "Course updated"
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to "Users > Enrolment methods" in current page administration
|
||||
And I should not see "Guest access" in the "generaltable" "table"
|
||||
@@ -0,0 +1,2 @@
|
||||
shortname,category,enrolment_1,enrolment_1_delete
|
||||
C1,1,guest,1
|
||||
|
@@ -0,0 +1,2 @@
|
||||
shortname,category,enrolment_1,enrolment_1_disable
|
||||
C1,1,guest,1
|
||||
|
@@ -0,0 +1,2 @@
|
||||
shortname,category,enrolment_1,enrolment_1_disable
|
||||
C1,1,guest,0
|
||||
|
@@ -283,7 +283,7 @@ class admin_uploaduser_form2 extends moodleform {
|
||||
$mform->setAdvanced('url');
|
||||
|
||||
$mform->addElement('text', 'idnumber', get_string('idnumber'), 'maxlength="255" size="25"');
|
||||
$mform->setType('idnumber', PARAM_NOTAGS);
|
||||
$mform->setType('idnumber', core_user::get_property_type('idnumber'));
|
||||
$mform->setForceLtr('idnumber');
|
||||
|
||||
$mform->addElement('text', 'institution', get_string('institution'), 'maxlength="255" size="25"');
|
||||
|
||||
@@ -45,18 +45,20 @@
|
||||
}
|
||||
|
||||
}}
|
||||
<div class="modal" data-role="flexitour-step">
|
||||
<div class="modal" data-region="modal-container" data-role="flexitour-step">
|
||||
<div class="modal" data-region="modal">
|
||||
<div data-role="arrow"></div>
|
||||
<div class="modal-header">
|
||||
<div class="modal-header" data-region="header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" data-role="end">×</button>
|
||||
<h3 data-placeholder="title"></h3>
|
||||
<h3 data-region="title" data-placeholder="title"></h3>
|
||||
</div>
|
||||
<div class="modal-body" data-placeholder="body"></div>
|
||||
<div class="modal-footer">
|
||||
<div class="modal-body" data-region="body" data-placeholder="body"></div>
|
||||
<div class="modal-footer" data-region="footer">
|
||||
<div class="btn-group">
|
||||
<button href="#" class="btn" data-role="previous">{{# str }} previous, moodle {{/ str }}</button>
|
||||
<button href="#" class="btn btn-primary" data-role="next">{{# str }} next, moodle {{/ str }}</button>
|
||||
</div>
|
||||
<button class="btn" data-role="end"> {{# str }} endtour, tool_usertours {{/ str }} </button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -374,7 +374,7 @@
|
||||
$row = array ();
|
||||
$row[] = "<a href=\"../user/view.php?id=$user->id&course=$site->id\">$fullname</a>";
|
||||
foreach ($extracolumns as $field) {
|
||||
$row[] = $user->{$field};
|
||||
$row[] = s($user->{$field});
|
||||
}
|
||||
$row[] = $user->city;
|
||||
$row[] = $user->country;
|
||||
|
||||
+2
-4
@@ -178,10 +178,10 @@ class auth_plugin_email extends auth_plugin_base {
|
||||
if ($user->auth != $this->authtype) {
|
||||
return AUTH_CONFIRM_ERROR;
|
||||
|
||||
} else if ($user->secret == $confirmsecret && $user->confirmed) {
|
||||
} else if ($user->secret === $confirmsecret && $user->confirmed) {
|
||||
return AUTH_CONFIRM_ALREADY;
|
||||
|
||||
} else if ($user->secret == $confirmsecret) { // They have provided the secret key to get in
|
||||
} else if ($user->secret === $confirmsecret) { // They have provided the secret key to get in
|
||||
$DB->set_field("user", "confirmed", 1, array("id"=>$user->id));
|
||||
|
||||
if ($wantsurl = get_user_preferences('auth_email_wantsurl', false, $user)) {
|
||||
@@ -257,5 +257,3 @@ class auth_plugin_email extends auth_plugin_base {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -49,6 +49,6 @@ class behat_auth_email extends behat_base {
|
||||
$confirmationpath = $confirmationurl->out_as_local_url(false);
|
||||
$url = $confirmationpath . '?' . 'data='. $secret .'/'. $username;
|
||||
|
||||
$this->getSession()->visit($this->locate_path($url));
|
||||
$this->execute('behat_general::i_visit', [$url]);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -598,10 +598,10 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
if ($user->auth != $this->authtype) {
|
||||
return AUTH_CONFIRM_ERROR;
|
||||
|
||||
} else if ($user->secret == $confirmsecret && $user->confirmed) {
|
||||
} else if ($user->secret === $confirmsecret && $user->confirmed) {
|
||||
return AUTH_CONFIRM_ALREADY;
|
||||
|
||||
} else if ($user->secret == $confirmsecret) { // They have provided the secret key to get in
|
||||
} else if ($user->secret === $confirmsecret) { // They have provided the secret key to get in
|
||||
if (!$this->user_activate($username)) {
|
||||
return AUTH_CONFIRM_FAIL;
|
||||
}
|
||||
|
||||
@@ -79,16 +79,19 @@ class auth_ldap_plugin_testcase extends advanced_testcase {
|
||||
$o['ou'] = 'users';
|
||||
ldap_add($connection, 'ou='.$o['ou'].','.$topdn, $o);
|
||||
|
||||
$createdusers = array();
|
||||
for ($i=1; $i<=5; $i++) {
|
||||
$this->create_ldap_user($connection, $topdn, $i);
|
||||
$createdusers[] = 'username' . $i;
|
||||
}
|
||||
|
||||
// Set up creators group.
|
||||
$assignedroles = array('username1', 'username2');
|
||||
$o = array();
|
||||
$o['objectClass'] = array('posixGroup');
|
||||
$o['cn'] = 'creators';
|
||||
$o['gidNumber'] = 1;
|
||||
$o['memberUid'] = array('username1', 'username2');
|
||||
$o['memberUid'] = $assignedroles;
|
||||
ldap_add($connection, 'cn='.$o['cn'].','.$topdn, $o);
|
||||
|
||||
$creatorrole = $DB->get_record('role', array('shortname'=>'coursecreator'));
|
||||
@@ -145,15 +148,23 @@ class auth_ldap_plugin_testcase extends advanced_testcase {
|
||||
// Check events, 5 users created with 2 users having roles.
|
||||
$this->assertCount(7, $events);
|
||||
foreach ($events as $index => $event) {
|
||||
$usercreatedindex = array(0, 2, 4, 5, 6);
|
||||
$roleassignedindex = array (1, 3);
|
||||
if (in_array($index, $usercreatedindex)) {
|
||||
$this->assertInstanceOf('\core\event\user_created', $event);
|
||||
}
|
||||
if (in_array($index, $roleassignedindex)) {
|
||||
$this->assertInstanceOf('\core\event\role_assigned', $event);
|
||||
$username = $DB->get_field('user', 'username', array('id' => $event->relateduserid)); // Get username.
|
||||
|
||||
if ($event->eventname === '\core\event\user_created') {
|
||||
$this->assertContains($username, $createdusers);
|
||||
unset($events[$index]); // Remove matching event.
|
||||
|
||||
} else if ($event->eventname === '\core\event\role_assigned') {
|
||||
$this->assertContains($username, $assignedroles);
|
||||
unset($events[$index]); // Remove matching event.
|
||||
|
||||
} else {
|
||||
$this->fail('Unexpected event found: ' . $event->eventname);
|
||||
}
|
||||
}
|
||||
// If all the user_created and role_assigned events have matched
|
||||
// then the $events array should be now empty.
|
||||
$this->assertCount(0, $events);
|
||||
|
||||
$this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
|
||||
$this->assertEquals(2, $DB->count_records('role_assignments'));
|
||||
|
||||
+1
-3
@@ -716,9 +716,7 @@ class auth_plugin_mnet extends auth_plugin_base {
|
||||
|
||||
foreach($superArray as $subArray) {
|
||||
$subArray = array_values($subArray);
|
||||
$instring = "('".implode("', '",$subArray)."')";
|
||||
$query = "select id, session_id, username from {mnet_session} where username in $instring";
|
||||
$results = $DB->get_records_sql($query);
|
||||
$results = $DB->get_records_list('mnet_session', 'username', $subArray, '', 'id, session_id, username');
|
||||
|
||||
if ($results == false) {
|
||||
// We seem to have a username that breaks our query:
|
||||
|
||||
@@ -314,10 +314,10 @@ class auth extends \auth_plugin_base {
|
||||
if ($user->auth != $this->authtype) {
|
||||
return AUTH_CONFIRM_ERROR;
|
||||
|
||||
} else if ($user->secret == $confirmsecret && $user->confirmed) {
|
||||
} else if ($user->secret === $confirmsecret && $user->confirmed) {
|
||||
return AUTH_CONFIRM_ALREADY;
|
||||
|
||||
} else if ($user->secret == $confirmsecret) { // They have provided the secret key to get in.
|
||||
} else if ($user->secret === $confirmsecret) { // They have provided the secret key to get in.
|
||||
$DB->set_field("user", "confirmed", 1, array("id" => $user->id));
|
||||
return AUTH_CONFIRM_OK;
|
||||
}
|
||||
|
||||
@@ -47,5 +47,39 @@ function xmldb_auth_oauth2_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2018051401) {
|
||||
// Fetch Facebook, Google, and Microsoft issuers. We use the URL field to determine the issuer type as it's the only
|
||||
// field that contains the keyword that can somewhat let us reliably determine the issuer type.
|
||||
$likefacebook = $DB->sql_like('oe.url', ':facebook');
|
||||
$likegoogle = $DB->sql_like('oe.url', ':google');
|
||||
$likemicrosoft = $DB->sql_like('oe.url', ':microsoft');
|
||||
|
||||
$params = [
|
||||
'facebook' => '%facebook%',
|
||||
'google' => '%google%',
|
||||
'microsoft' => '%microsoft%',
|
||||
];
|
||||
|
||||
// We're querying from the oauth2_endpoint table because the base URLs of FB and Microsoft can be empty in the issuer table.
|
||||
$subsql = "
|
||||
SELECT DISTINCT oe.issuerid
|
||||
FROM {oauth2_endpoint} oe
|
||||
WHERE $likefacebook
|
||||
OR $likegoogle
|
||||
OR $likemicrosoft";
|
||||
|
||||
// Update non-Facebook/Google/Microsoft issuers and set requireconfirmation to 1.
|
||||
$updatesql = "
|
||||
UPDATE {oauth2_issuer}
|
||||
SET requireconfirmation = 1
|
||||
WHERE id NOT IN ({$subsql})";
|
||||
$DB->execute($updatesql, $params);
|
||||
|
||||
// Delete linked logins for non-Facebook/Google/Microsoft issuers. They can easily re-link their logins anyway.
|
||||
$DB->delete_records_select('auth_oauth2_linked_login', "issuerid NOT IN ($subsql)", $params);
|
||||
|
||||
upgrade_plugin_savepoint(true, 2018051401, 'auth', 'oauth2');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,10 @@ then cut and paste the address into the address
|
||||
line at the top of your web browser window.
|
||||
|
||||
If you need help, please contact the site administrator,
|
||||
{$a->admin}';
|
||||
{$a->admin}
|
||||
|
||||
If you did not do this, someone else could be trying to compromise your account.
|
||||
Please contact the site administrator immediately.';
|
||||
$string['confirmaccountemailsubject'] = '{$a}: account confirmation';
|
||||
$string['confirmationinvalid'] = 'The confirmation link is either invalid, or has expired. Please start the login process again to generate a new confirmation email.';
|
||||
$string['confirmationpending'] = 'This account is pending email confirmation.';
|
||||
@@ -60,7 +63,10 @@ then cut and paste the address into the address
|
||||
line at the top of your web browser window.
|
||||
|
||||
If you need help, please contact the site administrator,
|
||||
{$a->admin}';
|
||||
{$a->admin}
|
||||
|
||||
If you did not do this, someone else could be trying to compromise your account.
|
||||
Please contact the site administrator immediately.';
|
||||
$string['confirmlinkedloginemailsubject'] = '{$a}: linked login confirmation';
|
||||
$string['createaccountswarning'] = 'This authentication plugin allows users to create accounts on your site. You may want to enable the setting "authpreventaccountcreation" if you use this plugin.';
|
||||
$string['createnewlinkedlogin'] = 'Link a new account ({$a})';
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2018051401; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2018050800; // Requires this Moodle version.
|
||||
$plugin->component = 'auth_oauth2'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -182,7 +182,8 @@ How to customize the way the Shibboleth user data is used in Moodle
|
||||
Among the Shibboleth settings in Moodle there is a field that should contain a
|
||||
path to a php file that can be used as data manipulation hook.
|
||||
You can use this if you want to further process the way your Shibboleth
|
||||
attributes are used in Moodle.
|
||||
attributes are used in Moodle. Due to security reasons this file cannot be
|
||||
located within the current site data directory ($CFG->dataroot).
|
||||
|
||||
Example 1: Your Shibboleth federation uses an attribute that specifies the
|
||||
user's preferred language, but the content of this attribute is not
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Special setting for auth_shibboleth convert_data.
|
||||
*
|
||||
* @package auth_shibboleth
|
||||
* @copyright 2020 Mihail Geshoski
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Admin settings class for the convert_data option.
|
||||
*
|
||||
* @package auth_shibboleth
|
||||
* @copyright 2020 Mihail Geshoski
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class auth_shibboleth_admin_setting_convert_data extends admin_setting_configfile {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $visiblename
|
||||
* @param string $description
|
||||
* @param mixed $defaultdirectory
|
||||
*/
|
||||
public function __construct($name, $visiblename, $description, $defaultdirectory) {
|
||||
parent::__construct($name, $visiblename, $description, $defaultdirectory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the file path (location).
|
||||
*
|
||||
* This method ensures that the file defined as a data modification API exists and is not located in the site
|
||||
* data directory ($CFG->dataroot). We should prohibit using files from the site data directory as this introduces
|
||||
* security vulnerabilities.
|
||||
*
|
||||
* @param string $filepath The path to the file.
|
||||
* @return mixed bool true for success or string:error on failure.
|
||||
*/
|
||||
public function validate($filepath) {
|
||||
global $CFG;
|
||||
|
||||
if (empty($filepath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fail if the file does not exist or it is not readable by the webserver process.
|
||||
if (!is_readable($filepath)) {
|
||||
return get_string('auth_shib_convert_data_warning', 'auth_shibboleth');
|
||||
}
|
||||
|
||||
// Fail if the absolute file path matches the currently defined dataroot path.
|
||||
if (preg_match('/' . preg_quote($CFG->dataroot, '/') . '/', realpath($filepath))) {
|
||||
return get_string('auth_shib_convert_data_filepath_warning', 'auth_shibboleth');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
* @return bool result
|
||||
*/
|
||||
function xmldb_auth_shibboleth_upgrade($oldversion) {
|
||||
global $CFG, $DB;
|
||||
global $CFG, $DB, $OUTPUT;
|
||||
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
@@ -51,5 +51,27 @@ function xmldb_auth_shibboleth_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2018051401) {
|
||||
// The 'Data modification API' setting in the Shibboleth authentication plugin can no longer be configured
|
||||
// to use files located within the site data directory, as it exposes the site to security risks. Therefore,
|
||||
// we need to find every existing case and reset the 'Data modification API' setting to its default value.
|
||||
|
||||
$convertdataconfig = get_config('auth_shibboleth', 'convert_data');
|
||||
|
||||
if (preg_match('/' . preg_quote($CFG->dataroot, '/') . '/', realpath($convertdataconfig))) {
|
||||
set_config('convert_data', '', 'auth_shibboleth');
|
||||
|
||||
$warn = 'Your \'Data modification API\' setting in the Shibboleth authentication plugin is currently
|
||||
configured to use a file located within the current site data directory ($CFG->dataroot). You are no
|
||||
longer able to use files from within this directory for this purpose as it exposes your site to security
|
||||
risks. This setting has been reset to its default value. Please reconfigure it by providing a path
|
||||
to a file which is not located within the site data directory.';
|
||||
|
||||
echo $OUTPUT->notification($warn, 'notifyproblem');
|
||||
}
|
||||
|
||||
upgrade_plugin_savepoint(true, 2018051401, 'auth', 'shibboleth');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ $string['auth_shibboleth_select_organization'] = 'For authentication via Shibbol
|
||||
$string['auth_shib_convert_data'] = 'Data modification API';
|
||||
$string['auth_shib_convert_data_description'] = 'You can use this API to further modify the data provided by Shibboleth. Read the <a href="../auth/shibboleth/README.txt">README</a> for further instructions.';
|
||||
$string['auth_shib_convert_data_warning'] = 'The file does not exist or is not readable by the webserver process!';
|
||||
$string['auth_shib_convert_data_filepath_warning'] = 'You cannot use a file that is located within the current site data directory ($CFG->dataroot) as the data modification API.';
|
||||
$string['auth_shib_changepasswordurl'] = 'Password-change URL';
|
||||
$string['auth_shib_idp_list'] = 'Identity providers';
|
||||
$string['auth_shib_idp_list_description'] = 'Provide a list of Identity Provider entityIDs to let the user choose from on the login page.<br />On each line there must be a comma-separated tuple for entityID of the IdP (see the Shibboleth metadata file) and Name of IdP as it shall be displayed in the drop-down list.<br />As an optional third parameter you can add the location of a Shibboleth session initiator that shall be used in case your Moodle installation is part of a multi federation setup.';
|
||||
|
||||
@@ -28,6 +28,7 @@ if ($ADMIN->fulltree) {
|
||||
// We use a couple of custom admin settings since we need to massage the data before it is inserted into the DB.
|
||||
require_once($CFG->dirroot.'/auth/shibboleth/classes/admin_setting_special_wayf_select.php');
|
||||
require_once($CFG->dirroot.'/auth/shibboleth/classes/admin_setting_special_idp_configtextarea.php');
|
||||
require_once($CFG->dirroot.'/auth/shibboleth/classes/admin_setting_special_convert_data_configfile.php');
|
||||
|
||||
// Introductory explanation.
|
||||
$settings->add(new admin_setting_heading('auth_shibboleth/pluginname', '',
|
||||
@@ -37,8 +38,8 @@ if ($ADMIN->fulltree) {
|
||||
$settings->add(new admin_setting_configtext('auth_shibboleth/user_attribute', get_string('username'),
|
||||
get_string('auth_shib_username_description', 'auth_shibboleth'), '', PARAM_RAW));
|
||||
|
||||
// COnvert Data configuration file.
|
||||
$settings->add(new admin_setting_configfile('auth_shibboleth/convert_data',
|
||||
// Convert Data configuration file.
|
||||
$settings->add(new auth_shibboleth_admin_setting_convert_data('auth_shibboleth/convert_data',
|
||||
get_string('auth_shib_convert_data', 'auth_shibboleth'),
|
||||
get_string('auth_shib_convert_data_description', 'auth_shibboleth'), ''));
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
This files describes API changes in /auth/shibboleth/*,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 3.5.16 ===
|
||||
|
||||
* The 'Data modification API' (convert_data) setting can no longer be configured to use files located within the
|
||||
current site data directory ($CFG->dataroot), as it exposes the site to security risks.
|
||||
|
||||
=== 3.5.2 ===
|
||||
|
||||
* Moved the public function unserializesession in auth/shibboleth/logout.php to auth/shibboleth/classes/helper.php and
|
||||
|
||||
@@ -25,6 +25,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX)
|
||||
$plugin->version = 2018051401; // The current plugin version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2018050800; // Requires this Moodle version
|
||||
$plugin->component = 'auth_shibboleth'; // Full name of the plugin (used for diagnostics)
|
||||
|
||||
@@ -42,10 +42,18 @@ class behat_auth extends behat_base {
|
||||
* Logs in the user. There should exist a user with the same value as username and password.
|
||||
*
|
||||
* @Given /^I log in as "(?P<username_string>(?:[^"]|\\")*)"$/
|
||||
* @param string $username the user to log in as.
|
||||
* @param moodle_url|null $wantsurl optional, URL to go to after logging in.
|
||||
*/
|
||||
public function i_log_in_as($username) {
|
||||
public function i_log_in_as(string $username, moodle_url $wantsurl = null) {
|
||||
|
||||
$loginurl = new moodle_url('/login/index.php');
|
||||
if ($wantsurl !== null) {
|
||||
$loginurl->param('wantsurl', $wantsurl->out_as_local_url());
|
||||
}
|
||||
|
||||
// Visit login page.
|
||||
$this->getSession()->visit($this->locate_path('login/index.php'));
|
||||
$this->execute('behat_general::i_visit', [$loginurl]);
|
||||
|
||||
// Enter username and password.
|
||||
$this->execute('behat_forms::i_set_the_field_to', array('Username', $this->escape($username)));
|
||||
|
||||
@@ -2090,12 +2090,30 @@ class restore_ras_and_caps_structure_step extends restore_structure_step {
|
||||
$data = (object)$data;
|
||||
|
||||
// Check roleid is one of the mapped ones
|
||||
$newroleid = $this->get_mappingid('role', $data->roleid);
|
||||
$newrole = $this->get_mapping('role', $data->roleid);
|
||||
$newroleid = isset($newrole->newitemid) ? $newrole->newitemid : false;
|
||||
$userid = $this->task->get_userid();
|
||||
|
||||
// If newroleid and context are valid assign it via API (it handles dupes and so on)
|
||||
if ($newroleid && $this->task->get_contextid()) {
|
||||
// TODO: assign_capability() needs one userid param to be able to specify our restore userid
|
||||
// TODO: it seems that assign_capability() doesn't check for valid capabilities at all ???
|
||||
assign_capability($data->capability, $data->permission, $newroleid, $this->task->get_contextid());
|
||||
if (!$capability = get_capability_info($data->capability)) {
|
||||
$this->log("Capability '{$data->capability}' was not found!", backup::LOG_WARNING);
|
||||
} else {
|
||||
$context = context::instance_by_id($this->task->get_contextid());
|
||||
$overrideableroles = get_overridable_roles($context, ROLENAME_SHORT);
|
||||
$safecapability = is_safe_capability($capability);
|
||||
|
||||
// Check if the new role is an overrideable role AND if the user performing the restore has the
|
||||
// capability to assign the capability.
|
||||
if (in_array($newrole->info['shortname'], $overrideableroles) &&
|
||||
($safecapability && has_capability('moodle/role:safeoverride', $context, $userid) ||
|
||||
!$safecapability && has_capability('moodle/role:override', $context, $userid))
|
||||
) {
|
||||
assign_capability($data->capability, $data->permission, $newroleid, $this->task->get_contextid());
|
||||
} else {
|
||||
$this->log("Insufficient capability to assign capability '{$data->capability}' to role!", backup::LOG_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2114,11 +2132,22 @@ class restore_default_enrolments_step extends restore_execution_step {
|
||||
}
|
||||
|
||||
$course = $DB->get_record('course', array('id'=>$this->get_courseid()), '*', MUST_EXIST);
|
||||
// Return any existing course enrolment instances.
|
||||
$enrolinstances = enrol_get_instances($course->id, false);
|
||||
|
||||
if ($enrolinstances) {
|
||||
// Something already added instances.
|
||||
// Get the existing enrolment methods in the course.
|
||||
$enrolmethods = array_map(function($enrolinstance) {
|
||||
return $enrolinstance->enrol;
|
||||
}, $enrolinstances);
|
||||
|
||||
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) {
|
||||
foreach ($plugins as $pluginname => $plugin) {
|
||||
// Make sure all default enrolment methods exist in the course.
|
||||
if (!in_array($pluginname, $enrolmethods)) {
|
||||
$plugin->course_updated(true, $course, null);
|
||||
}
|
||||
$plugin->restore_sync_course($course);
|
||||
}
|
||||
|
||||
|
||||
@@ -219,6 +219,7 @@ class restore_ui extends base_ui {
|
||||
$options = array();
|
||||
$options['keep_roles_and_enrolments'] = $this->get_setting_value('keep_roles_and_enrolments');
|
||||
$options['keep_groups_and_groupings'] = $this->get_setting_value('keep_groups_and_groupings');
|
||||
$options['userid'] = $this->controller->get_userid();
|
||||
restore_dbops::delete_course_content($this->controller->get_courseid(), $options);
|
||||
}
|
||||
$this->controller->execute_plan();
|
||||
|
||||
@@ -56,7 +56,7 @@ class behat_backup extends behat_base {
|
||||
// table elements are used, and we need to catch exceptions contantly.
|
||||
|
||||
// Go to homepage.
|
||||
$this->getSession()->visit($this->locate_path('/?redirect=0'));
|
||||
$this->execute('behat_general::i_visit', ['/?redirect=0']);
|
||||
$this->execute("behat_general::wait_until_the_page_is_ready");
|
||||
|
||||
// Click the course link.
|
||||
@@ -98,7 +98,7 @@ class behat_backup extends behat_base {
|
||||
// table elements are used, and we need to catch exceptions contantly.
|
||||
|
||||
// Go to homepage.
|
||||
$this->getSession()->visit($this->locate_path('/?redirect=0'));
|
||||
$this->execute('behat_general::i_visit', ['/?redirect=0']);
|
||||
|
||||
// Click the course link.
|
||||
$this->execute("behat_general::click_link", $backupcourse);
|
||||
@@ -134,7 +134,7 @@ class behat_backup extends behat_base {
|
||||
// table elements are used, and we need to catch exceptions contantly.
|
||||
|
||||
// Go to homepage.
|
||||
$this->getSession()->visit($this->locate_path('/?redirect=0'));
|
||||
$this->execute('behat_general::i_visit', ['/?redirect=0']);
|
||||
$this->execute("behat_general::wait_until_the_page_is_ready");
|
||||
|
||||
// Click the course link.
|
||||
|
||||
@@ -512,6 +512,10 @@ class block_activity_results extends block_base {
|
||||
$fields = implode(',', $fields);
|
||||
$users = $DB->get_records_list('user', 'id', $userids, '', $fields);
|
||||
|
||||
// If configured to view user idnumber, ensure current user can see it.
|
||||
$extrafields = get_extra_user_fields($this->context);
|
||||
$canviewidnumber = (array_search('idnumber', $extrafields) !== false);
|
||||
|
||||
// Ready for output!
|
||||
if ($activity->gradetype == GRADE_TYPE_SCALE) {
|
||||
// We must display the results using scales.
|
||||
@@ -537,10 +541,14 @@ class block_activity_results extends block_base {
|
||||
}
|
||||
$this->content->text .= '</caption><colgroup class="number" />';
|
||||
$this->content->text .= '<colgroup class="name" /><colgroup class="grade" /><tbody>';
|
||||
|
||||
foreach ($best as $userid => $gradeid) {
|
||||
switch ($nameformat) {
|
||||
case B_ACTIVITYRESULTS_NAME_FORMAT_ID:
|
||||
$thisname = get_string('user').' '.$users[$userid]->idnumber;
|
||||
$thisname = get_string('user');
|
||||
if ($canviewidnumber) {
|
||||
$thisname .= ' ' . s($users[$userid]->idnumber);
|
||||
}
|
||||
break;
|
||||
case B_ACTIVITYRESULTS_NAME_FORMAT_ANON:
|
||||
$thisname = get_string('user');
|
||||
@@ -603,7 +611,10 @@ class block_activity_results extends block_base {
|
||||
foreach ($worst as $userid => $gradeid) {
|
||||
switch ($nameformat) {
|
||||
case B_ACTIVITYRESULTS_NAME_FORMAT_ID:
|
||||
$thisname = get_string('user').' '.$users[$userid]->idnumber;
|
||||
$thisname = get_string('user');
|
||||
if ($canviewidnumber) {
|
||||
$thisname .= ' ' . s($users[$userid]->idnumber);
|
||||
};
|
||||
break;
|
||||
case B_ACTIVITYRESULTS_NAME_FORMAT_ANON:
|
||||
$thisname = get_string('user');
|
||||
|
||||
@@ -139,7 +139,9 @@ Feature: The activity results block displays student high scores
|
||||
And I should see "70.00" in the "Activity results" "block"
|
||||
|
||||
Scenario: Try to configure the block on the course page to show multiple high scores using ID numbers
|
||||
Given I add the "Activity results" block
|
||||
Given the following config values are set as admin:
|
||||
| showuseridentity | idnumber,email |
|
||||
And I add the "Activity results" block
|
||||
When I configure the "Activity results" block
|
||||
And I set the following fields to these values:
|
||||
| id_config_showbest | 3 |
|
||||
|
||||
@@ -78,7 +78,9 @@ Feature: The activity results block displays students high scores in group as sc
|
||||
And I should see "Good" in the "Activity results" "block"
|
||||
|
||||
Scenario: Try to configure the block on the course page to show multiple high scores using ID numbers
|
||||
Given I add the "Activity results" block
|
||||
Given the following config values are set as admin:
|
||||
| showuseridentity | idnumber,email |
|
||||
And I add the "Activity results" block
|
||||
When I configure the "Activity results" block
|
||||
And I set the following fields to these values:
|
||||
| id_config_showbest | 3 |
|
||||
|
||||
@@ -110,7 +110,9 @@ Feature: The activity results block displays student in group high scores as sca
|
||||
And I should see "Good" in the "Activity results" "block"
|
||||
|
||||
Scenario: Try to configure the block on the course page to show multiple high scores using ID numbers
|
||||
Given I add the "Activity results" block
|
||||
Given the following config values are set as admin:
|
||||
| showuseridentity | idnumber,email |
|
||||
And I add the "Activity results" block
|
||||
When I configure the "Activity results" block
|
||||
And I set the following fields to these values:
|
||||
| id_config_showbest | 3 |
|
||||
@@ -123,11 +125,13 @@ Feature: The activity results block displays student in group high scores as sca
|
||||
And I should see "Very good" in the "Activity results" "block"
|
||||
And I should see "Good" in the "Activity results" "block"
|
||||
And I log out
|
||||
# Students cannot see user identity fields.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I should see "User S1" in the "Activity results" "block"
|
||||
And I should see "User" in the "Activity results" "block"
|
||||
And I should not see "User S1" in the "Activity results" "block"
|
||||
And I should see "Excellent!" in the "Activity results" "block"
|
||||
And I should see "User S2" in the "Activity results" "block"
|
||||
And I should not see "User S2" in the "Activity results" "block"
|
||||
And I should see "Very good" in the "Activity results" "block"
|
||||
|
||||
Scenario: Try to configure the block on the course page to show multiple high scores using anonymous names
|
||||
|
||||
@@ -184,7 +184,9 @@ Feature: The activity results block displays student in separate groups scores
|
||||
And I should see "90.00" in the "Activity results" "block"
|
||||
|
||||
Scenario: Try to configure the block on the course page to show multiple high scores using ID numbers
|
||||
Given I add the "Activity results" block
|
||||
Given the following config values are set as admin:
|
||||
| showuseridentity | idnumber,email |
|
||||
And I add the "Activity results" block
|
||||
When I configure the "Activity results" block
|
||||
And I set the following fields to these values:
|
||||
| id_config_showbest | 3 |
|
||||
@@ -198,11 +200,13 @@ Feature: The activity results block displays student in separate groups scores
|
||||
And I should see "85.00%" in the "Activity results" "block"
|
||||
And I should see "75.00%" in the "Activity results" "block"
|
||||
And I log out
|
||||
# Students cannot see user identity fields.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I should see "User S1" in the "Activity results" "block"
|
||||
And I should see "User" in the "Activity results" "block"
|
||||
And I should not see "User S1" in the "Activity results" "block"
|
||||
And I should see "100.00%" in the "Activity results" "block"
|
||||
And I should see "User S2" in the "Activity results" "block"
|
||||
And I should not see "User S2" in the "Activity results" "block"
|
||||
And I should see "90.00%" in the "Activity results" "block"
|
||||
|
||||
Scenario: Try to configure the block on the course page to show multiple high scores using anonymous names
|
||||
|
||||
@@ -168,7 +168,9 @@ Feature: The activity results block displays student in visible groups scores
|
||||
And I should see "75.00" in the "Activity results" "block"
|
||||
|
||||
Scenario: Try to configure the block on the course page to show multiple high scores using ID numbers
|
||||
Given I add the "Activity results" block
|
||||
Given the following config values are set as admin:
|
||||
| showuseridentity | idnumber,email |
|
||||
And I add the "Activity results" block
|
||||
When I configure the "Activity results" block
|
||||
And I set the following fields to these values:
|
||||
| id_config_showbest | 3 |
|
||||
|
||||
@@ -128,7 +128,9 @@ Feature: The activity results block displays student low scores
|
||||
And I should see "70.00" in the "Activity results" "block"
|
||||
|
||||
Scenario: Try to configure the block on the course page to show multiple low scores using ID numbers
|
||||
Given I add the "Activity results" block
|
||||
Given the following config values are set as admin:
|
||||
| showuseridentity | idnumber,email |
|
||||
And I add the "Activity results" block
|
||||
When I configure the "Activity results" block
|
||||
And I set the following fields to these values:
|
||||
| id_config_showbest | 0 |
|
||||
|
||||
@@ -79,7 +79,9 @@ Feature: The activity results block displays student low scores as scales
|
||||
And I should see "Good" in the "Activity results" "block"
|
||||
|
||||
Scenario: Try to configure the block on the course page to show multiple low scores using ID numbers
|
||||
Given I add the "Activity results" block
|
||||
Given the following config values are set as admin:
|
||||
| showuseridentity | idnumber,email |
|
||||
And I add the "Activity results" block
|
||||
When I configure the "Activity results" block
|
||||
And I set the following fields to these values:
|
||||
| id_config_showbest | 0 |
|
||||
|
||||
@@ -108,7 +108,9 @@ Feature: The activity results block displays students in groups low scores as sc
|
||||
And I should see "Good" in the "Activity results" "block"
|
||||
|
||||
Scenario: Try to configure the block on the course page to show multiple high scores using ID numbers
|
||||
Given I add the "Activity results" block
|
||||
Given the following config values are set as admin:
|
||||
| showuseridentity | idnumber,email |
|
||||
And I add the "Activity results" block
|
||||
When I configure the "Activity results" block
|
||||
And I set the following fields to these values:
|
||||
| id_config_showbest | 0 |
|
||||
@@ -120,11 +122,13 @@ Feature: The activity results block displays students in groups low scores as sc
|
||||
And I should see "Very good" in the "Activity results" "block"
|
||||
And I should see "Good" in the "Activity results" "block"
|
||||
And I log out
|
||||
# Students cannot see user identity fields.
|
||||
And I log in as "student5"
|
||||
And I am on "Course 1" course homepage
|
||||
And I should see "User S5" in the "Activity results" "block"
|
||||
And I should see "User" in the "Activity results" "block"
|
||||
And I should not see "User S5" in the "Activity results" "block"
|
||||
And I should see "Good" in the "Activity results" "block"
|
||||
And I should see "User S6" in the "Activity results" "block"
|
||||
And I should not see "User S6" in the "Activity results" "block"
|
||||
And I should see "Average" in the "Activity results" "block"
|
||||
|
||||
Scenario: Try to configure the block on the course page to show multiple high scores using anonymous names
|
||||
|
||||
@@ -178,7 +178,9 @@ Feature: The activity results block displays students in separate groups scores
|
||||
And I should see "70.00" in the "Activity results" "block"
|
||||
|
||||
Scenario: Try to configure the block on the course page to show multiple low scores using ID numbers
|
||||
Given I add the "Activity results" block
|
||||
Given the following config values are set as admin:
|
||||
| showuseridentity | idnumber,email |
|
||||
And I add the "Activity results" block
|
||||
When I configure the "Activity results" block
|
||||
And I set the following fields to these values:
|
||||
| id_config_showbest | 0 |
|
||||
@@ -191,11 +193,13 @@ Feature: The activity results block displays students in separate groups scores
|
||||
And I should see "85.00%" in the "Activity results" "block"
|
||||
And I should see "75.00%" in the "Activity results" "block"
|
||||
And I log out
|
||||
# Students cannot see user identity fields.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I should see "User S1" in the "Activity results" "block"
|
||||
And I should see "User" in the "Activity results" "block"
|
||||
And I should not see "User S1" in the "Activity results" "block"
|
||||
And I should see "100.00%" in the "Activity results" "block"
|
||||
And I should see "User S2" in the "Activity results" "block"
|
||||
And I should not see "User S2" in the "Activity results" "block"
|
||||
And I should see "90.00%" in the "Activity results" "block"
|
||||
|
||||
Scenario: Try to configure the block on the course page to show multiple low scores using anonymous names
|
||||
|
||||
@@ -166,7 +166,9 @@ Feature: The activity results block displays student in visible groups low score
|
||||
And I should see "75.00" in the "Activity results" "block"
|
||||
|
||||
Scenario: Try to configure the block on the course page to show multiple low scores using ID numbers
|
||||
Given I add the "Activity results" block
|
||||
Given the following config values are set as admin:
|
||||
| showuseridentity | idnumber,email |
|
||||
And I add the "Activity results" block
|
||||
When I configure the "Activity results" block
|
||||
And I set the following fields to these values:
|
||||
| id_config_showbest | 0 |
|
||||
|
||||
@@ -9,9 +9,14 @@ Feature: Random glossary entry block linking to global glossary
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
| Course 2 | C2 |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber | globalglossary | defaultapproval |
|
||||
| glossary | Tips and Tricks | Frontpage glossary description | C2 | glossary0 | 1 | 1 |
|
||||
And the following "activity" exists:
|
||||
| activity | glossary |
|
||||
| name | Tips and Tricks |
|
||||
| intro | Frontpage glossary description |
|
||||
| course | C2 |
|
||||
| idnumber | glossary0 |
|
||||
| globalglossary | 1 |
|
||||
| defaultapproval | 1 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Sam1 | Student1 | student1@example.com |
|
||||
|
||||
@@ -88,14 +88,23 @@ class block_online_users extends block_base {
|
||||
|
||||
$this->content->text = '<div class="info">'.$usercount.' ('.$periodminutes.')</div>';
|
||||
|
||||
// Verify if we can see the list of users, if not just print number of users
|
||||
if (!has_capability('block/online_users:viewlist', $this->page->context)) {
|
||||
// Verify if we can see the list of users, if not just print number of users.
|
||||
// If the current user is not logged in OR it's a guest then don't show any users.
|
||||
if (!has_capability('block/online_users:viewlist', $this->page->context)
|
||||
|| isguestuser() || !isloggedin()) {
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
$userlimit = 50; // We'll just take the most recent 50 maximum.
|
||||
$initialcount = 0;
|
||||
if ($users = $onlineusers->get_users($userlimit)) {
|
||||
require_once($CFG->dirroot . '/user/lib.php');
|
||||
$initialcount = count($users);
|
||||
foreach ($users as $user) {
|
||||
if (!user_can_view_profile($user)) {
|
||||
unset($users[$user->id]);
|
||||
continue;
|
||||
}
|
||||
$users[$user->id]->fullname = fullname($user);
|
||||
}
|
||||
} else {
|
||||
@@ -137,6 +146,14 @@ class block_online_users extends block_base {
|
||||
}
|
||||
$this->content->text .= "</li>\n";
|
||||
}
|
||||
if ($initialcount - count($users) > 0) {
|
||||
$this->content->text .= '<li class="listentry"><div class="otherusers">';
|
||||
$this->content->text .= html_writer::span(
|
||||
get_string('otherusers', 'block_online_users', $initialcount - count($users))
|
||||
);
|
||||
$this->content->text .= "</div>";
|
||||
$this->content->text .= "</li>\n";
|
||||
}
|
||||
$this->content->text .= '</ul><div class="clearer"><!-- --></div>';
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ class fetcher {
|
||||
$timeaccess = ", ul.timeaccess AS lastaccess";
|
||||
$params = array();
|
||||
|
||||
$userfields = \user_picture::fields('u', array('username'));
|
||||
$userfields = \user_picture::fields('u', array('username', 'deleted'));
|
||||
|
||||
// Add this to the SQL to show only group users.
|
||||
if ($currentgroup !== null) {
|
||||
|
||||
@@ -55,7 +55,7 @@ $capabilities = array(
|
||||
'contextlevel' => CONTEXT_BLOCK,
|
||||
'archetypes' => array(
|
||||
'user' => CAP_ALLOW,
|
||||
'guest' => CAP_ALLOW,
|
||||
'guest' => CAP_PREVENT,
|
||||
'student' => CAP_ALLOW,
|
||||
'teacher' => CAP_ALLOW,
|
||||
'editingteacher' => CAP_ALLOW,
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['otherusers'] = 'Other Users ({$a})';
|
||||
$string['configtimetosee'] = 'Number of minutes determining the period of inactivity after which a user is no longer considered to be online.';
|
||||
$string['nouser'] = 'No online users';
|
||||
$string['numuser'] = '{$a} online user';
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.block_online_users .content .list li.listentry .otherusers {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
.block_online_users .content .list li.listentry .user .userpicture {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@block @block_online_users
|
||||
Feature: The online users block allow you to see who is currently online on dashboard
|
||||
There should be some commonality for the users to show up
|
||||
In order to use the online users block on the dashboard
|
||||
As a user
|
||||
I can view the online users block on my dashboard
|
||||
@@ -10,6 +11,14 @@ Feature: The online users block allow you to see who is currently online on dash
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| student2 | Student | 2 | student2@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
|
||||
Scenario: View the online users block on the dashboard and see myself
|
||||
Given I log in as "teacher1"
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
@block @block_online_users
|
||||
Feature: The online users block allow you to see who is currently online on frontpage
|
||||
In order to enable the online users block on the front page page
|
||||
There should be some commonality for the users to show up
|
||||
In order to enable the online users block on the frontpage
|
||||
As an admin
|
||||
I can add the online users block to the front page page
|
||||
I can add the online users block to the frontpage
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| student2 | Student | 2 | student2@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
|
||||
Scenario: View the online users block on the front page and see myself
|
||||
Given I log in as "admin"
|
||||
@@ -28,7 +36,8 @@ Feature: The online users block allow you to see who is currently online on fron
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I am on site homepage
|
||||
Then I should see "Admin User" in the "Online users" "block"
|
||||
Then I should not see "Admin User" in the "Online users" "block"
|
||||
And I should see "Other Users (1)" in the "Online users" "block"
|
||||
And I should see "Student 1" in the "Online users" "block"
|
||||
And I should see "Student 2" in the "Online users" "block"
|
||||
And I should see "3 online users" in the "Online users" "block"
|
||||
@@ -45,7 +54,7 @@ Feature: The online users block allow you to see who is currently online on fron
|
||||
And I log out
|
||||
When I log in as "guest"
|
||||
And I am on site homepage
|
||||
Then I should see "Admin User" in the "Online users" "block"
|
||||
And I should see "Student 1" in the "Online users" "block"
|
||||
And I should see "Student 2" in the "Online users" "block"
|
||||
Then I should not see "Admin User" in the "Online users" "block"
|
||||
And I should not see "Student 1" in the "Online users" "block"
|
||||
And I should not see "Student 2" in the "Online users" "block"
|
||||
And I should see "3 online users" in the "Online users" "block"
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX)
|
||||
$plugin->version = 2018051401; // The current plugin version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2018050800; // Requires this Moodle version
|
||||
$plugin->component = 'block_online_users'; // Full name of the plugin (used for diagnostics)
|
||||
|
||||
@@ -114,12 +114,8 @@ class behat_block_site_main_menu extends behat_base {
|
||||
protected function get_site_menu_activity_element($element, $selectortype, $activityname) {
|
||||
$activitynode = $this->get_site_menu_activity_node($activityname);
|
||||
|
||||
// Transforming to Behat selector/locator.
|
||||
list($selector, $locator) = $this->transform_selector($selectortype, $element);
|
||||
$exception = new ElementNotFoundException($this->getSession(), '"' . $element . '" "' .
|
||||
$selectortype . '" in "' . $activityname . '" ');
|
||||
|
||||
return $this->find($selector, $locator, $exception, $activitynode);
|
||||
$exception = new ElementNotFoundException($this->getSession(), "'{$element}' '{$selectortype}' in '${activityname}'");
|
||||
return $this->find($selectortype, $element, $exception, $activitynode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,4 +150,17 @@ class behat_block_site_main_menu extends behat_base {
|
||||
$xpath = "//*[contains(concat(' ',normalize-space(@class),' '),' block_site_main_menu ')]//li[contains(., $activityname)]";
|
||||
$this->execute('behat_action_menu::i_open_the_action_menu_in', [$xpath, 'xpath_element']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of partial named selectors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_partial_named_selectors(): array {
|
||||
return [
|
||||
new behat_component_named_selector('Activity', [
|
||||
"//*[contains(concat(' ',normalize-space(@class),' '),' block_site_main_menu ')]//li[contains(., %locator%)]"
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,16 @@ Feature: Edit activities in main menu block
|
||||
|
||||
@javascript
|
||||
Scenario: Edit name of acitivity in-place in site main menu block
|
||||
Given I log in as "admin"
|
||||
Given the following "activity" exists:
|
||||
| activity | forum |
|
||||
| course | Acceptance test site |
|
||||
| name | My forum name |
|
||||
| idnumber | forum |
|
||||
And I log in as "admin"
|
||||
And I am on site homepage
|
||||
And I navigate to "Turn editing on" node in "Front page settings"
|
||||
And I add the "Main menu" block
|
||||
When I add a "Forum" to section "0" and I fill the form with:
|
||||
| Forum name | My forum name |
|
||||
And I click on "Edit title" "link" in the "My forum name" activity in site main menu block
|
||||
And I set the field "New name for activity My forum name" to "New forum name"
|
||||
And I press key "13" in the field "New name for activity My forum name"
|
||||
When I set the field "Edit title" in the "My forum name" "block_site_main_menu > Activity" to "New forum name"
|
||||
Then I should not see "My forum name"
|
||||
And I should see "New forum name"
|
||||
And I follow "New forum name"
|
||||
|
||||
@@ -114,12 +114,8 @@ class behat_block_social_activities extends behat_base {
|
||||
protected function get_social_block_activity_element($element, $selectortype, $activityname) {
|
||||
$activitynode = $this->get_social_block_activity_node($activityname);
|
||||
|
||||
// Transforming to Behat selector/locator.
|
||||
list($selector, $locator) = $this->transform_selector($selectortype, $element);
|
||||
$exception = new ElementNotFoundException($this->getSession(), '"' . $element . '" "' .
|
||||
$selectortype . '" in "' . $activityname . '" ');
|
||||
|
||||
return $this->find($selector, $locator, $exception, $activitynode);
|
||||
$exception = new ElementNotFoundException($this->getSession(), "'{$element}' '{$selectortype}' in '${activityname}'");
|
||||
return $this->find($selectortype, $element, $exception, $activitynode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,4 +150,17 @@ class behat_block_social_activities extends behat_base {
|
||||
$xpath = "//*[contains(concat(' ',normalize-space(@class),' '),' block_social_activities ')]//li[contains(., $activityname)]";
|
||||
$this->execute('behat_action_menu::i_open_the_action_menu_in', [$xpath, 'xpath_element']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of partial named selectors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_partial_named_selectors(): array {
|
||||
return [
|
||||
new behat_component_named_selector('Activity', [
|
||||
"//*[contains(concat(' ',normalize-space(@class),' '),' block_social_activities ')]//li[contains(., %locator%)]",
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,7 @@ Feature: Edit activities in social activities block
|
||||
And I set the field "Add an activity to section 'section 0'" to "Forum"
|
||||
And I set the field "Forum name" to "My forum name"
|
||||
And I press "Save and return to course"
|
||||
And I click on "Edit title" "link" in the "My forum name" activity in social activities block
|
||||
And I set the field "New name for activity My forum name" to "New forum name"
|
||||
And I press key "13" in the field "New name for activity My forum name"
|
||||
When I set the field "Edit title" in the "My forum name" "block_social_activities > Activity" to "New forum name"
|
||||
Then I should not see "My forum name" in the "Social activities" "block"
|
||||
And I should see "New forum name"
|
||||
And I follow "New forum name"
|
||||
@@ -82,4 +80,3 @@ Feature: Edit activities in social activities block
|
||||
And I should not see "My forum name" in the "Social activities" "block"
|
||||
And I click on "My forum name" "link" in the "Recent activity" "block"
|
||||
And I should see "My forum name" in the ".breadcrumb" "css_element"
|
||||
And I log out
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core_calendar/selectors","core_calendar/events","core/str","core/templates"],function(a,b,c,d,e){var f=function(d){d.on("click",b.eventFilterItem,function(b){var c=a(b.currentTarget);g(c),b.preventDefault()}),a("body").on(c.viewUpdated,function(){var c=d.find(b.eventFilterItem);c.each(function(b,c){if(c=a(c),c.data("eventtype-hidden")){var d=i(c);h(d)}})})},g=function(a){var b=i(a);return b.hidden=!b.hidden,d.get_string("eventtype"+b.eventtype,"calendar").then(function(a){return b.name=a,b}).then(function(a){return e.render("core_calendar/event_filter_key",a)}).then(function(b,c){return e.replaceNode(a,b,c)}).then(function(){h(b)})},h=function(b){M.util.js_pending("month-mini-filterChanged"),a("body").trigger(c.filterChanged,{type:b.eventtype,hidden:b.hidden}),M.util.js_complete("month-mini-filterChanged")},i=function(a){return{eventtype:a.data("eventtype"),hidden:a.data("eventtype-hidden")}};return{init:function(b){b=a(b),f(b)}}});
|
||||
define(["jquery","core_calendar/selectors","core_calendar/events","core/str","core/templates"],function(a,b,c,d,e){var f=function(d){d.on("click",b.eventFilterItem,function(b){var c=a(b.currentTarget);g(c),b.preventDefault()}),a("body").on(c.viewUpdated,function(){var c=d.find(b.eventFilterItem);c.each(function(b,c){if(c=a(c),c.data("eventtype-hidden")){var d=i(c);h(d)}})})},g=function(a){var b=i(a);return b.hidden=!b.hidden,M.util.js_pending("core_calendar/calendar_filter:toggleFilter"),d.get_string("eventtype"+b.eventtype,"calendar").then(function(a){return b.name=a,b}).then(function(a){return e.render("core_calendar/event_filter_key",a)}).then(function(b,c){return e.replaceNode(a,b,c)}).then(function(){h(b),M.util.js_complete("core_calendar/calendar_filter:toggleFilter")})},h=function(b){M.util.js_pending("month-mini-filterChanged"),a("body").trigger(c.filterChanged,{type:b.eventtype,hidden:b.hidden}),M.util.js_complete("month-mini-filterChanged")},i=function(a){return{eventtype:a.data("eventtype"),hidden:a.data("eventtype-hidden")}};return{init:function(b){b=a(b),f(b)}}});
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/modal_event_form","core_calendar/repository","core_calendar/events","core_calendar/modal_delete","core_calendar/selectors"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){function n(d,e,f){var i=[{key:"deleteevent",component:"calendar"}];f=parseInt(f,10);var m,n=f>1;n?(i.push({key:"confirmeventseriesdelete",component:"calendar",param:{name:e,count:f}}),m=g.create({type:l.TYPE})):(i.push({key:"confirmeventdelete",component:"calendar",param:e}),m=g.create({type:g.types.SAVE_CANCEL})),m.then(function(a){a.show()}).fail(c.exception);var o=b.get_strings(i),p=a.when(o,m).then(function(b,e){return e.setTitle(b[0]),e.setBody(b[1]),n||e.setSaveButtonText(b[0]),e.getRoot().on(h.save,function(){j.deleteEvent(d,!1).then(function(){a("body").trigger(k.deleted,[d,!1])})["catch"](c.exception)}),e.getRoot().on(k.deleteAll,function(){j.deleteEvent(d,!0).then(function(){a("body").trigger(k.deleted,[d,!0])})["catch"](c.exception)}),e}).fail(c.exception);return p}function o(b){b.on("click",m.actions.remove,function(b){var c=a(this).closest(m.eventItem),d=c.data("eventId"),e=c.data("eventTitle"),f=c.data("eventCount");n(d,e,f),b.preventDefault()})}function p(b,d){return d.then(function(c){a("body").on(k.editEvent,function(a,d){var e=b.find(m.wrapper);c.setEventId(d),c.setContextId(e.data("contextId")),c.show(),a.stopImmediatePropagation()})}).fail(c.exception),d}var q=function(b){var d=g.create({type:i.TYPE,large:!0});return b.on("click",m.actions.create,function(a){d.then(function(a){var c=b.find(m.wrapper),d=c.data("categoryid");"undefined"!=typeof d&&a.setCategoryId(d);var e=b.find(m.today),f=b.find(m.day);!e.length&&f.length&&a.setStartTime(f.data("newEventTimestamp")),a.setContextId(c.data("contextId")),a.setCourseId(c.data("courseid")),a.show()}).fail(c.exception),a.preventDefault()}),b.on("click",m.actions.edit,function(b){b.preventDefault();var e=a(b.currentTarget),f=e.closest(m.wrapper),g=e.closest(m.eventItem);d.then(function(a){a.setEventId(g.data("eventId")),a.setContextId(f.data("contextId")),a.show(),b.stopImmediatePropagation()}).fail(c.exception)}),d};return{registerRemove:o,registerEditListeners:p,registerEventFormModal:q}});
|
||||
define(["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/modal_event_form","core_calendar/repository","core_calendar/events","core_calendar/modal_delete","core_calendar/selectors","core/pending"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){function o(d,e,f){var i=[{key:"deleteevent",component:"calendar"}];f=parseInt(f,10);var m,o=f>1;o?(i.push({key:"confirmeventseriesdelete",component:"calendar",param:{name:e,count:f}}),m=g.create({type:l.TYPE})):(i.push({key:"confirmeventdelete",component:"calendar",param:e}),m=g.create({type:g.types.SAVE_CANCEL}));var p=b.get_strings(i),q=a.when(p,m).then(function(b,e){return e.setTitle(b[0]),e.setBody(b[1]),o||e.setSaveButtonText(b[0]),e.show(),e.getRoot().on(h.save,function(){var b=new n("calendar/crud:initModal:deletedevent");j.deleteEvent(d,!1).then(function(){a("body").trigger(k.deleted,[d,!1])}).then(b.resolve)["catch"](c.exception)}),e.getRoot().on(k.deleteAll,function(){var b=new n("calendar/crud:initModal:deletedallevent");j.deleteEvent(d,!0).then(function(){a("body").trigger(k.deleted,[d,!0])}).then(b.resolve)["catch"](c.exception)}),e})["catch"](c.exception);return q}function p(b){b.on("click",m.actions.remove,function(b){var c=a(this).closest(m.eventItem),d=c.data("eventId"),e=c.data("eventTitle"),f=c.data("eventCount");o(d,e,f),b.preventDefault()})}function q(b,d){var e=new n("core_calendar/crud:registerEditListeners");return d.then(function(c){return a("body").on(k.editEvent,function(a,d){var e=b.find(m.wrapper);c.setEventId(d),c.setContextId(e.data("contextId")),c.show(),a.stopImmediatePropagation()}),c}).then(function(a){return e.resolve(),a})["catch"](c.exception)}var r=function(b){var d=g.create({type:i.TYPE,large:!0});return b.on("click",m.actions.create,function(a){d.then(function(a){var c=b.find(m.wrapper),d=c.data("categoryid");"undefined"!=typeof d&&a.setCategoryId(d);var e=b.find(m.today),f=b.find(m.day);!e.length&&f.length&&a.setStartTime(f.data("newEventTimestamp")),a.setContextId(c.data("contextId")),a.setCourseId(c.data("courseid")),a.show()}).fail(c.exception),a.preventDefault()}),b.on("click",m.actions.edit,function(b){b.preventDefault();var e=a(b.currentTarget),f=e.closest(m.wrapper),g=e.closest(m.eventItem);d.then(function(a){a.setEventId(g.data("eventId")),a.setContextId(f.data("contextId")),a.show(),b.stopImmediatePropagation()}).fail(c.exception)}),d};return{registerRemove:p,registerEditListeners:q,registerEventFormModal:r}});
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/repository","core_calendar/events","core_calendar/crud"],function(a,b,c,d,e,f,g,h,i,j,k){var l=!1,m={ROOT:"[data-region='summary-modal-container']",EDIT_BUTTON:'[data-action="edit"]',DELETE_BUTTON:'[data-action="delete"]'},n=function(a){e.call(this,a)};return n.TYPE="core_calendar-event_summary",n.prototype=Object.create(e.prototype),n.prototype.constructor=n,n.prototype.getEditButton=function(){return"undefined"==typeof this.editButton&&(this.editButton=this.getFooter().find(m.EDIT_BUTTON)),this.editButton},n.prototype.getDeleteButton=function(){return"undefined"==typeof this.deleteButton&&(this.deleteButton=this.getFooter().find(m.DELETE_BUTTON)),this.deleteButton},n.prototype.getEventId=function(){return this.getBody().find(m.ROOT).attr("data-event-id")},n.prototype.getEventTitle=function(){return this.getBody().find(m.ROOT).attr("data-event-title")},n.prototype.getEventCount=function(){return this.getBody().find(m.ROOT).attr("data-event-count")},n.prototype.getEditUrl=function(){return this.getBody().find(m.ROOT).attr("data-edit-url")},n.prototype.isActionEvent=function(){return"true"==this.getBody().find(m.ROOT).attr("data-action-event")},n.prototype.registerEventListeners=function(){e.prototype.registerEventListeners.call(this),this.getRoot().on(h.bodyRendered,function(){this.getModal().data({eventTitle:this.getEventTitle(),eventId:this.getEventId(),eventCount:this.getEventCount()}).attr("data-type","event"),k.registerRemove(this.getModal())}.bind(this)),a("body").on(j.deleted,function(){this.hide()}.bind(this)),d.define(this.getEditButton(),[d.events.activate]),this.getEditButton().on(d.events.activate,function(b,c){this.isActionEvent()?a("body").trigger(j.editActionEvent,[this.getEditUrl()]):a("body").trigger(j.editEvent,[this.getEventId()]),this.hide(),b.preventDefault(),b.stopPropagation(),c.originalEvent.preventDefault(),c.originalEvent.stopPropagation()}.bind(this))},l||(f.register(n.TYPE,n,"core_calendar/event_summary_modal"),l=!0),n});
|
||||
define(["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/repository","core_calendar/events","core_calendar/crud"],function(a,b,c,d,e,f,g,h,i,j,k){var l=!1,m={ROOT:"[data-region='summary-modal-container']",EDIT_BUTTON:'[data-action="edit"]',DELETE_BUTTON:'[data-action="delete"]'},n=function(a){e.call(this,a)};return n.TYPE="core_calendar-event_summary",n.prototype=Object.create(e.prototype),n.prototype.constructor=n,n.prototype.getEditButton=function(){return"undefined"==typeof this.editButton&&(this.editButton=this.getFooter().find(m.EDIT_BUTTON)),this.editButton},n.prototype.getDeleteButton=function(){return"undefined"==typeof this.deleteButton&&(this.deleteButton=this.getFooter().find(m.DELETE_BUTTON)),this.deleteButton},n.prototype.getEventId=function(){return this.getBody().find(m.ROOT).attr("data-event-id")},n.prototype.getEventTitle=function(){return this.getBody().find(m.ROOT).attr("data-event-title")},n.prototype.getEventCount=function(){return this.getBody().find(m.ROOT).attr("data-event-count")},n.prototype.getEditUrl=function(){return this.getBody().find(m.ROOT).attr("data-edit-url")},n.prototype.isActionEvent=function(){return"true"==this.getBody().find(m.ROOT).attr("data-action-event")},n.prototype.registerEventListeners=function(){e.prototype.registerEventListeners.call(this),M.util.js_pending("core_calendar/summary_modal:registerEventListeners:bodyRendered"),this.getRoot().on(h.bodyRendered,function(){this.getModal().data({eventTitle:this.getEventTitle(),eventId:this.getEventId(),eventCount:this.getEventCount()}).attr("data-type","event"),k.registerRemove(this.getModal()),M.util.js_complete("core_calendar/summary_modal:registerEventListeners:bodyRendered")}.bind(this)),a("body").on(j.deleted,function(){this.hide()}.bind(this)),d.define(this.getEditButton(),[d.events.activate]),this.getEditButton().on(d.events.activate,function(b,c){this.isActionEvent()?a("body").trigger(j.editActionEvent,[this.getEditUrl()]):a("body").trigger(j.editEvent,[this.getEventId()]),this.hide(),b.preventDefault(),b.stopPropagation(),c.originalEvent.preventDefault(),c.originalEvent.stopPropagation()}.bind(this))},l||(f.register(n.TYPE,n,"core_calendar/event_summary_modal"),l=!0),n});
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/templates","core/str","core/notification","core_calendar/repository","core_calendar/events","core_calendar/selectors","core/modal_factory","core/modal_events","core_calendar/summary_modal"],function(a,b,c,d,e,f,g,h,i,j){var k=function(b){b=a(b),b.on("click",g.links.eventLink,function(b){var c,d=a(b.target),e=null;c=d.is(g.actions.viewEvent)?d:d.closest(g.actions.viewEvent),e=c.length?c.data("eventId"):d.find(g.actions.viewEvent).data("eventId"),e&&(b.preventDefault(),b.stopPropagation(),w(e))}),b.on("click",g.links.navLink,function(c){var d=b.find(g.wrapper),e=d.data("view"),f=d.data("courseid"),h=d.data("categoryid"),i=a(c.currentTarget);"month"===e?(m(b,i.attr("href"),i.data("year"),i.data("month"),f,h),c.preventDefault()):"day"===e&&(q(b,i.attr("href"),i.data("year"),i.data("month"),i.data("day"),f,h),c.preventDefault())})},l=function(c,h,i,j,k,l){r(c),l=l||c.find(g.wrapper),M.util.js_pending([c.get("id"),h,i,j].join("-"));var m=c.data("includenavigation"),n=c.data("mini");return e.getCalendarMonthData(h,i,j,k,m,n).then(function(a){return b.render(c.attr("data-template"),a)}).then(function(a,c){return b.replaceNode(l,a,c)}).then(function(){a("body").trigger(f.viewUpdated)}).always(function(){return M.util.js_complete([c.get("id"),h,i,j].join("-")),s(c)}).fail(d.exception)},m=function(b,c,d,e,g,h){return l(b,d,e,g,h).then(function(){return c.length&&"#"!==c&&window.history.pushState({},"",c),arguments}).then(function(){return a("body").trigger(f.monthChanged,[d,e,g,h]),arguments})},n=function(a,b,c){var d=a.find(g.wrapper).data("year"),e=a.find(g.wrapper).data("month");return"undefined"==typeof b&&(b=a.find(g.wrapper).data("courseid")),"undefined"==typeof c&&(c=a.find(g.wrapper).data("categoryid")),l(a,d,e,b,c)},o=function(c,h,i,j,k,l,m){r(c),m=m||c.find(g.wrapper),M.util.js_pending([c.get("id"),h,i,j,k,l].join("-"));var n=c.data("includenavigation");return e.getCalendarDayData(h,i,j,k,l,n).then(function(a){return b.render(c.attr("data-template"),a)}).then(function(a,c){return b.replaceNode(m,a,c)}).then(function(){a("body").trigger(f.viewUpdated)}).always(function(){return M.util.js_complete([c.get("id"),h,i,j,k,l].join("-")),s(c)}).fail(d.exception)},p=function(a,b,c){var d=a.find(g.wrapper),e=d.data("year"),f=d.data("month"),h=d.data("day");return b||(b=a.find(g.wrapper).data("courseid")),"undefined"==typeof c&&(c=a.find(g.wrapper).data("categoryid")),o(a,e,f,h,b,c)},q=function(b,c,d,e,g,h,i){return o(b,d,e,g,h,i).then(function(){return c.length&&"#"!==c&&window.history.pushState({},"",c),arguments}).then(function(){return a("body").trigger(f.dayChanged,[d,e,g,h,i]),arguments})},r=function(a){var b=a.find(g.containers.loadingIcon);b.removeClass("hidden")},s=function(a){var b=a.find(g.containers.loadingIcon);b.addClass("hidden")},t=function(c,h,i){r(c);var j=c.find(g.wrapper);return"undefined"==typeof h&&(h=c.find(g.wrapper).data("courseid")),"undefined"==typeof i&&(i=c.find(g.wrapper).data("categoryid")),e.getCalendarUpcomingData(h,i).then(function(a){return b.render(c.attr("data-template"),a)}).then(function(a,c){return b.replaceNode(j,a,c)}).then(function(){a("body").trigger(f.viewUpdated)}).always(function(){return s(c)}).fail(d.exception)},u=function(a){switch(a){case"user":return"user";case"site":return"site";case"group":return"group";case"category":return"category";default:return"course"}},v=function(a){return"calendar_event_"+u(a)},w=function(a){var c="";e.getEventById(a).then(function(b){if(!b.event)throw new Error("Error encountered while trying to fetch calendar event with ID: "+a);var d=b.event;return c=v(d.eventtype),x(d.eventtype).then(function(a){return d.eventtype=a,d})}).then(function(a){var d={title:a.name,type:j.TYPE,body:b.render("core_calendar/event_summary_body",a),templateContext:{canedit:a.canedit,candelete:a.candelete,headerclasses:c,isactionevent:a.isactionevent,url:a.url}};return h.create(d)}).done(function(a){a.getRoot().on(i.hidden,function(){a.destroy()}),a.show()}).fail(d.exception)},x=function(a){var b="type"+u(a);return c.get_string(b,"core_calendar").then(function(a){return a})};return{init:function(a){k(a)},reloadCurrentMonth:n,changeMonth:m,refreshMonthContent:l,reloadCurrentDay:p,changeDay:q,refreshDayContent:o,reloadCurrentUpcoming:t}});
|
||||
define(["jquery","core/templates","core/str","core/notification","core_calendar/repository","core_calendar/events","core_calendar/selectors","core/modal_factory","core/modal_events","core_calendar/summary_modal","core/pending"],function(a,b,c,d,e,f,g,h,i,j,k){var l=function(b){b=a(b),b.on("click",g.links.eventLink,function(b){var c,d=new k("core_calendar/view_manager:eventLink:click"),e=a(b.target),f=null;c=e.is(g.actions.viewEvent)?e:e.closest(g.actions.viewEvent),f=c.length?c.data("eventId"):e.find(g.actions.viewEvent).data("eventId"),f?(b.preventDefault(),b.stopPropagation(),x(f).then(d.resolve())["catch"]()):d.resolve()}),b.on("click",g.links.navLink,function(c){var d=b.find(g.wrapper),e=d.data("view"),f=d.data("courseid"),h=d.data("categoryid"),i=a(c.currentTarget);"month"===e?(n(b,i.attr("href"),i.data("year"),i.data("month"),f,h),c.preventDefault()):"day"===e&&(r(b,i.attr("href"),i.data("year"),i.data("month"),i.data("day"),f,h),c.preventDefault())})},m=function(c,h,i,j,k,l){s(c),l=l||c.find(g.wrapper),M.util.js_pending([c.get("id"),h,i,j].join("-"));var m=c.data("includenavigation"),n=c.data("mini");return e.getCalendarMonthData(h,i,j,k,m,n).then(function(a){return b.render(c.attr("data-template"),a)}).then(function(a,c){return b.replaceNode(l,a,c)}).then(function(){a("body").trigger(f.viewUpdated)}).always(function(){return M.util.js_complete([c.get("id"),h,i,j].join("-")),t(c)}).fail(d.exception)},n=function(b,c,d,e,g,h){return m(b,d,e,g,h).then(function(){return c.length&&"#"!==c&&window.history.pushState({},"",c),arguments}).then(function(){return a("body").trigger(f.monthChanged,[d,e,g,h]),arguments})},o=function(a,b,c){var d=a.find(g.wrapper).data("year"),e=a.find(g.wrapper).data("month");return"undefined"==typeof b&&(b=a.find(g.wrapper).data("courseid")),"undefined"==typeof c&&(c=a.find(g.wrapper).data("categoryid")),m(a,d,e,b,c)},p=function(c,h,i,j,k,l,m){s(c),m=m||c.find(g.wrapper),M.util.js_pending([c.get("id"),h,i,j,k,l].join("-"));var n=c.data("includenavigation");return e.getCalendarDayData(h,i,j,k,l,n).then(function(a){return b.render(c.attr("data-template"),a)}).then(function(a,c){return b.replaceNode(m,a,c)}).then(function(){a("body").trigger(f.viewUpdated)}).always(function(){return M.util.js_complete([c.get("id"),h,i,j,k,l].join("-")),t(c)}).fail(d.exception)},q=function(a,b,c){var d=a.find(g.wrapper),e=d.data("year"),f=d.data("month"),h=d.data("day");return b||(b=a.find(g.wrapper).data("courseid")),"undefined"==typeof c&&(c=a.find(g.wrapper).data("categoryid")),p(a,e,f,h,b,c)},r=function(b,c,d,e,g,h,i){return p(b,d,e,g,h,i).then(function(){return c.length&&"#"!==c&&window.history.pushState({},"",c),arguments}).then(function(){return a("body").trigger(f.dayChanged,[d,e,g,h,i]),arguments})},s=function(a){var b=a.find(g.containers.loadingIcon);b.removeClass("hidden")},t=function(a){var b=a.find(g.containers.loadingIcon);b.addClass("hidden")},u=function(c,h,i){s(c);var j=c.find(g.wrapper);return"undefined"==typeof h&&(h=c.find(g.wrapper).data("courseid")),"undefined"==typeof i&&(i=c.find(g.wrapper).data("categoryid")),e.getCalendarUpcomingData(h,i).then(function(a){return b.render(c.attr("data-template"),a)}).then(function(a,c){return b.replaceNode(j,a,c)}).then(function(){a("body").trigger(f.viewUpdated)}).always(function(){return t(c)}).fail(d.exception)},v=function(a){switch(a){case"user":return"user";case"site":return"site";case"group":return"group";case"category":return"category";default:return"course"}},w=function(a){return"calendar_event_"+v(a)},x=function(a){var c=new k("core_calendar/view_manager:renderEventSummaryModal"),f="";return e.getEventById(a).then(function(b){if(!b.event)throw new Error("Error encountered while trying to fetch calendar event with ID: "+a);var c=b.event;return f=w(c.eventtype),y(c.eventtype).then(function(a){return c.eventtype=a,c})}).then(function(a){var c={title:a.name,type:j.TYPE,body:b.render("core_calendar/event_summary_body",a),templateContext:{canedit:a.canedit,candelete:a.candelete,headerclasses:f,isactionevent:a.isactionevent,url:a.url}};return h.create(c)}).then(function(a){return a.getRoot().on(i.hidden,function(){a.destroy()}),a.show(),a}).then(function(a){return c.resolve(),a})["catch"](d.exception)},y=function(a){var b="type"+v(a);return c.get_string(b,"core_calendar").then(function(a){return a})};return{init:function(a){l(a)},reloadCurrentMonth:o,changeMonth:n,refreshMonthContent:m,reloadCurrentDay:q,changeDay:r,refreshDayContent:p,reloadCurrentUpcoming:u}});
|
||||
@@ -64,6 +64,7 @@ function(
|
||||
// Toggle the hidden. We need to render the template before we change the value.
|
||||
data.hidden = !data.hidden;
|
||||
|
||||
M.util.js_pending("core_calendar/calendar_filter:toggleFilter");
|
||||
return Str.get_string('eventtype' + data.eventtype, 'calendar')
|
||||
.then(function(nameStr) {
|
||||
data.name = nameStr;
|
||||
@@ -78,6 +79,7 @@ function(
|
||||
})
|
||||
.then(function() {
|
||||
fireFilterChangedEvent(data);
|
||||
M.util.js_complete("core_calendar/calendar_filter:toggleFilter");
|
||||
return;
|
||||
});
|
||||
};
|
||||
|
||||
+19
-13
@@ -35,6 +35,7 @@ define([
|
||||
'core_calendar/events',
|
||||
'core_calendar/modal_delete',
|
||||
'core_calendar/selectors',
|
||||
'core/pending',
|
||||
],
|
||||
function(
|
||||
$,
|
||||
@@ -49,7 +50,8 @@ function(
|
||||
CalendarRepository,
|
||||
CalendarEvents,
|
||||
ModalDelete,
|
||||
CalendarSelectors
|
||||
CalendarSelectors,
|
||||
Pending
|
||||
) {
|
||||
|
||||
/**
|
||||
@@ -101,13 +103,6 @@ function(
|
||||
);
|
||||
}
|
||||
|
||||
deletePromise.then(function(deleteModal) {
|
||||
deleteModal.show();
|
||||
|
||||
return;
|
||||
})
|
||||
.fail(Notification.exception);
|
||||
|
||||
var stringsPromise = Str.get_strings(deleteStrings);
|
||||
|
||||
var finalPromise = $.when(stringsPromise, deletePromise)
|
||||
@@ -118,27 +113,33 @@ function(
|
||||
deleteModal.setSaveButtonText(strings[0]);
|
||||
}
|
||||
|
||||
deleteModal.show();
|
||||
|
||||
deleteModal.getRoot().on(ModalEvents.save, function() {
|
||||
var pendingPromise = new Pending('calendar/crud:initModal:deletedevent');
|
||||
CalendarRepository.deleteEvent(eventId, false)
|
||||
.then(function() {
|
||||
$('body').trigger(CalendarEvents.deleted, [eventId, false]);
|
||||
return;
|
||||
})
|
||||
.then(pendingPromise.resolve)
|
||||
.catch(Notification.exception);
|
||||
});
|
||||
|
||||
deleteModal.getRoot().on(CalendarEvents.deleteAll, function() {
|
||||
var pendingPromise = new Pending('calendar/crud:initModal:deletedallevent');
|
||||
CalendarRepository.deleteEvent(eventId, true)
|
||||
.then(function() {
|
||||
$('body').trigger(CalendarEvents.deleted, [eventId, true]);
|
||||
return;
|
||||
})
|
||||
.then(pendingPromise.resolve)
|
||||
.catch(Notification.exception);
|
||||
});
|
||||
|
||||
return deleteModal;
|
||||
})
|
||||
.fail(Notification.exception);
|
||||
.catch(Notification.exception);
|
||||
|
||||
return finalPromise;
|
||||
}
|
||||
@@ -233,7 +234,9 @@ function(
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function registerEditListeners(root, eventFormModalPromise) {
|
||||
eventFormModalPromise
|
||||
var pendingPromise = new Pending('core_calendar/crud:registerEditListeners');
|
||||
|
||||
return eventFormModalPromise
|
||||
.then(function(modal) {
|
||||
// When something within the calendar tells us the user wants
|
||||
// to edit an event then show the event form modal.
|
||||
@@ -245,11 +248,14 @@ function(
|
||||
|
||||
e.stopImmediatePropagation();
|
||||
});
|
||||
return;
|
||||
return modal;
|
||||
})
|
||||
.fail(Notification.exception);
|
||||
.then(function(modal) {
|
||||
pendingPromise.resolve();
|
||||
|
||||
return eventFormModalPromise;
|
||||
return modal;
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -165,6 +165,7 @@ function(
|
||||
|
||||
// We have to wait for the modal to finish rendering in order to ensure that
|
||||
// the data-event-title property is available to use as the modal title.
|
||||
M.util.js_pending('core_calendar/summary_modal:registerEventListeners:bodyRendered');
|
||||
this.getRoot().on(ModalEvents.bodyRendered, function() {
|
||||
this.getModal().data({
|
||||
eventTitle: this.getEventTitle(),
|
||||
@@ -173,7 +174,7 @@ function(
|
||||
})
|
||||
.attr('data-type', 'event');
|
||||
CalendarCrud.registerRemove(this.getModal());
|
||||
|
||||
M.util.js_complete('core_calendar/summary_modal:registerEventListeners:bodyRendered');
|
||||
}.bind(this));
|
||||
|
||||
$('body').on(CalendarEvents.deleted, function() {
|
||||
|
||||
@@ -32,6 +32,7 @@ define([
|
||||
'core/modal_factory',
|
||||
'core/modal_events',
|
||||
'core_calendar/summary_modal',
|
||||
'core/pending',
|
||||
], function(
|
||||
$,
|
||||
Templates,
|
||||
@@ -42,7 +43,8 @@ define([
|
||||
CalendarSelectors,
|
||||
ModalFactory,
|
||||
ModalEvents,
|
||||
SummaryModal
|
||||
SummaryModal,
|
||||
Pending
|
||||
) {
|
||||
|
||||
/**
|
||||
@@ -55,6 +57,7 @@ define([
|
||||
|
||||
// Bind click events to event links.
|
||||
root.on('click', CalendarSelectors.links.eventLink, function(e) {
|
||||
var pendingPromise = new Pending('core_calendar/view_manager:eventLink:click');
|
||||
var target = $(e.target);
|
||||
var eventId = null;
|
||||
|
||||
@@ -79,7 +82,11 @@ define([
|
||||
// and causing the day click handler to fire.
|
||||
e.stopPropagation();
|
||||
|
||||
renderEventSummaryModal(eventId);
|
||||
renderEventSummaryModal(eventId)
|
||||
.then(pendingPromise.resolve())
|
||||
.catch();
|
||||
} else {
|
||||
pendingPromise.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -375,12 +382,14 @@ define([
|
||||
* Render the event summary modal.
|
||||
*
|
||||
* @param {Number} eventId The calendar event id.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
var renderEventSummaryModal = function(eventId) {
|
||||
var pendingPromise = new Pending('core_calendar/view_manager:renderEventSummaryModal');
|
||||
var typeClass = '';
|
||||
|
||||
// Calendar repository promise.
|
||||
CalendarRepository.getEventById(eventId).then(function(getEventResponse) {
|
||||
return CalendarRepository.getEventById(eventId).then(function(getEventResponse) {
|
||||
if (!getEventResponse.event) {
|
||||
throw new Error('Error encountered while trying to fetch calendar event with ID: ' + eventId);
|
||||
}
|
||||
@@ -409,7 +418,8 @@ define([
|
||||
// Create the modal.
|
||||
return ModalFactory.create(modalParams);
|
||||
|
||||
}).done(function(modal) {
|
||||
})
|
||||
.then(function(modal) {
|
||||
// Handle hidden event.
|
||||
modal.getRoot().on(ModalEvents.hidden, function() {
|
||||
// Destroy when hidden.
|
||||
@@ -419,7 +429,14 @@ define([
|
||||
// Finally, render the modal!
|
||||
modal.show();
|
||||
|
||||
}).fail(Notification.exception);
|
||||
return modal;
|
||||
})
|
||||
.then(function(modal) {
|
||||
pendingPromise.resolve();
|
||||
|
||||
return modal;
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1270,6 +1270,12 @@ class calendar_information {
|
||||
* @param string|null $view preference view options (eg: day, month, upcoming)
|
||||
*/
|
||||
public function add_sidecalendar_blocks(core_calendar_renderer $renderer, $showfilters=false, $view=null) {
|
||||
global $PAGE;
|
||||
|
||||
if (!has_capability('moodle/block:view', $PAGE->context) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($showfilters) {
|
||||
$filters = new block_contents();
|
||||
$filters->content = $renderer->event_filter();
|
||||
|
||||
@@ -121,7 +121,7 @@ class behat_calendar extends behat_base {
|
||||
*/
|
||||
public function i_view_the_calendar_for($month, $year) {
|
||||
$time = make_timestamp($year, $month, 1);
|
||||
$this->getSession()->visit($this->locate_path('/calendar/view.php?view=month&course=1&time='.$time));
|
||||
$this->execute('behat_general::i_visit', ['/calendar/view.php?view=month&course=1&time='.$time]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,9 +59,7 @@ Feature: Add cohorts of users
|
||||
@javascript
|
||||
Scenario: Edit cohort name in-place
|
||||
When I follow "Cohorts"
|
||||
And I click on "Edit cohort name" "link" in the "Test cohort name" "table_row"
|
||||
And I set the field "New name for cohort Test cohort name" to "Students cohort"
|
||||
And I press key "13" in the field "New name for cohort Test cohort name"
|
||||
And I set the field "Edit cohort name" to "Students cohort"
|
||||
Then I should not see "Test cohort name"
|
||||
And I should see "Students cohort"
|
||||
And I follow "Cohorts"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user