Compare commits
362 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 |
@@ -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
|
||||
|
||||
@@ -2982,4 +2982,548 @@
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.9" requires="3.5">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="10.2.29" />
|
||||
<VENDOR name="mysql" version="5.6" />
|
||||
<VENDOR name="postgres" version="9.5" />
|
||||
<VENDOR name="mssql" version="11.0" />
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.2.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="mbstringrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<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">
|
||||
</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="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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,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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)';
|
||||
|
||||
@@ -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
|
||||
|
@@ -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>
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -53,7 +53,7 @@ class behat_auth extends behat_base {
|
||||
}
|
||||
|
||||
// Visit login page.
|
||||
$this->getSession()->visit($this->locate_path($loginurl->out_as_local_url()));
|
||||
$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.
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -150,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"
|
||||
|
||||
@@ -150,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"
|
||||
|
||||
@@ -777,13 +777,6 @@ $CFG->admin = 'admin';
|
||||
// ),
|
||||
// );
|
||||
//
|
||||
// You can force the browser session (not user's sessions) to restart after N seconds. This could
|
||||
// be useful if you are using a cloud-based service with time restrictions in the browser side.
|
||||
// Setting this value the browser session that Behat is using will be restarted. Set the time in
|
||||
// seconds. Is not recommended to use this setting if you don't explicitly need it.
|
||||
// Example:
|
||||
// $CFG->behat_restart_browser_after = 7200; // Restarts the browser session after 2 hours
|
||||
//
|
||||
// All this page's extra Moodle settings are compared against a white list of allowed settings
|
||||
// (the basic and behat_* ones) to avoid problems with production environments. This setting can be
|
||||
// used to expand the default white list with an array of extra settings.
|
||||
|
||||
@@ -53,9 +53,7 @@ Feature: Sections can be edited and deleted in topics format
|
||||
|
||||
@javascript
|
||||
Scenario: Inline edit section name in topics format
|
||||
When I click on "Edit topic name" "link" in the "li#section-1" "css_element"
|
||||
And I set the field "New name for topic Topic 1" to "Midterm evaluation"
|
||||
And I press key "13" in the field "New name for topic Topic 1"
|
||||
When I set the field "Edit topic name" in the "li#section-1" "css_element" to "Midterm evaluation"
|
||||
Then I should not see "Topic 1" in the "region-main" "region"
|
||||
And "New name for topic" "field" should not exist
|
||||
And I should see "Midterm evaluation" in the "li#section-1" "css_element"
|
||||
|
||||
@@ -54,9 +54,7 @@ Feature: Sections can be edited and deleted in weeks format
|
||||
|
||||
@javascript
|
||||
Scenario: Inline edit section name in weeks format
|
||||
When I click on "Edit week name" "link" in the "li#section-1" "css_element"
|
||||
And I set the field "New name for week 1 May - 7 May" to "Midterm evaluation"
|
||||
And I press key "13" in the field "New name for week 1 May - 7 May"
|
||||
When I set the field "Edit week name" in the "li#section-1" "css_element" to "Midterm evaluation"
|
||||
Then I should not see "1 May - 7 May" in the "region-main" "region"
|
||||
And "New name for week" "field" should not exist
|
||||
And I should see "Midterm evaluation" in the "li#section-1" "css_element"
|
||||
|
||||
@@ -15,15 +15,16 @@ Feature: Edit activity name in-place
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And the following "activity" exists:
|
||||
| course | C1 |
|
||||
| activity | forum |
|
||||
| name | Test forum name |
|
||||
| description | Test forum description |
|
||||
| idnumber | forum1 |
|
||||
When I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Forum" to section "1" and I fill the form with:
|
||||
| Forum name | Test forum name |
|
||||
| Description | Test forum description |
|
||||
# Rename activity
|
||||
And I click on "Edit title" "link" in the "//div[contains(@class,'activityinstance') and contains(.,'Test forum name')]" "xpath_element"
|
||||
And I set the field "New name for activity Test forum name" to "Good news"
|
||||
And I press key "13" in the field "New name for activity Test forum name"
|
||||
And I set the field "Edit title" in the "Test forum name" "activity" to "Good news"
|
||||
Then I should not see "Test forum name" in the ".course-content" "css_element"
|
||||
And "New name for activity Test forum name" "field" should not exist
|
||||
And I should see "Good news"
|
||||
@@ -32,12 +33,11 @@ Feature: Edit activity name in-place
|
||||
And I should not see "Test forum name"
|
||||
# Cancel renaming
|
||||
And I click on "Edit title" "link" in the "//div[contains(@class,'activityinstance') and contains(.,'Good news')]" "xpath_element"
|
||||
And I set the field "New name for activity Good news" to "Terrible news"
|
||||
And I press key "27" in the field "New name for activity Good news"
|
||||
And I type "Terrible news"
|
||||
And I press the escape key
|
||||
And "New name for activity Good news" "field" should not exist
|
||||
And I should see "Good news"
|
||||
And I should not see "Terrible news"
|
||||
And I am on "Course 1" course homepage
|
||||
And I should see "Good news"
|
||||
And I should not see "Terrible news"
|
||||
And I log out
|
||||
|
||||
@@ -809,22 +809,12 @@ class behat_course extends behat_base {
|
||||
* @param string $newactivityname
|
||||
*/
|
||||
public function i_change_activity_name_to($activityname, $newactivityname) {
|
||||
|
||||
if (!$this->running_javascript()) {
|
||||
throw new DriverException('Change activity name step is not available with Javascript disabled');
|
||||
}
|
||||
|
||||
$activity = $this->escape($activityname);
|
||||
|
||||
$this->execute('behat_course::i_click_on_in_the_activity',
|
||||
array(get_string('edittitle'), "link", $activity)
|
||||
);
|
||||
|
||||
// Adding chr(10) to save changes.
|
||||
$this->execute('behat_forms::i_set_the_field_to',
|
||||
array('title', $this->escape($newactivityname) . chr(10))
|
||||
);
|
||||
|
||||
$this->execute('behat_forms::i_set_the_field_in_container_to', [
|
||||
get_string('edittitle'),
|
||||
$activityname,
|
||||
'activity',
|
||||
$newactivityname
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,7 +38,7 @@ Feature: Tagging courses
|
||||
And I expand all fieldsets
|
||||
Then I should see "Mathematics" in the ".form-autocomplete-selection" "css_element"
|
||||
And I set the following fields to these values:
|
||||
| Tags | Algebra |
|
||||
| Tags | Mathematics, Algebra |
|
||||
And I press "Save and display"
|
||||
And I am on "Course 2" course homepage
|
||||
And I navigate to "Edit settings" node in "Course administration"
|
||||
@@ -74,7 +74,7 @@ Feature: Tagging courses
|
||||
And I navigate to "Course tags" in current page administration
|
||||
Then I should see "Mathematics" in the ".form-autocomplete-selection" "css_element"
|
||||
And I set the following fields to these values:
|
||||
| Tags | Algebra |
|
||||
| Tags | Mathematics, Algebra |
|
||||
And I press "Save changes"
|
||||
And I am on "Course 2" course homepage
|
||||
And I navigate to "Course tags" in current page administration
|
||||
|
||||
@@ -97,6 +97,13 @@ switch ($action) {
|
||||
|
||||
if (empty($roleid)) {
|
||||
$roleid = null;
|
||||
} else {
|
||||
if (!has_capability('moodle/role:assign', $context)) {
|
||||
throw new enrol_ajax_exception('assignnotpermitted');
|
||||
}
|
||||
if (!array_key_exists($roleid, get_assignable_roles($context, ROLENAME_ALIAS, false))) {
|
||||
throw new enrol_ajax_exception('invalidrole');
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($startdate)) {
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["core/templates","jquery","core/str","core/config","core/notification","core/modal_factory","core/modal_events","core/fragment"],function(a,b,c,d,e,f,g,h){var i={COHORTSELECT:"#id_cohortlist",TRIGGERBUTTONS:".enrolusersbutton.enrol_manual_plugin [type='submit']",UNWANTEDHIDDENFIELDS:":input[value='_qf__force_multiselect_submission']"},j=function(a){this.contextid=a.contextid,this.initModal()};return j.prototype.courseid=0,j.prototype.modal=null,j.prototype.initModal=function(){var a=b(i.TRIGGERBUTTONS);b.when(c.get_strings([{key:"enroluserscohorts",component:"enrol_manual"},{key:"enrolusers",component:"enrol_manual"}]),f.create({type:f.types.SAVE_CANCEL,large:!0},a)).then(function(a,c){this.modal=c,c.setTitle(a[1]),c.setSaveButtonText(a[1]),c.getRoot().on(g.save,this.submitForm.bind(this)),c.getRoot().on("submit","form",this.submitFormAjax.bind(this)),c.getRoot().on(g.hidden,function(){c.setBody("")}),c.getRoot().on(g.shown,function(){var d=this.getBody();d.then(function(d){var e=b(d).find(i.COHORTSELECT).length?0:1;c.setSaveButtonText(a[e])}).fail(e.exception),c.setBody(d)}.bind(this))}.bind(this)).fail(e.exception)},j.prototype.submitForm=function(a){a.preventDefault(),this.modal.getRoot().find("form").submit()},j.prototype.submitFormAjax=function(a){a.preventDefault();var c=this.modal.getRoot().find("form"),f=c.find(i.UNWANTEDHIDDENFIELDS);f.each(function(){b(this).remove()});var g=c.serialize();this.modal.hide();var h={type:"GET",processData:!1,contentType:"application/json"},j=d.wwwroot+"/enrol/manual/ajax.php?"+g;b.ajax(j,h).then(function(a){a.error?e.addNotification({message:a.error,type:"error"}):("undefined"!=typeof window.M.core_formchangechecker&&window.M.core_formchangechecker.reset_form_dirty_state(),window.location.reload())}).fail(e.exception)},j.prototype.getBody=function(){return h.loadFragment("enrol_manual","enrol_users_form",this.contextid,{}).fail(e.exception)},j.prototype.getFooter=function(){return a.render("enrol_manual/enrol_modal_footer",{})},{init:function(a){new j(a)}}});
|
||||
define(["core/templates","jquery","core/str","core/config","core/notification","core/modal_factory","core/modal_events","core/fragment","core/pending"],function(a,b,c,d,e,f,g,h,i){var j={COHORTSELECT:"#id_cohortlist",TRIGGERBUTTONS:".enrolusersbutton.enrol_manual_plugin [type='submit']",UNWANTEDHIDDENFIELDS:":input[value='_qf__force_multiselect_submission']"},k=function(a){this.contextid=a.contextid,this.initModal()};return k.prototype.courseid=0,k.prototype.modal=null,k.prototype.initModal=function(){var a=b(j.TRIGGERBUTTONS);b.when(c.get_strings([{key:"enroluserscohorts",component:"enrol_manual"},{key:"enrolusers",component:"enrol_manual"}]),f.create({type:f.types.SAVE_CANCEL,large:!0},a)).then(function(a,c){this.modal=c,c.setTitle(a[1]),c.setSaveButtonText(a[1]),c.getRoot().on(g.save,this.submitForm.bind(this)),c.getRoot().on("submit","form",this.submitFormAjax.bind(this)),c.getRoot().on(g.hidden,function(){c.setBody("")}),c.getRoot().on(g.shown,function(){var d=new i("enrol_manual/quickenrolment:initModal:shown"),f=this.getBody();f.then(function(d){var e=b(d).find(j.COHORTSELECT).length?0:1;c.setSaveButtonText(a[e])}).then(d.resolve)["catch"](e.exception),c.setBody(f)}.bind(this))}.bind(this)).fail(e.exception)},k.prototype.submitForm=function(a){a.preventDefault(),this.modal.getRoot().find("form").submit()},k.prototype.submitFormAjax=function(a){a.preventDefault();var c=this.modal.getRoot().find("form"),f=c.find(j.UNWANTEDHIDDENFIELDS);f.each(function(){b(this).remove()});var g=c.serialize();this.modal.hide();var h={type:"GET",processData:!1,contentType:"application/json"},i=d.wwwroot+"/enrol/manual/ajax.php?"+g;b.ajax(i,h).then(function(a){a.error?e.addNotification({message:a.error,type:"error"}):("undefined"!=typeof window.M.core_formchangechecker&&window.M.core_formchangechecker.reset_form_dirty_state(),window.location.reload())}).fail(e.exception)},k.prototype.getBody=function(){return h.loadFragment("enrol_manual","enrol_users_form",this.contextid,{}).fail(e.exception)},k.prototype.getFooter=function(){return a.render("enrol_manual/enrol_modal_footer",{})},{init:function(a){new k(a)}}});
|
||||
@@ -28,8 +28,9 @@ define(['core/templates',
|
||||
'core/modal_factory',
|
||||
'core/modal_events',
|
||||
'core/fragment',
|
||||
'core/pending',
|
||||
],
|
||||
function(Template, $, Str, Config, Notification, ModalFactory, ModalEvents, Fragment) {
|
||||
function(Template, $, Str, Config, Notification, ModalFactory, ModalEvents, Fragment, Pending) {
|
||||
|
||||
/** @type {Object} The list of selectors for the quick enrolment modal. */
|
||||
var SELECTORS = {
|
||||
@@ -91,6 +92,7 @@ define(['core/templates',
|
||||
});
|
||||
|
||||
modal.getRoot().on(ModalEvents.shown, function() {
|
||||
var pendingPromise = new Pending('enrol_manual/quickenrolment:initModal:shown');
|
||||
var bodyPromise = this.getBody();
|
||||
bodyPromise.then(function(html) {
|
||||
var stringIndex = $(html).find(SELECTORS.COHORTSELECT).length ? 0 : 1;
|
||||
@@ -98,7 +100,8 @@ define(['core/templates',
|
||||
|
||||
return;
|
||||
})
|
||||
.fail(Notification.exception);
|
||||
.then(pendingPromise.resolve)
|
||||
.catch(Notification.exception);
|
||||
|
||||
modal.setBody(bodyPromise);
|
||||
}.bind(this));
|
||||
|
||||
@@ -129,18 +129,17 @@ Feature: Teacher can search and enrol users one by one into the course
|
||||
Scenario: Searching for a non-existing user
|
||||
Given I navigate to course participants
|
||||
And I press "Enrol users"
|
||||
And I set the field "Select users" to "qwertyuiop"
|
||||
And I click on ".form-autocomplete-downarrow" "css_element" in the "Select users" "form_row"
|
||||
And I click on "Select users" "field"
|
||||
And I type "qwertyuiop"
|
||||
Then I should see "No suggestions"
|
||||
|
||||
@javascript
|
||||
Scenario: If there are less than 100 matching users, all are displayed for selection
|
||||
Given I navigate to course participants
|
||||
And I press "Enrol users"
|
||||
When I set the field "Select users" to "example.com"
|
||||
And I click on ".form-autocomplete-downarrow" "css_element" in the "Select users" "form_row"
|
||||
And I click on "Student 099" item in the autocomplete list
|
||||
Then I should see "Student 099"
|
||||
When I click on "Select users" "field"
|
||||
And I type "example.com"
|
||||
Then "Student 099" "autocomplete_suggestions" should exist
|
||||
|
||||
@javascript
|
||||
Scenario: If there are more than 100 matching users, inform there are too many.
|
||||
@@ -150,8 +149,8 @@ Feature: Teacher can search and enrol users one by one into the course
|
||||
| student101 | Student | 101 | student101@example.com |
|
||||
And I navigate to course participants
|
||||
And I press "Enrol users"
|
||||
When I set the field "Select users" to "example.com"
|
||||
And I click on ".form-autocomplete-downarrow" "css_element" in the "Select users" "form_row"
|
||||
When I click on "Select users" "field"
|
||||
And I type "example.com"
|
||||
Then I should see "Too many users (>100) to show"
|
||||
|
||||
@javascript
|
||||
@@ -166,13 +165,13 @@ Feature: Teacher can search and enrol users one by one into the course
|
||||
When I am on "Course 001" course homepage
|
||||
Then I navigate to course participants
|
||||
And I press "Enrol users"
|
||||
When I set the field "Select users" to "student100@example.com"
|
||||
And I click on ".form-autocomplete-downarrow" "css_element" in the "Select users" "form_row"
|
||||
When I click on "Select users" "field"
|
||||
And I type "student100@example.com"
|
||||
Then I should see "student100@example.com, CITY1, UK, 1234567892, 1234567893, ABC1, ABC2"
|
||||
# Remove identity field in setting User policies
|
||||
And the following config values are set as admin:
|
||||
| showuseridentity | idnumber,email,phone1,phone2,department,institution |
|
||||
When I am on "Course 001" course homepage
|
||||
And I am on "Course 001" course homepage
|
||||
And I navigate to course participants
|
||||
And I press "Enrol users"
|
||||
When I set the field "Select users" to "student100@example.com"
|
||||
|
||||
@@ -32,6 +32,6 @@ Feature: Users can be added to multiple groups at once
|
||||
And I click on "Group 1" item in the autocomplete list
|
||||
And I click on ".form-autocomplete-downarrow" "css_element" in the "student1" "table_row"
|
||||
And I click on "Group 2" item in the autocomplete list
|
||||
And I press key "27" in the field "Edit groups for \"Student 1\""
|
||||
And I press the escape key
|
||||
And I click on "Save changes" "link" in the "student1" "table_row"
|
||||
Then I should see "Group 1, Group 2"
|
||||
|
||||
@@ -178,6 +178,219 @@ class core_enrollib_testcase extends advanced_testcase {
|
||||
$this->assertEquals(array($course1->id, $course3->id, $course2->id), array_keys($courses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test enrol_course_delete() without passing a user id. When a value for user id is not present, the method
|
||||
* should delete all enrolment related data in the course.
|
||||
*/
|
||||
public function test_enrol_course_delete_without_userid() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Create users.
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
// Create a course.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
|
||||
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
|
||||
|
||||
$manual = enrol_get_plugin('manual');
|
||||
$manualinstance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'manual'], '*', MUST_EXIST);
|
||||
// Enrol user1 as a student in the course using manual enrolment.
|
||||
$manual->enrol_user($manualinstance, $user1->id, $studentrole->id);
|
||||
|
||||
$self = enrol_get_plugin('self');
|
||||
$selfinstance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'self'], '*', MUST_EXIST);
|
||||
$self->update_status($selfinstance, ENROL_INSTANCE_ENABLED);
|
||||
// Enrol user2 as a student in the course using self enrolment.
|
||||
$self->enrol_user($selfinstance, $user2->id, $studentrole->id);
|
||||
|
||||
// Delete all enrolment related records in the course.
|
||||
enrol_course_delete($course);
|
||||
|
||||
// The course enrolment of user1 should not exists.
|
||||
$user1enrolment = $DB->get_record('user_enrolments',
|
||||
['enrolid' => $manualinstance->id, 'userid' => $user1->id]);
|
||||
$this->assertFalse($user1enrolment);
|
||||
|
||||
// The role assignment of user1 should not exists.
|
||||
$user1roleassignment = $DB->get_record('role_assignments',
|
||||
['roleid' => $studentrole->id, 'userid'=> $user1->id, 'contextid' => $coursecontext->id]
|
||||
);
|
||||
$this->assertFalse($user1roleassignment);
|
||||
|
||||
// The course enrolment of user2 should not exists.
|
||||
$user2enrolment = $DB->get_record('user_enrolments',
|
||||
['enrolid' => $selfinstance->id, 'userid' => $user2->id]);
|
||||
$this->assertFalse($user2enrolment);
|
||||
|
||||
// The role assignment of user2 should not exists.
|
||||
$user2roleassignment = $DB->get_record('role_assignments',
|
||||
['roleid' => $studentrole->id, 'userid'=> $user2->id, 'contextid' => $coursecontext->id]);
|
||||
$this->assertFalse($user2roleassignment);
|
||||
|
||||
// All existing course enrolment instances should not exists.
|
||||
$enrolmentinstances = enrol_get_instances($course->id, false);
|
||||
$this->assertCount(0, $enrolmentinstances);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test enrol_course_delete() when user id is present.
|
||||
* When a value for user id is present, the method should make sure the user has the proper capability to
|
||||
* un-enrol users before removing the enrolment data. If the capabilities are missing the data should not be removed.
|
||||
*
|
||||
* @dataProvider enrol_course_delete_with_userid_provider
|
||||
* @param array $excludedcapabilities The capabilities that should be excluded from the user's role
|
||||
* @param bool $expected The expected results
|
||||
*/
|
||||
public function test_enrol_course_delete_with_userid($excludedcapabilities, $expected) {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
// Create users.
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$user3 = $this->getDataGenerator()->create_user();
|
||||
// Create a course.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
|
||||
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
|
||||
$editingteacherrole = $DB->get_record('role', ['shortname' => 'editingteacher']);
|
||||
|
||||
$manual = enrol_get_plugin('manual');
|
||||
$manualinstance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'manual'],
|
||||
'*', MUST_EXIST);
|
||||
// Enrol user1 as a student in the course using manual enrolment.
|
||||
$manual->enrol_user($manualinstance, $user1->id, $studentrole->id);
|
||||
// Enrol user3 as an editing teacher in the course using manual enrolment.
|
||||
// By default, the editing teacher role has the capability to un-enroll users which have been enrolled using
|
||||
// the existing enrolment methods.
|
||||
$manual->enrol_user($manualinstance, $user3->id, $editingteacherrole->id);
|
||||
|
||||
$self = enrol_get_plugin('self');
|
||||
$selfinstance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'self'],
|
||||
'*', MUST_EXIST);
|
||||
$self->update_status($selfinstance, ENROL_INSTANCE_ENABLED);
|
||||
// Enrol user2 as a student in the course using self enrolment.
|
||||
$self->enrol_user($selfinstance, $user2->id, $studentrole->id);
|
||||
|
||||
foreach($excludedcapabilities as $capability) {
|
||||
// Un-assign the given capability from the editing teacher role.
|
||||
unassign_capability($capability, $editingteacherrole->id);
|
||||
}
|
||||
|
||||
// Delete only enrolment related records in the course where user3 has the required capability.
|
||||
enrol_course_delete($course, $user3->id);
|
||||
|
||||
// Check the existence of the course enrolment of user1.
|
||||
$user1enrolmentexists = (bool) $DB->count_records('user_enrolments',
|
||||
['enrolid' => $manualinstance->id, 'userid' => $user1->id]);
|
||||
$this->assertEquals($expected['User 1 course enrolment exists'], $user1enrolmentexists);
|
||||
|
||||
// Check the existence of the role assignment of user1 in the course.
|
||||
$user1roleassignmentexists = (bool) $DB->count_records('role_assignments',
|
||||
['roleid' => $studentrole->id, 'userid' => $user1->id, 'contextid' => $coursecontext->id]);
|
||||
$this->assertEquals($expected['User 1 role assignment exists'], $user1roleassignmentexists);
|
||||
|
||||
// Check the existence of the course enrolment of user2.
|
||||
$user2enrolmentexists = (bool) $DB->count_records('user_enrolments',
|
||||
['enrolid' => $selfinstance->id, 'userid' => $user2->id]);
|
||||
$this->assertEquals($expected['User 2 course enrolment exists'], $user2enrolmentexists);
|
||||
|
||||
// Check the existence of the role assignment of user2 in the course.
|
||||
$user2roleassignmentexists = (bool) $DB->count_records('role_assignments',
|
||||
['roleid' => $studentrole->id, 'userid' => $user2->id, 'contextid' => $coursecontext->id]);
|
||||
$this->assertEquals($expected['User 2 role assignment exists'], $user2roleassignmentexists);
|
||||
|
||||
// Check the existence of the course enrolment of user3.
|
||||
$user3enrolmentexists = (bool) $DB->count_records('user_enrolments',
|
||||
['enrolid' => $manualinstance->id, 'userid' => $user3->id]);
|
||||
$this->assertEquals($expected['User 3 course enrolment exists'], $user3enrolmentexists);
|
||||
|
||||
// Check the existence of the role assignment of user3 in the course.
|
||||
$user3roleassignmentexists = (bool) $DB->count_records('role_assignments',
|
||||
['roleid' => $editingteacherrole->id, 'userid' => $user3->id, 'contextid' => $coursecontext->id]);
|
||||
$this->assertEquals($expected['User 3 role assignment exists'], $user3roleassignmentexists);
|
||||
|
||||
// Check the existence of the manual enrolment instance in the course.
|
||||
$manualinstance = (bool) $DB->count_records('enrol', ['enrol' => 'manual', 'courseid' => $course->id]);
|
||||
$this->assertEquals($expected['Manual course enrolment instance exists'], $manualinstance);
|
||||
|
||||
// Check existence of the self enrolment instance in the course.
|
||||
$selfinstance = (bool) $DB->count_records('enrol', ['enrol' => 'self', 'courseid' => $course->id]);
|
||||
$this->assertEquals($expected['Self course enrolment instance exists'], $selfinstance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for test_enrol_course_delete_with_userid().
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function enrol_course_delete_with_userid_provider() {
|
||||
return [
|
||||
'The teacher can un-enrol users in a course' =>
|
||||
[
|
||||
'excludedcapabilities' => [],
|
||||
'results' => [
|
||||
// Whether certain enrolment related data still exists in the course after the deletion.
|
||||
// When the user has the capabilities to un-enrol users and the enrolment plugins allow manual
|
||||
// unenerolment than all course enrolment data should be removed.
|
||||
'Manual course enrolment instance exists' => false,
|
||||
'Self course enrolment instance exists' => false,
|
||||
'User 1 course enrolment exists' => false,
|
||||
'User 1 role assignment exists' => false,
|
||||
'User 2 course enrolment exists' => false,
|
||||
'User 2 role assignment exists' => false,
|
||||
'User 3 course enrolment exists' => false,
|
||||
'User 3 role assignment exists' => false
|
||||
],
|
||||
],
|
||||
'The teacher cannot un-enrol self enrolled users' =>
|
||||
[
|
||||
'excludedcapabilities' => [
|
||||
// Exclude the following capabilities for the editing teacher.
|
||||
'enrol/self:unenrol'
|
||||
],
|
||||
'results' => [
|
||||
// When the user does not have the capabilities to un-enrol self enrolled users, the data
|
||||
// related to this enrolment method should not be removed. Everything else should be removed.
|
||||
'Manual course enrolment instance exists' => false,
|
||||
'Self course enrolment instance exists' => true,
|
||||
'User 1 course enrolment exists' => false,
|
||||
'User 1 role assignment exists' => false,
|
||||
'User 2 course enrolment exists' => true,
|
||||
'User 2 role assignment exists' => true,
|
||||
'User 3 course enrolment exists' => false,
|
||||
'User 3 role assignment exists' => false
|
||||
],
|
||||
],
|
||||
'The teacher cannot un-enrol self and manually enrolled users' =>
|
||||
[
|
||||
'excludedcapabilities' => [
|
||||
// Exclude the following capabilities for the editing teacher.
|
||||
'enrol/manual:unenrol',
|
||||
'enrol/self:unenrol'
|
||||
],
|
||||
'results' => [
|
||||
// When the user does not have the capabilities to un-enrol self and manually enrolled users,
|
||||
// the data related to these enrolment methods should not be removed.
|
||||
'Manual course enrolment instance exists' => true,
|
||||
'Self course enrolment instance exists' => true,
|
||||
'User 1 course enrolment exists' => true,
|
||||
'User 1 role assignment exists' => true,
|
||||
'User 2 course enrolment exists' => true,
|
||||
'User 2 role assignment exists' => true,
|
||||
'User 3 course enrolment exists' => true,
|
||||
'User 3 role assignment exists' => true
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function test_enrol_user_sees_own_courses() {
|
||||
global $DB, $CFG;
|
||||
|
||||
|
||||
@@ -186,5 +186,17 @@ MathJax.Hub.Config({
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2018051401) {
|
||||
// Update CDN url.
|
||||
$originalurl = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js';
|
||||
$newurl = 'https://cdn.jsdelivr.net/npm/mathjax@2.7.8/MathJax.js';
|
||||
$currenturl = get_config('filter_mathjaxloader', 'httpsurl');
|
||||
if ($currenturl == $originalurl) {
|
||||
set_config('httpsurl', $newurl, 'filter_mathjaxloader');
|
||||
}
|
||||
|
||||
upgrade_plugin_savepoint(true, 2018051401, 'filter', 'mathjaxloader');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Description of MathJAX library integration in Moodle
|
||||
====================================================
|
||||
|
||||
* Default MathJax version: 2.7.2
|
||||
* Default MathJax version: 2.7.8
|
||||
* License: Apache 2.0
|
||||
* Source: https://www.mathjax.org/
|
||||
|
||||
@@ -18,3 +18,8 @@ Upgrading the default MathJax version
|
||||
3. Check and eventually update the list of language mappings in filter.php.
|
||||
Also see the unit test for the language mappings.
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
* The MathJax 2.7.2 seems to have a possible security issue, the CDN default value have been
|
||||
updated to point to the recommended 2.7.8 version. See MDL-68430 for details.
|
||||
|
||||
@@ -33,7 +33,7 @@ if ($ADMIN->fulltree) {
|
||||
$item = new admin_setting_configtext('filter_mathjaxloader/httpsurl',
|
||||
new lang_string('httpsurl', 'filter_mathjaxloader'),
|
||||
new lang_string('httpsurl_help', 'filter_mathjaxloader'),
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js',
|
||||
'https://cdn.jsdelivr.net/npm/mathjax@2.7.8/MathJax.js',
|
||||
PARAM_RAW);
|
||||
$settings->add($item);
|
||||
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018051400;
|
||||
$plugin->version = 2018051401;
|
||||
$plugin->requires = 2018050800; // Requires this Moodle version.
|
||||
$plugin->component= 'filter_mathjaxloader';
|
||||
|
||||
@@ -189,6 +189,9 @@ class filter_tex extends moodle_text_filter {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanitize the decoded string, because filter_text_image() injects the final string between script tags.
|
||||
$texexp = clean_param($texexp, PARAM_TEXT);
|
||||
|
||||
$md5 = md5($texexp);
|
||||
if (!$DB->record_exists("cache_filters", array("filter"=>"tex", "md5key"=>$md5))) {
|
||||
$texcache = new stdClass();
|
||||
|
||||
@@ -59,18 +59,18 @@ Feature: Using the AJAX grading feature of Grader report to update grades and fe
|
||||
Then I should see a grade field for "Student 2" and grade item "Item VU"
|
||||
And I should not see a feedback field for "Student 2" and grade item "Item VU"
|
||||
And I set the field "ajaxgrade" to "33"
|
||||
And I press key "13" in the field "ajaxgrade"
|
||||
And I press the enter key
|
||||
And I should not see a grade field for "Student 2" and grade item "Item VU"
|
||||
And I should not see a feedback field for "Student 2" and grade item "Item VU"
|
||||
And I click on student "Student 3" for grade item "Item VU"
|
||||
And I set the field "ajaxgrade" to "50"
|
||||
And I press key "13" in the field "ajaxgrade"
|
||||
And I click on student "Student 3" for grade item "Item 1"
|
||||
And I set the field "ajaxgrade" to "80"
|
||||
And I press key "13" in the field "ajaxgrade"
|
||||
And I click on student "Student 3" for grade item "Item SU"
|
||||
And I set the field "ajaxgrade" to "Very good"
|
||||
And I press key "13" in the field "ajaxgrade"
|
||||
And I press the shift tab key
|
||||
And I click on student "Student 3" for grade item "Item VU"
|
||||
And I set the field "ajaxgrade" to "50"
|
||||
And I press the enter key
|
||||
And I click on student "Student 3" for grade item "Item 1"
|
||||
And I set the field "ajaxgrade" to "80"
|
||||
And I press the enter key
|
||||
And the following should exist in the "user-grades" table:
|
||||
| -1- | -6- | -7- | -13- | -16- |
|
||||
| Student 2 | - | 33.00 | - | 33.00 |
|
||||
@@ -88,7 +88,7 @@ Feature: Using the AJAX grading feature of Grader report to update grades and fe
|
||||
And I should see a grade field for "Student 1" and grade item "Course total"
|
||||
And I should not see a feedback field for "Student 1" and grade item "Course total"
|
||||
And I set the field "ajaxgrade" to "90"
|
||||
And I press key "13" in the field "ajaxgrade"
|
||||
And I press the enter key
|
||||
And the following should exist in the "user-grades" table:
|
||||
| -1- | -16- |
|
||||
| Student 1 | 90.00 |
|
||||
@@ -112,7 +112,7 @@ Feature: Using the AJAX grading feature of Grader report to update grades and fe
|
||||
And I should see a feedback field for "Student 2" and grade item "Item VU"
|
||||
And I set the field "ajaxgrade" to "33"
|
||||
And I set the field "ajaxfeedback" to "Student 2 VU feedback"
|
||||
And I press key "13" in the field "ajaxfeedback"
|
||||
And I press the enter key
|
||||
And I click on student "Student 3" for grade item "Item VL"
|
||||
And I should not see a grade field for "Student 3" and grade item "Item VL"
|
||||
And I should not see a feedback field for "Student 3" and grade item "Item VL"
|
||||
@@ -120,11 +120,11 @@ Feature: Using the AJAX grading feature of Grader report to update grades and fe
|
||||
And I should not see a grade field for "Student 3" and grade item "Item TU"
|
||||
And I should see a feedback field for "Student 3" and grade item "Item TU"
|
||||
And I set the field "ajaxfeedback" to "Student 3 TU feedback"
|
||||
And I press key "13" in the field "ajaxfeedback"
|
||||
And I press the enter key
|
||||
And I click on student "Student 2" for grade item "Item SU"
|
||||
And I set the field "ajaxgrade" to "Very good"
|
||||
And I set the field "ajaxfeedback" to "Student 2 SU feedback"
|
||||
And I press key "13" in the field "ajaxfeedback"
|
||||
And I press the enter key
|
||||
# Reload grader report:
|
||||
And I navigate to "View > User report" in the course gradebook
|
||||
And I navigate to "View > Grader report" in the course gradebook
|
||||
@@ -148,7 +148,7 @@ Feature: Using the AJAX grading feature of Grader report to update grades and fe
|
||||
Then I should see a grade field for "Student 2" and grade item "Item VU"
|
||||
And I should see a feedback field for "Student 2" and grade item "Item VU"
|
||||
And I set the field "ajaxgrade" to "33"
|
||||
And I press key "13" in the field "ajaxgrade"
|
||||
And I press the enter key
|
||||
And I click on student "Student 2" for grade item "Course total"
|
||||
And I should not see a grade field for "Student 3" and grade item "Course total"
|
||||
And I should not see a feedback field for "Student 3" and grade item "Course total"
|
||||
|
||||
@@ -128,6 +128,7 @@ class helper {
|
||||
* @return array sql and params list
|
||||
*/
|
||||
protected static function get_users_sql_and_params($context, $search = '', $count = false) {
|
||||
global $DB, $USER;
|
||||
|
||||
// Fields we need from the user table.
|
||||
$extrafields = get_extra_user_fields($context);
|
||||
@@ -147,13 +148,33 @@ class helper {
|
||||
$select = "SELECT DISTINCT $ufields ";
|
||||
$orderby = " ORDER BY u.lastname ASC, u.firstname ASC";
|
||||
}
|
||||
|
||||
$groupjoinsql = '';
|
||||
$groupwheresql = '';
|
||||
$courseid = $context->instanceid;
|
||||
$groupmode = groups_get_course_groupmode(get_course($courseid));
|
||||
|
||||
// We're only interested in separate groups mode because it's the only group mode that requires the user to be a member of
|
||||
// specific group(s), except when they have the 'moodle/site:accessallgroups' capability.
|
||||
if ($groupmode == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $context)) {
|
||||
// Fetch the groups that the user can see.
|
||||
$groups = groups_get_all_groups($courseid, $USER->id, 0, 'g.id');
|
||||
|
||||
// Add join condition to include users that only belong to the same group as the user.
|
||||
list($insql, $inparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED, 'gid', true, 0);
|
||||
$groupjoinsql = " JOIN {groups_members} gm ON gm.userid = u.id ";
|
||||
$groupwheresql = " AND gm.groupid $insql ";
|
||||
$params = array_merge($params, $inparams);
|
||||
}
|
||||
|
||||
$sql = "$select
|
||||
FROM {user} u
|
||||
JOIN {grade_grades_history} ggh ON u.id = ggh.userid
|
||||
JOIN {grade_items} gi ON gi.id = ggh.itemid
|
||||
WHERE $filtersql gi.courseid = :courseid";
|
||||
$groupjoinsql
|
||||
WHERE $filtersql gi.courseid = :courseid $groupwheresql";
|
||||
$sql .= $orderby;
|
||||
$params['courseid'] = $context->instanceid;
|
||||
$params['courseid'] = $courseid;
|
||||
|
||||
return array($sql, $params);
|
||||
}
|
||||
@@ -166,18 +187,31 @@ class helper {
|
||||
* @return array list of graders.
|
||||
*/
|
||||
public static function get_graders($courseid) {
|
||||
global $DB;
|
||||
global $DB, $USER;
|
||||
|
||||
$groupjoinsql = $groupwheresql = '';
|
||||
$inparams = [];
|
||||
$groupmode = groups_get_course_groupmode(get_course($courseid));
|
||||
if ($groupmode == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', \context_course::instance($courseid))) {
|
||||
// Fetch the groups that the user can see.
|
||||
$groups = groups_get_all_groups($courseid, $USER->id, 0, 'g.id');
|
||||
// Add join condition to include users that only belong to the same group as the user.
|
||||
list($insql, $inparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED, 'gid', true, 0);
|
||||
$groupjoinsql = " JOIN {groups_members} gm ON gm.userid = u.id ";
|
||||
$groupwheresql = " AND gm.groupid $insql ";
|
||||
}
|
||||
|
||||
$ufields = get_all_user_name_fields(true, 'u');
|
||||
$sql = "SELECT u.id, $ufields
|
||||
FROM {user} u
|
||||
JOIN {grade_grades_history} ggh ON ggh.usermodified = u.id
|
||||
JOIN {grade_items} gi ON gi.id = ggh.itemid
|
||||
WHERE gi.courseid = :courseid
|
||||
$groupjoinsql
|
||||
WHERE gi.courseid = :courseid $groupwheresql
|
||||
GROUP BY u.id, $ufields
|
||||
ORDER BY u.lastname ASC, u.firstname ASC";
|
||||
|
||||
$graders = $DB->get_records_sql($sql, array('courseid' => $courseid));
|
||||
$graders = $DB->get_records_sql($sql, array('courseid' => $courseid) + $inparams);
|
||||
$return = array(0 => get_string('allgraders', 'gradereport_history'));
|
||||
foreach ($graders as $grader) {
|
||||
$return[$grader->id] = fullname($grader);
|
||||
|
||||
@@ -324,7 +324,7 @@ class tablelog extends \table_sql implements \renderable {
|
||||
* @return array containing sql to use and an array of params.
|
||||
*/
|
||||
protected function get_filters_sql_and_params() {
|
||||
global $DB;
|
||||
global $DB, $USER;
|
||||
|
||||
$coursecontext = $this->context;
|
||||
$filter = 'gi.courseid = :courseid';
|
||||
@@ -355,6 +355,16 @@ class tablelog extends \table_sql implements \renderable {
|
||||
$params += array('grader' => $this->filters->grader);
|
||||
}
|
||||
|
||||
// If the course is separate group mode and the current user is not allowed to see all groups make sure
|
||||
// that we display only users from the same groups as current user.
|
||||
$groupmode = get_course($coursecontext->instanceid)->groupmode;
|
||||
if ($groupmode == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $coursecontext)) {
|
||||
$groupids = array_column(groups_get_all_groups($coursecontext->instanceid, $USER->id, 0, 'g.id'), 'id');
|
||||
list($gsql, $gparams) = $DB->get_in_or_equal($groupids, SQL_PARAMS_NAMED, 'gmuparam', true, 0);
|
||||
$filter .= " AND EXISTS (SELECT 1 FROM {groups_members} gmu WHERE gmu.userid=ggh.userid AND gmu.groupid $gsql)";
|
||||
$params += $gparams;
|
||||
}
|
||||
|
||||
return array($filter, $params);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,12 @@ class gradereport_history_report_testcase extends advanced_testcase {
|
||||
$u5 = $this->getDataGenerator()->create_user();
|
||||
$grader1 = $this->getDataGenerator()->create_user();
|
||||
$grader2 = $this->getDataGenerator()->create_user();
|
||||
self::getDataGenerator()->enrol_user($grader1->id, $c1->id, 'teacher');
|
||||
self::getDataGenerator()->enrol_user($grader2->id, $c1->id, 'teacher');
|
||||
self::getDataGenerator()->enrol_user($u2->id, $c1->id, 'student');
|
||||
self::getDataGenerator()->enrol_user($u3->id, $c1->id, 'student');
|
||||
self::getDataGenerator()->enrol_user($u4->id, $c1->id, 'student');
|
||||
self::getDataGenerator()->enrol_user($u5->id, $c1->id, 'student');
|
||||
|
||||
// Modules.
|
||||
$c1m1 = $this->getDataGenerator()->create_module('assign', array('course' => $c1));
|
||||
@@ -65,6 +71,8 @@ class gradereport_history_report_testcase extends advanced_testcase {
|
||||
$giparams = array('itemtype' => 'mod', 'itemmodule' => 'assign');
|
||||
$grades = array();
|
||||
|
||||
$this->setUser($grader1);
|
||||
|
||||
$gi = grade_item::fetch($giparams + array('iteminstance' => $c1m1->id));
|
||||
$grades['c1m1u1'] = $this->create_grade_history(array('itemid' => $gi->id, 'userid' => $u1->id,
|
||||
'timemodified' => time() - 3600));
|
||||
@@ -163,6 +171,20 @@ class gradereport_history_report_testcase extends advanced_testcase {
|
||||
$this->assertEquals(null, $results[$grades['c2m2u1a']->id]->prevgrade);
|
||||
$this->assertEquals($grades['c2m2u1a']->finalgrade, $results[$grades['c2m2u1c']->id]->prevgrade);
|
||||
$this->assertEquals($grades['c2m2u1c']->finalgrade, $results[$grades['c2m2u1e']->id]->prevgrade);
|
||||
|
||||
// Put course in separate groups mode, add grader1 and two students to the same group.
|
||||
$c1->groupmode = SEPARATEGROUPS;
|
||||
update_course($c1);
|
||||
$this->assertFalse(has_capability('moodle/site:accessallgroups', \context_course::instance($c1->id)));
|
||||
$g1 = self::getDataGenerator()->create_group(['courseid' => $c1->id, 'name' => 'g1']);
|
||||
self::getDataGenerator()->create_group_member(['groupid' => $g1->id, 'userid' => $grader1->id]);
|
||||
self::getDataGenerator()->create_group_member(['groupid' => $g1->id, 'userid' => $u1->id]);
|
||||
self::getDataGenerator()->create_group_member(['groupid' => $g1->id, 'userid' => $u2->id]);
|
||||
$this->assertEquals(2, $this->get_tablelog_results($c1ctx, array(), true));
|
||||
|
||||
// Grader2 is not in any groups.
|
||||
$this->setUser($grader2);
|
||||
$this->assertEquals(0, $this->get_tablelog_results($c1ctx, array(), true));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,6 +241,118 @@ class gradereport_history_report_testcase extends advanced_testcase {
|
||||
$this->assertEquals(1, \gradereport_history\helper::get_users_count($c1ctx, 'c'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider method for \gradereport_history_report_testcase::test_get_users_with_groups()
|
||||
*/
|
||||
public function get_users_provider() {
|
||||
return [
|
||||
'Visible groups, non-editing teacher, not in any group' => [
|
||||
VISIBLEGROUPS, 'teacher', ['g1', 'g2'], ['s1', 's2', 's3', 's4', 's5']
|
||||
],
|
||||
'Visible groups, non-editing teacher' => [
|
||||
VISIBLEGROUPS, 'teacher', [], ['s1', 's2', 's3', 's4', 's5']
|
||||
],
|
||||
'Visible groups, editing teacher' => [
|
||||
VISIBLEGROUPS, 'editingteacher', ['g1', 'g2'], ['s1', 's2', 's3', 's4', 's5']
|
||||
],
|
||||
'Separate groups, non-editing teacher' => [
|
||||
SEPARATEGROUPS, 'teacher', ['g1', 'g2'], ['s1', 's2']
|
||||
],
|
||||
'Separate groups, non-editing teacher, not in any group' => [
|
||||
SEPARATEGROUPS, 'teacher', [], []
|
||||
],
|
||||
'Separate groups, non-editing teacher and student share two groups' => [
|
||||
SEPARATEGROUPS, 'teacher', ['g4', 'g5'], ['s5']
|
||||
],
|
||||
'Separate groups, editing teacher' => [
|
||||
SEPARATEGROUPS, 'editingteacher', ['g1', 'g2'], ['s1', 's2', 's3', 's4', 's5']
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for helper::get_users() with course group mode set.
|
||||
*
|
||||
* @dataProvider get_users_provider
|
||||
* @param $groupmode
|
||||
* @param $teacherrole
|
||||
* @param $teachergroups
|
||||
* @param $expectedusers
|
||||
*/
|
||||
public function test_get_users_with_groups($groupmode, $teacherrole, $teachergroups, $expectedusers) {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
// Create a test course.
|
||||
$course = $generator->create_course(['groupmode' => $groupmode]);
|
||||
|
||||
// Create an assignment module.
|
||||
$assign = $generator->create_module('assign', ['course' => $course]);
|
||||
|
||||
// Fetch roles.
|
||||
$role = $DB->get_record('role', ['shortname' => $teacherrole], '*', MUST_EXIST);
|
||||
$studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST);
|
||||
|
||||
// Create users.
|
||||
$t1 = $generator->create_user(['username' => 't1', 'email' => 't1@example.com']);
|
||||
$s1 = $generator->create_user(['username' => 's1', 'email' => 's1@example.com']);
|
||||
$s2 = $generator->create_user(['username' => 's2', 'email' => 's2@example.com']);
|
||||
$s3 = $generator->create_user(['username' => 's3', 'email' => 's3@example.com']);
|
||||
$s4 = $generator->create_user(['username' => 's4', 'email' => 's4@example.com']);
|
||||
$s5 = $generator->create_user(['username' => 's5', 'email' => 's5@example.com']);
|
||||
|
||||
// Enrol users.
|
||||
$generator->enrol_user($t1->id, $course->id, $role->id);
|
||||
$generator->enrol_user($s1->id, $course->id, $studentrole->id);
|
||||
$generator->enrol_user($s2->id, $course->id, $studentrole->id);
|
||||
$generator->enrol_user($s3->id, $course->id, $studentrole->id);
|
||||
$generator->enrol_user($s4->id, $course->id, $studentrole->id);
|
||||
$generator->enrol_user($s5->id, $course->id, $studentrole->id);
|
||||
|
||||
// Create groups.
|
||||
$groups = [];
|
||||
$groups['g1'] = $generator->create_group(['courseid' => $course->id, 'name' => 'g1']);
|
||||
$groups['g2'] = $generator->create_group(['courseid' => $course->id, 'name' => 'g2']);
|
||||
$groups['g3'] = $generator->create_group(['courseid' => $course->id, 'name' => 'g3']);
|
||||
$groups['g4'] = $generator->create_group(['courseid' => $course->id, 'name' => 'g4']);
|
||||
$groups['g5'] = $generator->create_group(['courseid' => $course->id, 'name' => 'g5']);
|
||||
|
||||
// Add teacher to the assigned groups.
|
||||
foreach ($teachergroups as $groupname) {
|
||||
$group = $groups[$groupname];
|
||||
$generator->create_group_member(['groupid' => $group->id, 'userid' => $t1->id]);
|
||||
}
|
||||
|
||||
// Add students to groups.
|
||||
$generator->create_group_member(['groupid' => $groups['g1']->id, 'userid' => $s1->id]);
|
||||
$generator->create_group_member(['groupid' => $groups['g2']->id, 'userid' => $s2->id]);
|
||||
$generator->create_group_member(['groupid' => $groups['g3']->id, 'userid' => $s3->id]);
|
||||
$generator->create_group_member(['groupid' => $groups['g4']->id, 'userid' => $s5->id]);
|
||||
$generator->create_group_member(['groupid' => $groups['g5']->id, 'userid' => $s5->id]);
|
||||
|
||||
// Creating grade history for the students.
|
||||
$gi = grade_item::fetch(['iteminstance' => $assign->id, 'itemtype' => 'mod', 'itemmodule' => 'assign']);
|
||||
$this->create_grade_history(['itemid' => $gi->id, 'userid' => $s1->id]);
|
||||
$this->create_grade_history(['itemid' => $gi->id, 'userid' => $s2->id]);
|
||||
$this->create_grade_history(['itemid' => $gi->id, 'userid' => $s3->id]);
|
||||
$this->create_grade_history(['itemid' => $gi->id, 'userid' => $s4->id]);
|
||||
$this->create_grade_history(['itemid' => $gi->id, 'userid' => $s5->id]);
|
||||
|
||||
// Log in as the teacher.
|
||||
$this->setUser($t1);
|
||||
|
||||
// Fetch the users.
|
||||
$users = \gradereport_history\helper::get_users(context_course::instance($course->id));
|
||||
// Confirm that the number of users fetched is the same as the count of expected users.
|
||||
$this->assertCount(count($expectedusers), $users);
|
||||
foreach ($users as $user) {
|
||||
// Confirm that each user returned is in the list of expected users.
|
||||
$this->assertTrue(in_array($user->username, $expectedusers));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the get graders helper method.
|
||||
*/
|
||||
@@ -228,9 +362,11 @@ class gradereport_history_report_testcase extends advanced_testcase {
|
||||
// Making the setup.
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
$c3 = $this->getDataGenerator()->create_course(['groupmode' => SEPARATEGROUPS]);
|
||||
|
||||
$c1m1 = $this->getDataGenerator()->create_module('assign', array('course' => $c1));
|
||||
$c2m1 = $this->getDataGenerator()->create_module('assign', array('course' => $c2));
|
||||
$c3m1 = $this->getDataGenerator()->create_module('assign', array('course' => $c3));
|
||||
|
||||
// Users.
|
||||
$u1 = $this->getDataGenerator()->create_user(array('firstname' => 'Eric', 'lastname' => 'Cartman'));
|
||||
@@ -238,6 +374,12 @@ class gradereport_history_report_testcase extends advanced_testcase {
|
||||
$u3 = $this->getDataGenerator()->create_user(array('firstname' => 'Kyle', 'lastname' => 'Broflovski'));
|
||||
$u4 = $this->getDataGenerator()->create_user(array('firstname' => 'Kenny', 'lastname' => 'McCormick'));
|
||||
|
||||
foreach ([$c1, $c2, $c3] as $course) {
|
||||
foreach ([$u1, $u2, $u3, $u4] as $user) {
|
||||
self::getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
}
|
||||
}
|
||||
|
||||
// Creating grade history for some users.
|
||||
$gi = grade_item::fetch(array('iteminstance' => $c1m1->id, 'itemtype' => 'mod', 'itemmodule' => 'assign'));
|
||||
$this->create_grade_history(array('itemid' => $gi->id, 'userid' => $u1->id, 'usermodified' => $u1->id));
|
||||
@@ -247,6 +389,10 @@ class gradereport_history_report_testcase extends advanced_testcase {
|
||||
$gi = grade_item::fetch(array('iteminstance' => $c2m1->id, 'itemtype' => 'mod', 'itemmodule' => 'assign'));
|
||||
$this->create_grade_history(array('itemid' => $gi->id, 'userid' => $u1->id, 'usermodified' => $u4->id));
|
||||
|
||||
$gi = grade_item::fetch(array('iteminstance' => $c3m1->id, 'itemtype' => 'mod', 'itemmodule' => 'assign'));
|
||||
$this->create_grade_history(array('itemid' => $gi->id, 'userid' => $u1->id, 'usermodified' => $u1->id));
|
||||
$this->create_grade_history(array('itemid' => $gi->id, 'userid' => $u2->id, 'usermodified' => $u2->id));
|
||||
|
||||
// Checking fetching some users.
|
||||
$graders = \gradereport_history\helper::get_graders($c1->id);
|
||||
$this->assertCount(4, $graders); // Including "all graders" .
|
||||
@@ -256,6 +402,17 @@ class gradereport_history_report_testcase extends advanced_testcase {
|
||||
$graders = \gradereport_history\helper::get_graders($c2->id);
|
||||
$this->assertCount(2, $graders); // Including "all graders" .
|
||||
$this->assertArrayHasKey($u4->id, $graders);
|
||||
|
||||
// Third course is in separate groups mode. Only graders from the same group will be returned.
|
||||
$g = self::getDataGenerator()->create_group(['courseid' => $course->id, 'name' => 'g1']);
|
||||
self::getDataGenerator()->create_group_member(['groupid' => $g->id, 'userid' => $u1->id]);
|
||||
self::getDataGenerator()->create_group_member(['groupid' => $g->id, 'userid' => $u2->id]);
|
||||
$this->setUser($u1);
|
||||
$graders = \gradereport_history\helper::get_graders($c3->id);
|
||||
$this->assertCount(3, $graders); // Including "all graders" .
|
||||
$this->setUser($u3);
|
||||
$graders = \gradereport_history\helper::get_graders($c3->id);
|
||||
$this->assertCount(1, $graders); // Including "all graders" .
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -195,7 +195,7 @@ class behat_grade extends behat_base {
|
||||
$inputxpath = "//input[@class='idnumber'][" .
|
||||
"parent::li[@class='item'][text()='" . $gradeitem . "']" .
|
||||
" | " .
|
||||
"parent::li[@class='categoryitem' | @class='courseitem']" .
|
||||
"parent::li[@class='categoryitem' or @class='courseitem']" .
|
||||
"/parent::ul/parent::li[starts-with(text(),'" . $gradeitem . "')]" .
|
||||
"]";
|
||||
$this->execute('behat_forms::i_set_the_field_with_xpath_to', array($inputxpath, $idnumber));
|
||||
|
||||
@@ -124,4 +124,4 @@ Feature: Organize students into groups
|
||||
And I set the following fields to these values:
|
||||
| Group name | Group A |
|
||||
| Enrolment key | Abcdef-1 |
|
||||
And I should not see "This enrolment key is already used for another group."
|
||||
And I should not see "This enrolment key is already used for another group."
|
||||
|
||||
@@ -101,4 +101,4 @@ Feature: The description of a group can be viewed by students and teachers
|
||||
And I log in as "student2"
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to course participants
|
||||
And ".groupinfobox" "css_element" should not exist
|
||||
And ".groupinfobox" "css_element" should not exist
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
*
|
||||
* Do not edit this file manually! It contains just a subset of strings
|
||||
* needed during the very first steps of installation. This file was
|
||||
* generated automatically by export-installer.php (which is part of AMOS
|
||||
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
|
||||
* list of strings defined in /install/stringnames.txt.
|
||||
*
|
||||
* @package installer
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['thislanguage'] = 'Аԥсуа бызшәа';
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
*
|
||||
* Do not edit this file manually! It contains just a subset of strings
|
||||
* needed during the very first steps of installation. This file was
|
||||
* generated automatically by export-installer.php (which is part of AMOS
|
||||
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
|
||||
* list of strings defined in /install/stringnames.txt.
|
||||
*
|
||||
* @package installer
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['cannotcreatedboninstall'] = '<p>Kon nie die databasis skep nie.</p>
|
||||
<p>Die gespesifiseerde databasis bestaan nie en die aangetoonde gebruiker het nie toestemming om die databasis te skep nie.</p>
|
||||
<p>Die werf se administrateur moet die konfigurasie van databasisse verifieer.</p>';
|
||||
$string['cannotcreatelangdir'] = 'Kan nie taalgids skep nie';
|
||||
$string['cannotcreatetempdir'] = 'Kan nie tydelike gids skep nie';
|
||||
$string['cannotdownloadcomponents'] = 'Kan nie komponente aflaai nie';
|
||||
$string['cannotdownloadzipfile'] = 'Kan nie ZIP-lêer aflaai nie';
|
||||
$string['cannotfindcomponent'] = 'Kan nie komponent vind nie';
|
||||
$string['cannotsavemd5file'] = 'Kan nie md5-lêer stoor nie';
|
||||
$string['cannotsavezipfile'] = 'Kan nie ZIP-lêer stoor nie';
|
||||
$string['cannotunzipfile'] = 'Kan nie lêer uitpak nie';
|
||||
$string['componentisuptodate'] = 'Komponent is bygewerk';
|
||||
$string['dmlexceptiononinstall'] = '<p>\'n Databasisfout het opgeduik [{$a->errorcode}].<br />{$a->debuginfo}</p>';
|
||||
$string['downloadedfilecheckfailed'] = 'Kontrolering van afgelaaide lêer het misluk';
|
||||
$string['invalidmd5'] = 'Die kontroleveranderlike was verkeerd - probeer weer';
|
||||
$string['missingrequiredfield'] = 'Een of ander verlangde veld ontbreek';
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
@@ -34,12 +33,12 @@ $string['clianswerno'] = 'لا';
|
||||
$string['cliansweryes'] = 'نعم';
|
||||
$string['cliincorrectvalueerror'] = 'خطأ، القيمة "{$a->value}" غير صحيحة من أجل "{$a->option}"';
|
||||
$string['cliincorrectvalueretry'] = 'قيمة غير صحيحة، حاول مرة أخرى';
|
||||
$string['clitypevalue'] = 'أدخل القيمة';
|
||||
$string['clitypevaluedefault'] = 'ادخل القيم أو اضغط انتر (Enter) لإستخدام القيم الأفتراضية ({$a})';
|
||||
$string['clitypevalue'] = 'اطبع القيمة';
|
||||
$string['clitypevaluedefault'] = 'ادخل القيم أو اضغط إدخال (Enter) لإستخدام القيم الأفتراضية ({$a})';
|
||||
$string['cliunknowoption'] = 'خيارات غير معروفة
|
||||
{$a}
|
||||
الرجاء استخدام خيار المساعدة';
|
||||
$string['cliyesnoprompt'] = 'ادخل (Y) تعني نعم أو (N) تعني لأ';
|
||||
$string['environmentrequireinstall'] = 'مطلوب تثبيته/تفيله';
|
||||
$string['environmentrequireversion'] = 'مطلوب الأصدار {$a->needed} ، وانت تستخدم الأصدار {$a->current}';
|
||||
$string['cliyesnoprompt'] = 'أدخل y (وتعني نعم) أو n (وتعني لا)';
|
||||
$string['environmentrequireinstall'] = 'يجب تنصيبه/تمكينه';
|
||||
$string['environmentrequireversion'] = 'يتطلب الإصدار {$a->needed}، وأنت تستعمل الإصدار {$a->current}';
|
||||
$string['upgradekeyset'] = 'مفتاح التحديث (اتركها فارغة إن لم ترغب بتحديده)';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
@@ -35,14 +34,19 @@ $string['cannotcreatedboninstall'] = '<p> لا يمكن إنشاء قاعدة ا
|
||||
<p> المسؤول عن الموقع يجب أن يتحقق من إعدادات قاعدة بيانات. </p>';
|
||||
$string['cannotcreatelangdir'] = 'لا يمكن إنشاء مجلد اللغة';
|
||||
$string['cannotcreatetempdir'] = 'لا يمكن إنشاء المجلد المؤقت';
|
||||
$string['cannotdownloadcomponents'] = 'لم يتم تحميل العناصر';
|
||||
$string['cannotdownloadcomponents'] = 'تعذر تنزيل المُكونات';
|
||||
$string['cannotdownloadzipfile'] = 'لم يتم تحميل الملف المضغوط';
|
||||
$string['cannotfindcomponent'] = 'لم يتم العثور على المكون';
|
||||
$string['cannotsavemd5file'] = 'لم يتم حفظ ملف md5';
|
||||
$string['cannotsavezipfile'] = 'لم يتم حفظ الملف المضغوط';
|
||||
$string['cannotunzipfile'] = 'لم يتم فك الملف المضغوط';
|
||||
$string['cannotsavemd5file'] = 'تعذر حفظ ملف md5';
|
||||
$string['cannotsavezipfile'] = 'تعذر حفظ الملف المضغوط';
|
||||
$string['cannotunzipfile'] = 'تعذر فك الملف المضغوط';
|
||||
$string['componentisuptodate'] = 'العنصر محدث';
|
||||
$string['dmlexceptiononinstall'] = '<p>حدث خطأ في قاعدة البيانات[{$a->errorcode}].<br />{$a->debuginfo}</p>';
|
||||
$string['downloadedfilecheckfailed'] = 'فشل التحقق من الملف الذي تم تنزيله';
|
||||
$string['invalidmd5'] = 'المتغير المُختار خاطئ - حاول مرة أخرى';
|
||||
$string['missingrequiredfield'] = 'بعض الحقول المطلوبة مفقودة';
|
||||
$string['remotedownloaderror'] = '<p>فشل تنزيل المكون إلى مُخدمك. لطفاً تحقق من إعدادات الوكيل؛ الامتداد cURL لـ PHP موصى به بشدة.</p>
|
||||
<p>عليك تنزيل الملف <a href="{$a->url}">{$a->url}</a> يدوياً، ونسخه إلى "{$a->dest}" في مُخدمك ثم القيام بفك ضغطه هناك.</p>';
|
||||
$string['wrongdestpath'] = 'مسار الهدف خاطئ';
|
||||
$string['wrongsourcebase'] = 'أساس رابط المصدر خاطئ';
|
||||
$string['wrongzipfilename'] = 'تسمية الملف المضغوط خاطئة';
|
||||
|
||||
+68
-15
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
// This file is part of Moodle - https://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
|
||||
@@ -13,7 +12,7 @@
|
||||
// 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/>.
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
@@ -31,22 +30,76 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['admindirname'] = 'مجلد الإدارة';
|
||||
$string['availablelangs'] = 'حزم اللغة المتوفره';
|
||||
$string['chooselanguagehead'] = 'اختر اللغة';
|
||||
$string['availablelangs'] = 'حزم اللغات المتاحة';
|
||||
$string['chooselanguagehead'] = 'إختر اللغة';
|
||||
$string['chooselanguagesub'] = 'الرجاء حدد لغة للتثبيت. هذه اللغة ستستخدم أيضاً كاللغة الافتراضية للموقع، لكن يمكنك تغييرها لاحقا.';
|
||||
$string['clialreadyinstalled'] = 'الملف config.php موجود مسبقاً، يرجى استخدام admin/cli/upgrade.php إن كنت تريد تحديث موقعك.';
|
||||
$string['clialreadyconfigured'] = 'ملف التهيئة config.php موجود سلفاً. لطفاً، استعمل الرابط ذي المسار admin/cli/install_database.php لتنصيب مودل في هذا الموقع.';
|
||||
$string['clialreadyinstalled'] = 'ملف التهيئة config.php موجود سلفاً. لطفاً، استعمل الرابط ذي المسار admin/cli/install_database.php لترقية مودل في هذا الموقع.';
|
||||
$string['cliinstallheader'] = 'برنامج تنصيب مودل {$a} عبر سطر الأوامر النصية';
|
||||
$string['databasehost'] = 'مستضيف قاعدة البيانات';
|
||||
$string['databasename'] = 'اسم قاعدة البيانات';
|
||||
$string['databasetypehead'] = 'إختر مشغل قاعدة البيانات';
|
||||
$string['dataroot'] = 'مجلد البيانات';
|
||||
$string['dbprefix'] = 'مقدمة الجداول';
|
||||
$string['datarootpermission'] = 'صلاحيات مجلدات البيانات';
|
||||
$string['dbprefix'] = 'بادئة الجداول';
|
||||
$string['dirroot'] = 'مجلد مودل';
|
||||
$string['environmenthead'] = 'يتم فحص البيئة';
|
||||
$string['installation'] = 'تثبيت';
|
||||
$string['paths'] = 'مسارات';
|
||||
$string['environmentsub2'] = 'كل إصدار من مودل يتطلب وجود إصدار معين من PHP على الأقل مع عدد من امتداداته الإجبارية.
|
||||
يجري فحص شامل لبيئة العمل قبل كل تنصيب وترقية. لطفاً، تواصل مع المشرف على المخدم إن لم تكن على دراية بكيفية تنصيب الإصدار الأحدث من PHP أو امتداداته.';
|
||||
$string['errorsinenvironment'] = 'فشلت مهمة التحقق من بيئة التشغيل!';
|
||||
$string['installation'] = 'التثبيت';
|
||||
$string['langdownloaderror'] = 'مع الأسف، اللغة "{$a}" تعذر تنزيلها. عملية التنصيب ستستمر باللغة الإنجليزية.';
|
||||
$string['memorylimithelp'] = '<p>إن حد استعمال الذاكرة لـ PHP في مخدمك حالياً هو {$a}.</p>
|
||||
|
||||
<p>هذا قد يسبب لمودل مشاكل في الذاكرة لاحقاً، خصوصاً
|
||||
إن كانت لديك الكثير من الوحدات مُمَكَّنة و/أو الكثير من المستخدمين.</p>
|
||||
|
||||
<p>نوصي بضبط ذاكرة PHP على حد أعلى إن أمكن، مثلاً 40M.
|
||||
هناك عدة طرق للقيام بذلك والتي بإمكانك تجربتها:</p>
|
||||
<ol>
|
||||
<li>إن كنت تستطيع القيام بذلك، أعد تجميع PHP مع المفتاح <i>--enable-memory-limit</i>.
|
||||
هذا سيسمح لمودل بضبط حد الذاكرة بنفسه.</li>
|
||||
<li>إن كنت تستطيع الوصول إلى ملف php.ini، يمكنك تغيير الخاصية <b>memory_limit</b>
|
||||
هناك إلى شيء آخر مثل 40M. إن لم يكن بإمكانك الوصول إليه
|
||||
فقد تستطيع مطالبة المشرف بالقيام بذلك من جانبه.</li>
|
||||
<li>في بعض مخدمات PHP يمكنك إنشاء ملف .htaccess في مجلد مودل
|
||||
وجعله محتوياً على السطر الآتي:
|
||||
<blockquote><div>php_value memory_limit 40M</div></blockquote>
|
||||
<p>مع ذلك، في بعض المخدمات، هذا من شأنه منع <b>كل</b> صفحات PHP من العمل
|
||||
(ستشاهد أخطاءً عند النظر إلى الصفحات) مما يحتم عليك إزالة ملف .htaccess.</p></li>
|
||||
</ol>';
|
||||
$string['paths'] = 'المسارات';
|
||||
$string['pathserrcreatedataroot'] = 'مجلد البيانات ({$a->dataroot}) لا يمكن إنشاؤه من قبل برنامج التنصيب.';
|
||||
$string['pathshead'] = 'تأكيد المسارات';
|
||||
$string['phpversion'] = 'أصدار PHP';
|
||||
$string['phpversionhelp'] = '<p> يتطلب مودل على الاقل الأصدار 4.3.0 لـ PHP </p>
|
||||
<p> انت تستخدم الأصدار {$a} </p>
|
||||
<p> يجب عليك ترقية PHP أو الانتقال إلى مستظيف أخر لديه أصدار اجد لـ PHP.</p>
|
||||
في حالة وجود إصدار 5.0 فما بعد يمكنك الرجوع إلى إصدار 4.4 فما بعد';
|
||||
$string['wwwroot'] = 'WWW';
|
||||
$string['pathsrodataroot'] = 'مجلد البيانات الرئيسي غير قابل للكتابة.';
|
||||
$string['pathsroparentdataroot'] = 'المجلد ذي الرتبة الأعلى ({$a->parent}) غير قابل للكتابة. مجلد البيانات ({$a->dataroot}) لا يمكن إنشاؤه من قِبَل برنامج التنصيب.';
|
||||
$string['pathssubadmindir'] = 'القليل جداً من مستضيفي الويب يستعمل /admin بمثابة رابط للوصول إلى لوحة التحكم أو ما سواها. لسوء الحظ هذا يتعارض مع الموضع القياسي لصفحات إدارة مودل. يمكنك حل هذه المشكلة عبر إعادة تسمية مجلد الإدارة admin في نسختك من هذا التنصيب، لتضع هذا الاسم الجديد هنا. مثلاً: <em>moodleadmin</em>. هذا من شأنه إصلاح روابط الإدارة في مودل.';
|
||||
$string['pathssubdataroot'] = '<p>المجلد الذي يخزن فيه مودل كل المحتوى من الملفات التي يرفعها المستخدمون.</p>
|
||||
<p>هذا المجلد ينبغي أن يكون قابلاً للقراءة والكتابة من قبل مستخدم مخدم الويب (عادة هو \'www-data\'، \'nobody\'، أو \'apache\').</p>
|
||||
<p>ينبغي أن لا يكون متاحاً للوصول المباشر عبر الويب.</p>
|
||||
<p>إن كان المجلد غير موجود حالياً، فعملية التنصيب ستحاول إنشاءه.</p>';
|
||||
$string['pathssubdirroot'] = '<p>المسار الكامل للمجلد الذي يحتوي على ترميز نظام مودل.</p>';
|
||||
$string['pathssubwwwroot'] = '<p>العنوان الكامل حيث سيتم الوصول به إلى مودل، بعبارة أخرى، العنوان الذي على المستخدمين إدخاله في شريط العناوين لمستعرض الإنترنت للوصول إلى مودل.</p>
|
||||
<p>ليس من الممكن الوصول إلى مودل عبر عناوين متعددة. إن كان موقعك قابلاً للوصول عبر عناوين متعددة، فعليك اختيار أسهلها واستعمل إعادة توجيه دائمية من باقي العناوين إلى هذا العنوان.</p>
|
||||
<p>إن كان موقعك قابلاً للوصول من شبكة الإنترنت ومن شبكتك الداخلية، الإنترانت، استعمل العنوان الخارجي على الإنترنت هنا.</p>
|
||||
<p>إذا كان العنوان الحالي خاطئاً، لطفاً، غيِّر الرابط في شريط العناوين لمتصفحك ثم أعد عملية التنصيب.</p>';
|
||||
$string['pathsunsecuredataroot'] = 'موضع مجلد البيانات الرئيسي غير مُؤَمن';
|
||||
$string['pathswrongadmindir'] = 'مجلد المشرف غير موجود';
|
||||
$string['phpextension'] = 'إمتداد PHP {$a}';
|
||||
$string['phpversion'] = 'إصدار PHP';
|
||||
$string['phpversionhelp'] = '<p>يتطلب مودل على الأقل وجود PHP بالإصدار 5.6.5 أو 7.1 (الإصدار 7.0.x فيه بعض القيود عند التشغيل).</p>
|
||||
<p>انت تستعمل حالياً الإصدار {$a}.</p>
|
||||
<p>يجب عليك ترقية PHP أو الانتقال إلى مستضيف آخر لديه إصدار أحدث من PHP.</p>';
|
||||
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
|
||||
$string['welcomep20'] = 'أنت تشاهد هذه الصفحة لأنك تمكنت بنجاح من تنصيب وإطلاق
|
||||
حزمة <strong>{$a->packname} {$a->packversion}</strong> في حاسبتك. تهانينا!';
|
||||
$string['welcomep30'] = 'الإطلاق العائد لـ <strong>{$a->installername}</strong> يتضمن
|
||||
التطبيقات المعدة لإنشاء بيئة تسمح لـ <strong>مودل</strong> بالعمل، وهي:';
|
||||
$string['welcomep40'] = 'الحزمة تتضمن أيضاً <strong>مودل {$a->moodlerelease} ({$a->moodleversion})</strong>.';
|
||||
$string['welcomep50'] = 'استعمال كل التطبيقات في هذه الحزمة محكوم برخصها. حزمة التنصيب <strong>{$a->installername}</strong> الكاملة هي <a href="https://www.opensource.org/docs/definition_plain.html">مفتوحة المصدر</a> وموزعة بموجب الرخصة <a href="https://www.gnu.org/copyleft/gpl.html">GPL</a>.';
|
||||
$string['welcomep60'] = 'الصفحات الآتية ستقودك عبر خطوات سهلة التتبع لتنصيب
|
||||
وتهيئة <strong>مودل</strong> في حاسبتك. يمكنك قبول
|
||||
الإعدادات الافتراضية، أو إختيارياً، تغييرها بما يتناسب مع احتياجاتك الخاصة.';
|
||||
$string['welcomep70'] = 'أنقر زر "التالي" أدناه لمتابعة عملية تنصيب
|
||||
<strong>مودل</strong>.';
|
||||
$string['wwwroot'] = 'عنوان الويب';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user