Compare commits
326 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 47806ea17e | |||
| a0473f798d | |||
| b4e5db392e | |||
| b775dad6e6 | |||
| 56d67f1a9d | |||
| e9f2e5bea7 | |||
| 6416b84bd6 | |||
| 4e4e8e1da9 | |||
| 2a55226144 | |||
| 46672f08d6 | |||
| 9563554a1a | |||
| 73ad749ca0 | |||
| e1f29cf6e2 | |||
| 40867c234a | |||
| 22861d00c7 | |||
| 5c280e9e1e | |||
| 61453e1a79 | |||
| 30a9fe133a | |||
| 879b5bb6fc | |||
| b406f53662 | |||
| ec0edd25ac | |||
| 8d30e8370d | |||
| d49170cb14 | |||
| c3001c7457 | |||
| 4f5a6e28ec | |||
| 395a315e9f | |||
| 501f7d53c6 | |||
| a7a037ecde | |||
| 967a7bc2ac | |||
| defc74db0d | |||
| 222e854a9e | |||
| ccb97f2eef | |||
| cf3ac3f218 | |||
| c25d8efc47 | |||
| abc0cac40a | |||
| eef85ddc40 | |||
| b65a928372 | |||
| 47560d0eb0 | |||
| 068904f73b | |||
| 65432b1b7b | |||
| 8fba57e950 | |||
| a933f85a73 | |||
| 8cba7fda3a | |||
| f852010c6c | |||
| 2a5bb77321 | |||
| 2212fbf028 | |||
| 1a4184a396 | |||
| d722da1bb4 | |||
| c58312fe82 | |||
| cdea4ac6b9 | |||
| 6db40f889f | |||
| e0db9ee82f | |||
| b3a751da05 | |||
| 388367917d | |||
| 3141d42c63 | |||
| 0c725854e5 | |||
| 965b65b7fc | |||
| 759f83aa63 | |||
| d046221fba | |||
| ad1e11c2e4 | |||
| e1f14856da | |||
| 7d0add9d19 | |||
| 7ef6433c2f | |||
| f80645cc48 | |||
| 184a6e1647 | |||
| 89ea16e44e | |||
| 938a970d1f | |||
| 6513308ba7 | |||
| 9c6b1e40e5 | |||
| 9ba44fa3fd | |||
| ddbd25d941 | |||
| 9c3b409079 | |||
| 161cb17822 | |||
| c944c35cff | |||
| 31ba691136 | |||
| 6f8f5068f2 | |||
| 2a1401d20f | |||
| a1b8fc6ab7 | |||
| 3d152bc38e | |||
| 640c427b68 | |||
| 9a4c8f1c1a | |||
| 19a7ed9484 | |||
| 27733ee4d0 | |||
| 5a1ead041a | |||
| 04bb5da9de | |||
| 25a4f6c2d6 | |||
| 3078a3b7a1 | |||
| 82f52c19fa | |||
| 143e3dc11c | |||
| 7403d5ea59 | |||
| a1a562fbbe | |||
| e31f0fdf7e | |||
| b228a7cf40 | |||
| e9ae977cb8 | |||
| 31a5f2a08c | |||
| f9bca6404e | |||
| 78c703bc12 | |||
| f1198d7998 | |||
| 29cc92c86f | |||
| 2ac06ec994 | |||
| 4c19da2a1e | |||
| ac081697da | |||
| 97b0761cdf | |||
| 54b80ab68d | |||
| 4ec683ac2f | |||
| 41931b749d | |||
| 79796a1590 | |||
| 793d92aa86 | |||
| e0b8d7864f | |||
| a8bab41277 | |||
| 20ede33d61 | |||
| cd7cc95637 | |||
| 1348e21d66 | |||
| 99e7d66fba | |||
| d53a433b6f | |||
| 3a71830435 | |||
| 862792e1a1 | |||
| 80d2c7cfb0 | |||
| b36b4f05c7 | |||
| 063bf930f9 | |||
| fdc9faed62 | |||
| 2d0bbd3b6e | |||
| dd171628ea | |||
| a11180570f | |||
| eb36cfcbb6 | |||
| 7f2fd69024 | |||
| 97bf703346 | |||
| 89bacb163f | |||
| f31567294d | |||
| 055bf3ca61 | |||
| e9019af21d | |||
| df221cbf8d | |||
| e4f4a7c353 | |||
| 1036d079c1 | |||
| f000a6635b | |||
| 479b269fe9 | |||
| 6ba23735ab | |||
| 988b11c4da | |||
| 1b43a5372c | |||
| cbe10ace43 | |||
| de854b05c3 | |||
| 465db5bd7c | |||
| 2579f5f1fa | |||
| 50804bc956 | |||
| 6b90efd076 | |||
| 64995b83fc | |||
| 041e40cf1b | |||
| e2dee6f3cd | |||
| afee01ac2f | |||
| f4d1b3ec40 | |||
| d5088c4ef5 | |||
| cbcbf005fb | |||
| 4e1af2ead5 | |||
| 34d728d3e9 | |||
| cb2844dc40 | |||
| ccddc71ecb | |||
| 9759072f4f | |||
| 56fb03794c | |||
| 9b1232cb59 | |||
| 1e0b57f90d | |||
| b1ab556419 | |||
| 6bf8caa573 | |||
| 9d2e2a80be | |||
| 315c9ba2d4 | |||
| 86e079521b | |||
| b25610f206 | |||
| 0e31c8f92b | |||
| 8e6783526e | |||
| 5f738b0be5 | |||
| 0bca566c04 | |||
| 2cfe2ecee6 | |||
| 0ab99aa340 | |||
| e12f32dcda | |||
| a7221c5f19 | |||
| 940fe6f99c | |||
| 143f3624cf | |||
| bdcc0dd59f | |||
| b77709ca67 | |||
| 557ca0146d | |||
| 261eb32955 | |||
| 28ac27c0c2 | |||
| 894ed0c657 | |||
| 07c8765f2b | |||
| c4c826305d | |||
| cff883d5a1 | |||
| b5882aa15c | |||
| e744a082e1 | |||
| 23bba6e190 | |||
| f9a6276f42 | |||
| 5f9e0cd58b | |||
| 082a4b6b6c | |||
| 50b19dc897 | |||
| beb50de63d | |||
| 3863f17ffe | |||
| 233231def7 | |||
| 9685d369ad | |||
| d991bbefe9 | |||
| ba7a9b6b9c | |||
| edc4a0af61 | |||
| a3bc32d4e9 | |||
| 0563e83290 | |||
| ec82991b97 | |||
| bf98b7fe3b | |||
| 2331a70c89 | |||
| 63ba15c7db | |||
| 2398d9160a | |||
| a72a8de00c | |||
| 490b6ff9ea | |||
| 7512e4d33c | |||
| adaf294a75 | |||
| 906c52ee96 | |||
| 8dc6bb8591 | |||
| 5043e4c31c | |||
| dbc6c12766 | |||
| 8abe0e83ee | |||
| 318ff4acd7 | |||
| e9ada19fbb | |||
| af8fc4c11d | |||
| a2ae2c665f | |||
| e7bb6f945c | |||
| aa6fa3a56e | |||
| 94f5d8a803 | |||
| 162dbe8d8c | |||
| 5761f5831b | |||
| d4e14393b4 | |||
| 76c2a5dfbd | |||
| 5bb6def595 | |||
| 1b6d059624 | |||
| f0043725b5 | |||
| c8f3dfd3af | |||
| bbe5d6c827 | |||
| 34d3e68c0d | |||
| fa46696e27 | |||
| 30062d5a8e | |||
| cbf3b1db5e | |||
| 599293befd | |||
| 7141480e85 | |||
| 32e79050f8 | |||
| b0f7923ff0 | |||
| b30e4fd68d | |||
| eb17d699ab | |||
| 4f66e19b09 | |||
| 1b27eacc4a | |||
| 88ed1fab62 | |||
| 328bc43039 | |||
| 6862093122 | |||
| 9fb7f10fc5 | |||
| 50e4de2385 | |||
| 347cab92be | |||
| 86d0cd25c4 | |||
| b1d6b851c0 | |||
| 9ec8754e68 | |||
| 117b5cb9a3 | |||
| 035cc9e043 | |||
| b42e0d4465 | |||
| 6294da8660 | |||
| 88e39d3fdd | |||
| 8a101cc06d | |||
| eb61d44fec | |||
| 44ed5d73f2 | |||
| 5f14cedd3a | |||
| c32dd85426 | |||
| 502a3013ce | |||
| fdf8bf0793 | |||
| 78792b028f | |||
| c183e31da0 | |||
| 6381f3bfe0 | |||
| 73cf33aad4 | |||
| 49634dadc9 | |||
| f14ddb2a8c | |||
| 245aed199b | |||
| 87e2b6154c | |||
| c2235a04bf | |||
| 3574cae7ff | |||
| 11c59cb6b4 | |||
| 89b2e4fb30 | |||
| 5a2228cf82 | |||
| 6898e4da78 | |||
| 83350ad075 | |||
| cfb0491dad | |||
| 54720d454f | |||
| 4c5ab56a33 | |||
| fd52a9fbe2 | |||
| 849b9e71b0 | |||
| 1b383c0177 | |||
| 3077735595 | |||
| 8b374f593c | |||
| 0995a547d9 | |||
| 330216eb8a | |||
| fb91edbe50 | |||
| 6760e6db34 | |||
| 66da5c76b3 | |||
| 782aa36c34 | |||
| 1ad3ddcd24 | |||
| d30f466a17 | |||
| f7a5f1c835 | |||
| 93f06a35dd | |||
| 9efa43c6a9 | |||
| 80d33cde74 | |||
| 89fba73a45 | |||
| 95b82c9dcf | |||
| b1b097e2b6 | |||
| d7f63b7499 | |||
| 9cfc039669 | |||
| f60b424d82 | |||
| f5cd3cb44d | |||
| 14b4959d75 | |||
| 5d2f086eea | |||
| 5cd7c8e84f | |||
| 2e458712c8 | |||
| 87ae1417bf | |||
| 01fbf70982 | |||
| fad41516de | |||
| 1987b771ad | |||
| 0761279e9e | |||
| f073424c08 | |||
| 7b6cb91d6c | |||
| 98e4407615 | |||
| 4f92858527 | |||
| 025e98434d | |||
| 48e08145e2 | |||
| 0f365eb0b2 | |||
| d0adf43eec | |||
| 4ac0e2d5e1 | |||
| dabfdc2c6c | |||
| 1f010acdd0 |
+69
-40
@@ -27,16 +27,19 @@ env:
|
||||
# Postgres is significantly is pretty reasonable in its run-time.
|
||||
|
||||
# Run unit tests on MySQL
|
||||
- DB=mysqli PHPUNIT=true INSTALL=false CITEST=false
|
||||
- DB=mysqli TASK=PHPUNIT
|
||||
|
||||
# Run CI Tests without running PHPUnit.
|
||||
- DB=none PHPUNIT=false INSTALL=false CITEST=true
|
||||
- DB=none TASK=CITEST
|
||||
|
||||
# Run unit tests on Postgres
|
||||
- DB=pgsql PHPUNIT=true INSTALL=false CITEST=false
|
||||
- DB=pgsql TASK=PHPUNIT
|
||||
|
||||
# Perform an upgrade test too.
|
||||
- DB=pgsql PHPUNIT=false INSTALL=true CITEST=false UPGRADE=true
|
||||
- DB=pgsql TASK=UPGRADE
|
||||
|
||||
# Run a check for unbuilt files with Grunt.
|
||||
- DB=none TASK=GRUNT
|
||||
|
||||
matrix:
|
||||
# Enable fast finish.
|
||||
@@ -47,22 +50,26 @@ matrix:
|
||||
exclude:
|
||||
# MySQL - it's just too slow.
|
||||
# Exclude it on all versions except for 7.0
|
||||
# - env: DB=mysqli PHPUNIT=true INSTALL=false CITEST=false
|
||||
# - env: DB=mysqli TASK=PHPUNIT
|
||||
# php: 5.6
|
||||
#
|
||||
# - env: DB=mysqli PHPUNIT=true INSTALL=false CITEST=false
|
||||
# - env: DB=mysqli TASK=PHPUNIT
|
||||
# php: 5.5
|
||||
|
||||
- env: DB=mysqli PHPUNIT=true INSTALL=false CITEST=false
|
||||
- env: DB=mysqli TASK=PHPUNIT
|
||||
php: 5.4
|
||||
|
||||
- env: DB=none TASK=GRUNT
|
||||
php: 5.4
|
||||
|
||||
# Moodle 2.7 is not compatible with PHP 7 for the upgrade test.
|
||||
- env: DB=pgsql PHPUNIT=false INSTALL=true CITEST=false UPGRADE=true
|
||||
- env: DB=pgsql TASK=UPGRADE
|
||||
php: 7.0
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
- $HOME/.npm
|
||||
|
||||
install:
|
||||
# Disable xdebug. We aren't generating code coverage, and it has a huge impact upon test performance.
|
||||
@@ -70,6 +77,7 @@ install:
|
||||
|
||||
# Set the encrypted GITHUB_TOKEN if it's available to raise the API limit.
|
||||
- if [ -n "$GITHUB_APITOKEN" ]; then composer config github-oauth.github.com $GITHUB_APITOKEN; fi
|
||||
- echo 'auth.json' >> .git/info/exclude
|
||||
|
||||
# Install composer dependencies.
|
||||
# We need --no-interaction in case we hit API limits for composer. This causes it to fall back to a standard clone.
|
||||
@@ -78,7 +86,7 @@ install:
|
||||
|
||||
before_script:
|
||||
- >
|
||||
if [ "$INSTALL" = 'true' -o "$PHPUNIT" = 'true' ];
|
||||
if [ "$TASK" = 'PHPUNIT' -o "$TASK" = 'UPGRADE' ];
|
||||
then
|
||||
# Copy generic configuration in place.
|
||||
cp config-dist.php config.php ;
|
||||
@@ -120,38 +128,37 @@ before_script:
|
||||
mysql -u root -e 'SET GLOBAL innodb_file_per_table=ON;' ;
|
||||
mysql -e 'CREATE DATABASE travis_ci_test DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_bin;' ;
|
||||
fi
|
||||
|
||||
if [ "$PHPUNIT" = 'true' ];
|
||||
then
|
||||
# Create a directory for the phpunit dataroot.
|
||||
mkdir -p "$HOME"/roots/phpunit
|
||||
|
||||
# The phpunit dataroot and prefix..
|
||||
sed -i \
|
||||
-e "/require_once/i \\\$CFG->phpunit_dataroot = '\/home\/travis\/roots\/phpunit';" \
|
||||
-e "/require_once/i \\\$CFG->phpunit_prefix = 'p_';" \
|
||||
config.php ;
|
||||
|
||||
# Initialise PHPUnit for Moodle.
|
||||
php admin/tool/phpunit/cli/init.php
|
||||
fi
|
||||
fi
|
||||
|
||||
script:
|
||||
########################################################################
|
||||
# PHPUnit
|
||||
########################################################################
|
||||
- >
|
||||
if [ "$PHPUNIT" = 'true' ];
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
then
|
||||
vendor/bin/phpunit;
|
||||
# Create a directory for the phpunit dataroot.
|
||||
mkdir -p "$HOME"/roots/phpunit
|
||||
|
||||
# The phpunit dataroot and prefix..
|
||||
sed -i \
|
||||
-e "/require_once/i \\\$CFG->phpunit_dataroot = '\/home\/travis\/roots\/phpunit';" \
|
||||
-e "/require_once/i \\\$CFG->phpunit_prefix = 'p_';" \
|
||||
config.php ;
|
||||
|
||||
# Initialise PHPUnit for Moodle.
|
||||
php admin/tool/phpunit/cli/init.php
|
||||
fi
|
||||
|
||||
- >
|
||||
if [ "$TASK" = 'GRUNT' ];
|
||||
then
|
||||
npm install ;
|
||||
npm install -g grunt ;
|
||||
grunt ;
|
||||
fi
|
||||
|
||||
########################################################################
|
||||
# CI Tests
|
||||
########################################################################
|
||||
- >
|
||||
if [ "$CITEST" = 'true' ];
|
||||
if [ "$TASK" = 'CITEST' ];
|
||||
then
|
||||
# Note - this is deliberately placed in the script section as we
|
||||
# should not add any code until after phpunit has run.
|
||||
@@ -163,7 +170,7 @@ script:
|
||||
# We need the official upstream for comparison
|
||||
git remote add upstream https://github.com/moodle/moodle.git;
|
||||
|
||||
git fetch upstream master;
|
||||
git fetch upstream MOODLE_31_STABLE;
|
||||
export GIT_PREVIOUS_COMMIT="`git merge-base FETCH_HEAD $TRAVIS_COMMIT`";
|
||||
export GIT_COMMIT="$TRAVIS_COMMIT";
|
||||
export UPSTREAM_FETCH_HEAD=`git rev-parse FETCH_HEAD`
|
||||
@@ -174,18 +181,11 @@ script:
|
||||
export phpcmd=`which php`;
|
||||
fi
|
||||
|
||||
# Actually run the CI Tests - do this outside of the main test to make output clearer.
|
||||
- >
|
||||
if [ "$CITEST" = 'true' ];
|
||||
then
|
||||
bash local/ci/php_lint/php_lint.sh;
|
||||
fi
|
||||
|
||||
########################################################################
|
||||
# Upgrade test
|
||||
########################################################################
|
||||
- >
|
||||
if [ "$UPGRADE" = 'true' ];
|
||||
if [ "$TASK" = 'UPGRADE' ];
|
||||
then
|
||||
# We need the official upstream.
|
||||
git remote add upstream https://github.com/moodle/moodle.git;
|
||||
@@ -205,7 +205,36 @@ script:
|
||||
|
||||
# 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' ];
|
||||
then
|
||||
vendor/bin/phpunit;
|
||||
fi
|
||||
|
||||
- >
|
||||
if [ "$TASK" = 'CITEST' ];
|
||||
then
|
||||
bash local/ci/php_lint/php_lint.sh;
|
||||
fi
|
||||
|
||||
- >
|
||||
if [ "$TASK" = 'GRUNT' ];
|
||||
then
|
||||
# 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.
|
||||
git add . ;
|
||||
git diff --cached --exit-code ;
|
||||
fi
|
||||
|
||||
########################################################################
|
||||
# Upgrade test
|
||||
########################################################################
|
||||
- >
|
||||
if [ "$TASK" = 'UPGRADE' ];
|
||||
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
|
||||
|
||||
@@ -1703,4 +1703,149 @@
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.2" requires="2.7">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="5.5.31" />
|
||||
<VENDOR name="mysql" version="5.5.31" />
|
||||
<VENDOR name="postgres" version="9.1" />
|
||||
<VENDOR name="mssql" version="10.0" />
|
||||
<VENDOR name="oracle" version="10.2" />
|
||||
</DATABASE>
|
||||
<PHP version="5.6.5" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="mbstringrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opensslrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="intlrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
</PHP_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_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
@@ -36,6 +36,8 @@ require_login($course, false, $cm);
|
||||
require_capability('moodle/role:review', $context);
|
||||
require_sesskey();
|
||||
|
||||
$OUTPUT->header();
|
||||
|
||||
list($overridableroles, $overridecounts, $nameswithcounts) = get_overridable_roles($context,
|
||||
ROLENAME_BOTH, true);
|
||||
|
||||
|
||||
@@ -71,8 +71,9 @@ class core_role_preset {
|
||||
$dom->appendChild($top);
|
||||
|
||||
$top->appendChild($dom->createElement('shortname', $role->shortname));
|
||||
$top->appendChild($dom->createElement('name', $role->name));
|
||||
$top->appendChild($dom->createElement('description', $role->description));
|
||||
$top->appendChild($dom->createElement('name', htmlspecialchars($role->name, ENT_COMPAT | ENT_HTML401, 'UTF-8')));
|
||||
$top->appendChild($dom->createElement('description', htmlspecialchars($role->description, ENT_COMPAT | ENT_HTML401,
|
||||
'UTF-8')));
|
||||
$top->appendChild($dom->createElement('archetype', $role->archetype));
|
||||
|
||||
$contextlevels = $dom->createElement('contextlevels');
|
||||
|
||||
@@ -101,6 +101,9 @@ if (empty($options['torun'])) {
|
||||
if (extension_loaded('pcntl')) {
|
||||
$disabled = explode(',', ini_get('disable_functions'));
|
||||
if (!in_array('pcntl_signal', $disabled)) {
|
||||
// Handle interrupts on PHP7.
|
||||
declare(ticks = 1);
|
||||
|
||||
pcntl_signal(SIGTERM, "signal_handler");
|
||||
pcntl_signal(SIGINT, "signal_handler");
|
||||
}
|
||||
|
||||
@@ -38,5 +38,8 @@ function xmldb_tool_customlang_upgrade($oldversion) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,5 +42,8 @@ function xmldb_tool_log_upgrade($oldversion) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -36,5 +36,8 @@ function xmldb_logstore_database_upgrade($oldversion) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -53,5 +53,8 @@ function xmldb_logstore_standard_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2016041200, 'logstore', 'standard');
|
||||
}
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
define(["jquery"],function(a){var b=function(b,c,d,e,f,g){this._baseUrl=d,this._userId=e+"",this._competencyId=f+"",this._courseId=g,a(b).on("change",this._userChanged.bind(this)),a(c).on("change",this._competencyChanged.bind(this))};return b.prototype._userChanged=function(b){var c=a(b.target).val(),d="?userid="+c+"&courseid="+this._courseId+"&competencyid="+this._competencyId;document.location=this._baseUrl+d},b.prototype._competencyChanged=function(b){var c=a(b.target).val(),d="?userid="+this._userId+"&courseid="+this._courseId+"&competencyid="+c;document.location=this._baseUrl+d},b.prototype._competencyId=null,b.prototype._userId=null,b.prototype._courseId=null,b.prototype._baseUrl=null,b.prototype._ignoreFirstUser=null,b.prototype._ignoreFirstCompetency=null,b});
|
||||
define(["jquery"],function(a){var b=function(b,c,d,e,f,g){this._baseUrl=d,this._userId=e+"",this._competencyId=f+"",this._courseId=g,a(b).on("change",this._userChanged.bind(this)),a(c).on("change",this._competencyChanged.bind(this))};return b.prototype._userChanged=function(b){var c=a(b.target).val(),d="?userid="+c+"&courseid="+this._courseId+"&competencyid="+this._competencyId;document.location=this._baseUrl+d},b.prototype._competencyChanged=function(b){var c=a(b.target).val(),d="?userid="+this._userId+"&courseid="+this._courseId+"&competencyid="+c;document.location=this._baseUrl+d},b.prototype._competencyId=null,b.prototype._userId=null,b.prototype._courseId=null,b.prototype._baseUrl=null,b.prototype._ignoreFirstCompetency=null,b});
|
||||
@@ -75,8 +75,6 @@ define(['jquery'], function($) {
|
||||
UserCompetencyCourseNavigation.prototype._courseId = null;
|
||||
/** @type {String} Plugin base url. */
|
||||
UserCompetencyCourseNavigation.prototype._baseUrl = null;
|
||||
/** @type {Boolean} Ignore the first change event for users. */
|
||||
UserCompetencyCourseNavigation.prototype._ignoreFirstUser = null;
|
||||
/** @type {Boolean} Ignore the first change event for competencies. */
|
||||
UserCompetencyCourseNavigation.prototype._ignoreFirstCompetency = null;
|
||||
|
||||
|
||||
@@ -40,14 +40,18 @@ class course_module_summary_exporter extends \core_competency\external\exporter
|
||||
}
|
||||
|
||||
protected function get_other_values(renderer_base $output) {
|
||||
$context = $this->related['cm']->context;
|
||||
$cm = $this->related['cm'];
|
||||
$context = $cm->context;
|
||||
|
||||
return array(
|
||||
'id' => $this->related['cm']->id,
|
||||
'name' => external_format_string($this->related['cm']->name, $context->id),
|
||||
'url' => $this->related['cm']->url->out(),
|
||||
'iconurl' => $this->related['cm']->get_icon_url()->out()
|
||||
$values = array(
|
||||
'id' => $cm->id,
|
||||
'name' => external_format_string($cm->name, $context->id),
|
||||
'iconurl' => $cm->get_icon_url()->out()
|
||||
);
|
||||
if ($cm->url) {
|
||||
$values['url'] = $cm->url->out();
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +64,8 @@ class course_module_summary_exporter extends \core_competency\external\exporter
|
||||
'type' => PARAM_TEXT
|
||||
),
|
||||
'url' => array(
|
||||
'type' => PARAM_URL
|
||||
'type' => PARAM_URL,
|
||||
'optional' => true,
|
||||
),
|
||||
'iconurl' => array(
|
||||
'type' => PARAM_URL
|
||||
|
||||
@@ -40,11 +40,11 @@ $url = new moodle_url('/admin/tool/lp/coursecompetencies.php', $urlparams);
|
||||
list($title, $subtitle) = \tool_lp\page_helper::setup_for_course($url, $course);
|
||||
|
||||
$output = $PAGE->get_renderer('tool_lp');
|
||||
$page = new \tool_lp\output\course_competencies_page($course->id);
|
||||
|
||||
echo $output->header();
|
||||
echo $output->heading($title);
|
||||
|
||||
|
||||
$page = new \tool_lp\output\course_competencies_page($course->id);
|
||||
echo $output->render($page);
|
||||
|
||||
echo $output->footer();
|
||||
|
||||
+10
-3
@@ -36,6 +36,13 @@ function tool_lp_extend_navigation_course($navigation, $course, $coursecontext)
|
||||
return;
|
||||
}
|
||||
|
||||
// Check access to the course and competencies page.
|
||||
$capabilities = array('moodle/competency:coursecompetencyview', 'moodle/competency:coursecompetencymanage');
|
||||
$context = context_course::instance($course->id);
|
||||
if (!has_any_capability($capabilities, $context) || !can_access_course($course)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Just a link to course competency.
|
||||
$title = get_string('competencies', 'core_competency');
|
||||
$path = new moodle_url("/admin/tool/lp/coursecompetencies.php", array('courseid' => $course->id));
|
||||
@@ -44,7 +51,7 @@ function tool_lp_extend_navigation_course($navigation, $course, $coursecontext)
|
||||
navigation_node::TYPE_SETTING,
|
||||
null,
|
||||
null,
|
||||
new pix_icon('competency', '', 'tool_lp'));
|
||||
new pix_icon('i/competencies', ''));
|
||||
if (isset($settingsnode)) {
|
||||
$navigation->add_node($settingsnode);
|
||||
}
|
||||
@@ -129,7 +136,7 @@ function tool_lp_extend_navigation_category_settings($navigation, $coursecategor
|
||||
navigation_node::TYPE_SETTING,
|
||||
null,
|
||||
null,
|
||||
new pix_icon('competency', '', 'tool_lp'));
|
||||
new pix_icon('i/competencies', ''));
|
||||
if (isset($settingsnode)) {
|
||||
$navigation->add_node($settingsnode);
|
||||
}
|
||||
@@ -144,7 +151,7 @@ function tool_lp_extend_navigation_category_settings($navigation, $coursecategor
|
||||
navigation_node::TYPE_SETTING,
|
||||
null,
|
||||
null,
|
||||
new pix_icon('competency', '', 'tool_lp'));
|
||||
new pix_icon('i/competencies', ''));
|
||||
if (isset($settingsnode)) {
|
||||
$navigation->add_node($settingsnode);
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 994 B After Width: | Height: | Size: 245 B |
@@ -1,76 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="32"
|
||||
height="32"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="New document 1" preserveAspectRatio="xMinYMid meet">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6"
|
||||
inkscape:cx="32.609372"
|
||||
inkscape:cy="35.818362"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
width="33px"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="981"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="72"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1020.3622)">
|
||||
<g
|
||||
id="g3050"
|
||||
transform="matrix(0.92121849,0,0,0.92121849,-35.507391,91.120599)">
|
||||
<path
|
||||
style="fill:#fafafa"
|
||||
d="m 38.892857,1026.2908 0,-17 17,0 17,0 0,17 0,17 -17,0 -17,0 0,-17 z"
|
||||
id="path3056"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#989898"
|
||||
d="m 40.954049,1037.3439 c -3.465234,-3.0054 -1.687876,-4.0531 6.875853,-4.0531 6.919599,0 7.985143,0.2502 9.263277,2.1752 0.947236,1.4266 2.021632,1.9536 3.121972,1.5314 1.37559,-0.5279 1.677706,-2.0754 1.677706,-8.5934 0,-4.3723 0.273145,-8.6614 0.606989,-9.5314 0.509481,-1.3277 -0.303204,-1.5818 -5.059017,-1.5818 -3.522693,0 -5.432219,-0.3783 -5.047972,-1 1.028329,-1.6639 14.737848,-1.2221 17.277494,0.5567 3.593622,2.5171 2.823056,4.4433 -1.777494,4.4433 l -4,0 0,7.345 c 0,9.8743 -0.892111,10.655 -12.175731,10.655 -7.005509,0 -8.916904,-0.3458 -10.763077,-1.9469 z m 3.938808,-10.6589 c 0,-2.9538 0.324365,-3.3942 2.5,-3.3942 2.153991,0 2.499902,0.4498 2.499294,3.25 -6e-4,2.7611 -0.376693,3.2717 -2.5,3.3942 -2.203157,0.1271 -2.499294,-0.2579 -2.499294,-3.25 z m 7.428571,-1.3942 c 0,-1.6931 0.666667,-2 4.344732,-2 5.000845,0 4.927087,1.9191 -0.08232,2.1419 -3.011559,0.1339 -2.966069,0.1822 0.809017,0.8581 l 4,0.7162 -4.535714,0.1419 c -3.91635,0.1225 -4.535715,-0.1312 -4.535715,-1.8581 z m -6.761904,-3.6667 c -1.167788,-1.1678 -0.718106,-4.0457 0.833333,-5.3333 1.846181,-1.5322 4.5,0.2461 4.5,3.0155 0,2.2918 -3.735778,3.9154 -5.333333,2.3178 z m 6.833333,-0.3333 c 0.339919,-0.55 2.139919,-1 4,-1 1.860081,0 3.660081,0.45 4,1 0.377944,0.6115 -1.175955,1 -4,1 -2.824045,0 -4.377944,-0.3885 -4,-1 z"
|
||||
id="path3054"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#5e5e5e"
|
||||
d="m 40.892857,1036.2908 c -1.027328,-1.9196 -0.76049,-2 6.636076,-2 5.863885,0 8.157677,0.4084 9.593719,1.708 1.429812,1.2939 2.297278,1.4506 3.578741,0.6463 1.318167,-0.8273 1.801811,-3.0713 2.191464,-10.1678 l 0.5,-9.1062 -5.5,-0.351 c -4.40263,-0.2811 -3.893849,-0.3889 2.55,-0.5402 5.16365,-0.1213 8.480263,0.2412 9.25,1.0109 2.011735,2.0117 1.364232,2.8 -2.3,2.8 l -3.5,0 0,7.0657 c 0,10.7479 -0.204518,10.9343 -12,10.9343 -8.906177,0 -10.039956,-0.2061 -11,-2 z m 5.078947,-9.0357 c -0.06465,-2.8954 0.283272,-3.5357 1.921053,-3.5357 1.608782,0 2,0.6666 2,3.4081 0,2.8111 -0.245749,3.2042 -1.402754,2.244 -1.077175,-0.894 -1.523051,-0.8644 -1.921053,0.1276 -0.285064,0.7104 -0.553825,-0.2994 -0.597246,-2.244 z m 8.682712,-0.6716 c 1.243913,-0.2392 3.043913,-0.2301 4,0.02 0.956088,0.2503 -0.06166,0.446 -2.261659,0.4349 -2.2,-0.011 -2.982253,-0.2159 -1.738341,-0.4551 z m 0,-3 c 1.243913,-0.2392 3.043913,-0.2301 4,0.02 0.956088,0.2503 -0.06166,0.446 -2.261659,0.4349 -2.2,-0.011 -2.982253,-0.2159 -1.738341,-0.4551 z m 0,-2 c 1.243913,-0.2392 3.043913,-0.2301 4,0.02 0.956088,0.2503 -0.06166,0.446 -2.261659,0.4349 -2.2,-0.011 -2.982253,-0.2159 -1.738341,-0.4551 z m -9.205886,-1.2025 c -0.810187,-1.3109 0.447721,-4.0902 1.851216,-4.0902 1.828446,0 3.499178,2.081 2.907877,3.6219 -0.592966,1.5452 -3.892838,1.87 -4.759093,0.4683 z"
|
||||
id="path3052"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
|
||||
]>
|
||||
<svg version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
|
||||
x="0px" y="0px" width="16px" height="16px" viewBox="-0.042 -0.036 16 16"
|
||||
style="overflow:visible;enable-background:new -0.042 -0.036 16 16;" xml:space="preserve" preserveAspectRatio="xMinYMid meet">
|
||||
<defs>
|
||||
</defs>
|
||||
<path style="fill:#999999;" d="M15.821,15.071c0.248,0.491,0,0.893-0.55,0.893H0.646c-0.55,0-0.797-0.401-0.55-0.893l1.566-3.107
|
||||
h12.592L15.821,15.071z M4.184,6.964l-2.017,4H13.75l-2.017-4H4.184z M11.23,5.964L8.409,0.369c-0.248-0.491-0.653-0.491-0.9,0
|
||||
L4.688,5.964H11.23z"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 934 B |
@@ -118,6 +118,9 @@
|
||||
.path-admin-tool-lp [data-region="competencylinktree"] select {
|
||||
width: 100%;
|
||||
}
|
||||
.path-admin-tool-lp [data-region] .generaltable.fullwidth {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.path-admin-tool-lp .competency-rule-points {
|
||||
margin-top: 10px;
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<dt>{{#str}}rating, tool_lp{{/str}}</dt>
|
||||
<dd>{{gradename}}
|
||||
{{#cangrade}}
|
||||
<buttn class="btn" id="rate_{{uniqid}}">{{#str}}rate, tool_lp{{/str}}</button>
|
||||
<button class="btn" id="rate_{{uniqid}}">{{#str}}rate, tool_lp{{/str}}</button>
|
||||
{{/cangrade}}
|
||||
</dd>
|
||||
{{#js}}
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<dt>{{#str}}rating, tool_lp{{/str}}</dt>
|
||||
<dd>{{gradename}}
|
||||
{{#cangrade}}
|
||||
<buttn class="btn" id="rate_{{uniqid}}">{{#str}}rate, tool_lp{{/str}}</button>
|
||||
<button class="btn" id="rate_{{uniqid}}">{{#str}}rate, tool_lp{{/str}}</button>
|
||||
{{/cangrade}}
|
||||
</dd>
|
||||
{{/usercompetencycourse}}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
This files describes changes in /admin/tool/lp/* - plugins,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 3.1.1 ===
|
||||
|
||||
* The plugin icon 'competency.png/svg' will be removed in the future use i/competencies instead.
|
||||
@@ -50,7 +50,8 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
|
||||
$result = external::get_plugins_supporting_mobile();
|
||||
$result = external_api::clean_returnvalue(external::get_plugins_supporting_mobile_returns(), $result);
|
||||
$this->assertCount(0, $result['warnings']);
|
||||
$this->assertCount(0, $result['plugins']);
|
||||
$this->assertArrayHasKey('plugins', $result);
|
||||
$this->assertTrue(is_array($result['plugins']));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -140,6 +140,10 @@ class eventobservers {
|
||||
$subscriptions = subscription_manager::get_subscriptions_by_event($eventobj);
|
||||
$idstosend = array();
|
||||
foreach ($subscriptions as $subscription) {
|
||||
// Only proceed to fire events and notifications if the subscription is active.
|
||||
if (!subscription_manager::subscription_is_active($subscription)) {
|
||||
continue;
|
||||
}
|
||||
$starttime = $now - $subscription->timewindow;
|
||||
$starttime = ($starttime > $subscription->lastnotificationsent) ? $starttime : $subscription->lastnotificationsent;
|
||||
if ($subscription->courseid == 0) {
|
||||
|
||||
@@ -55,17 +55,26 @@ class subscription {
|
||||
* Magic get method.
|
||||
*
|
||||
* @param string $prop property to get.
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public function __get($prop) {
|
||||
if (property_exists($this->subscription, $prop)) {
|
||||
if (isset($this->subscription->$prop)) {
|
||||
return $this->subscription->$prop;
|
||||
}
|
||||
throw new \coding_exception('Property "' . $prop . '" doesn\'t exist');
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic isset method.
|
||||
*
|
||||
* @param string $prop the property to get.
|
||||
* @return bool true if the property is set, false otherwise.
|
||||
*/
|
||||
public function __isset($prop) {
|
||||
return property_exists($this->subscription, $prop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a human readable name for instances associated with this subscription.
|
||||
*
|
||||
|
||||
@@ -35,6 +35,10 @@ defined('MOODLE_INTERNAL') || die();
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class subscription_manager {
|
||||
|
||||
/** @const Period of time, in days, after which an inactive subscription will be removed completely.*/
|
||||
const INACTIVE_SUBSCRIPTION_LIFESPAN_IN_DAYS = 30;
|
||||
|
||||
/**
|
||||
* Subscribe a user to a given rule.
|
||||
*
|
||||
@@ -456,4 +460,78 @@ class subscription_manager {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates a group of subscriptions based on an input array of ids.
|
||||
*
|
||||
* @since 3.1.1
|
||||
* @param array $ids of subscription ids.
|
||||
* @return bool true if the operation was successful, false otherwise.
|
||||
*/
|
||||
public static function activate_subscriptions(array $ids) {
|
||||
global $DB;
|
||||
if (!empty($ids)) {
|
||||
list($sql, $params) = $DB->get_in_or_equal($ids);
|
||||
$success = $DB->set_field_select('tool_monitor_subscriptions', 'inactivedate', '0', 'id ' . $sql, $params);
|
||||
return $success;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates a group of subscriptions based on an input array of ids.
|
||||
*
|
||||
* @since 3.1.1
|
||||
* @param array $ids of subscription ids.
|
||||
* @return bool true if the operation was successful, false otherwise.
|
||||
*/
|
||||
public static function deactivate_subscriptions(array $ids) {
|
||||
global $DB;
|
||||
if (!empty($ids)) {
|
||||
$inactivedate = time();
|
||||
list($sql, $params) = $DB->get_in_or_equal($ids);
|
||||
$success = $DB->set_field_select('tool_monitor_subscriptions', 'inactivedate', $inactivedate, 'id ' . $sql,
|
||||
$params);
|
||||
return $success;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes subscriptions which have been inactive for a period of time.
|
||||
*
|
||||
* @since 3.1.1
|
||||
* @param int $userid if provided, only this user's stale subscriptions will be deleted.
|
||||
* @return bool true if the operation was successful, false otherwise.
|
||||
*/
|
||||
public static function delete_stale_subscriptions($userid = 0) {
|
||||
global $DB;
|
||||
// Get the expiry duration, in days.
|
||||
$cutofftime = strtotime("-" . self::INACTIVE_SUBSCRIPTION_LIFESPAN_IN_DAYS . " days", time());
|
||||
|
||||
if (!empty($userid)) {
|
||||
// Remove any stale subscriptions for the desired user only.
|
||||
$success = $DB->delete_records_select('tool_monitor_subscriptions',
|
||||
'userid = ? AND inactivedate < ? AND inactivedate <> 0',
|
||||
array($userid, $cutofftime));
|
||||
|
||||
} else {
|
||||
// Remove all stale subscriptions.
|
||||
$success = $DB->delete_records_select('tool_monitor_subscriptions',
|
||||
'inactivedate < ? AND inactivedate <> 0',
|
||||
array($cutofftime));
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a subscription is active.
|
||||
*
|
||||
* @since 3.1.1
|
||||
* @param \tool_monitor\subscription $subscription instance.
|
||||
* @return bool true if the subscription is active, false otherwise.
|
||||
*/
|
||||
public static function subscription_is_active(subscription $subscription) {
|
||||
return empty($subscription->inactivedate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
<?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/>.
|
||||
namespace tool_monitor\task;
|
||||
use tool_monitor\subscription;
|
||||
use tool_monitor\subscription_manager;
|
||||
|
||||
/**
|
||||
* Simple task class responsible for activating, deactivating and removing subscriptions.
|
||||
*
|
||||
* Activation/deactivation is managed by looking at the same access rules used to determine whether a user can
|
||||
* subscribe to the rule in the first place.
|
||||
*
|
||||
* Removal occurs when a subscription has been inactive for a period of time exceeding the lifespan, as set by
|
||||
* subscription_manager::get_inactive_subscription_lifespan().
|
||||
*
|
||||
* I.e.
|
||||
* - Activation: If a user can subscribe currently, then an existing subscription should be made active.
|
||||
* - Deactivation: If a user cannot subscribe currently, then an existing subscription should be made inactive.
|
||||
* - Removal: If a user has a subscription that has been inactive for longer than the prescribed period, then
|
||||
* delete the subscription entirely.
|
||||
*
|
||||
* @since 3.1.1
|
||||
* @package tool_monitor
|
||||
* @copyright 2016 Jake Dallimore <jrhdallimore@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class check_subscriptions extends \core\task\scheduled_task {
|
||||
|
||||
/** @var array 1d static cache, indexed by userid, storing whether or not the user has been fully set up.*/
|
||||
protected $userssetupcache = array();
|
||||
|
||||
/** @var array 2d static cache, indexed by courseid and userid, storing whether a user can access the course with
|
||||
* the 'tool/monitor:subscribe' capability.
|
||||
*/
|
||||
protected $courseaccesscache = array();
|
||||
|
||||
/**
|
||||
* Get a descriptive name for this task.
|
||||
*
|
||||
* @since 3.1.1
|
||||
* @return string name of the task.
|
||||
*/
|
||||
public function get_name() {
|
||||
return get_string('taskchecksubscriptions', 'tool_monitor');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks all course-level rule subscriptions and activates/deactivates based on current course access.
|
||||
*
|
||||
* The ordering of checks within the task is important for optimisation purposes. The aim is to be able to make a decision
|
||||
* about whether to activate/deactivate each subscription without making unnecessary checks. The ordering roughly follows the
|
||||
* context model, starting with system and user checks and moving down to course and course-module only when necessary.
|
||||
*
|
||||
* For example, if the user is suspended, then any active subscription is made inactive right away. I.e. there is no need to
|
||||
* check site-level, course-level or course-module-level permissions. Likewise, if a subscriptions is site-level, there is no
|
||||
* need to check course-level and course-module-level permissions.
|
||||
*
|
||||
* The task performs the following checks, in this order:
|
||||
* 1. Check for a suspended user, breaking if suspended.
|
||||
* 2. Check for an incomplete (not set up) user, breaking if not fully set up.
|
||||
* 3. Check for the required capability in the relevant context, breaking if the capability is not found.
|
||||
* 4. Check whether the subscription is site-context, breaking if true.
|
||||
* 5. Check whether the user has course access, breaking only if the subscription is not also course-module-level.
|
||||
* 6. Check whether the user has course-module access.
|
||||
*
|
||||
* @since 3.1.1
|
||||
*/
|
||||
public function execute() {
|
||||
global $DB;
|
||||
|
||||
if (!get_config('tool_monitor', 'enablemonitor')) {
|
||||
return; // The tool is disabled. Nothing to do.
|
||||
}
|
||||
|
||||
$toactivate = array(); // Store the ids of subscriptions to be activated upon completion.
|
||||
$todeactivate = array(); // Store the ids of subscriptions to be deactivated upon completion.
|
||||
|
||||
// Resultset rows are ordered by userid and courseid to work nicely with get_fast_modinfo() caching.
|
||||
$sql = "SELECT u.id AS userid, u.firstname AS userfirstname, u.lastname AS userlastname, u.suspended AS usersuspended,
|
||||
u.email AS useremail, c.visible as coursevisible, c.cacherev as coursecacherev, s.courseid AS subcourseid,
|
||||
s.userid AS subuserid, s.cmid AS subcmid, s.inactivedate AS subinactivedate, s.id AS subid
|
||||
FROM {user} u
|
||||
JOIN {tool_monitor_subscriptions} s ON (s.userid = u.id)
|
||||
LEFT JOIN {course} c ON (c.id = s.courseid)
|
||||
WHERE u.id = s.userid
|
||||
ORDER BY s.userid, s.courseid";
|
||||
$rs = $DB->get_recordset_sql($sql);
|
||||
|
||||
foreach ($rs as $row) {
|
||||
// Create skeleton records from the result. This should be enough to use in subsequent access calls and avoids DB hits.
|
||||
$sub = $this->get_subscription_from_rowdata($row);
|
||||
$sub = new subscription($sub);
|
||||
if (!isset($user) || $user->id != $sub->userid) {
|
||||
$user= $this->get_user_from_rowdata($row);
|
||||
}
|
||||
if ((!isset($course) || $course->id != $sub->courseid) && !empty($sub->courseid)) {
|
||||
$course = $this->get_course_from_rowdata($row);
|
||||
}
|
||||
|
||||
// The user is suspended at site level, so deactivate any active subscriptions.
|
||||
if ($user->suspended) {
|
||||
if (subscription_manager::subscription_is_active($sub)) {
|
||||
$todeactivate[] = $sub->id;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is the user fully set up? As per require_login on the subscriptions page.
|
||||
if (!$this->is_user_setup($user)) {
|
||||
if (subscription_manager::subscription_is_active($sub)) {
|
||||
$todeactivate[] = $sub->id;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine the context, based on the subscription course id.
|
||||
$sitelevelsubscription = false;
|
||||
if (empty($sub->courseid)) {
|
||||
$context = \context_system::instance();
|
||||
$sitelevelsubscription = true;
|
||||
} else {
|
||||
$context = \context_course::instance($sub->courseid);
|
||||
}
|
||||
|
||||
// Check capability in the context.
|
||||
if (!has_capability('tool/monitor:subscribe', $context, $user)) {
|
||||
if (subscription_manager::subscription_is_active($sub)) {
|
||||
$todeactivate[] = $sub->id;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the subscription is site-level, then we've run all the checks required to make an access decision.
|
||||
if ($sitelevelsubscription) {
|
||||
if (!subscription_manager::subscription_is_active($sub)) {
|
||||
$toactivate[] = $sub->id;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check course access.
|
||||
if (!$this->user_can_access_course($user, $course, 'tool/monitor:subscribe')) {
|
||||
if (subscription_manager::subscription_is_active($sub)) {
|
||||
$todeactivate[] = $sub->id;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the subscription has no course module relationship.
|
||||
if (empty($sub->cmid)) {
|
||||
if (!subscription_manager::subscription_is_active($sub)) {
|
||||
$toactivate[] = $sub->id;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, check the course module info. We use the same checks as on the subscription page.
|
||||
$modinfo = get_fast_modinfo($course, $sub->userid);
|
||||
$cm = $modinfo->get_cm($sub->cmid);
|
||||
if (!$cm || !$cm->uservisible || !$cm->available) {
|
||||
if (subscription_manager::subscription_is_active($sub)) {
|
||||
$todeactivate[] = $sub->id;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// The course module is available and visible, so make a decision.
|
||||
if (!subscription_manager::subscription_is_active($sub)) {
|
||||
$toactivate[] = $sub->id;
|
||||
}
|
||||
}
|
||||
$rs->close();
|
||||
|
||||
// Activate/deactivate/delete relevant subscriptions.
|
||||
subscription_manager::activate_subscriptions($toactivate);
|
||||
subscription_manager::deactivate_subscriptions($todeactivate);
|
||||
subscription_manager::delete_stale_subscriptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a user is fully set up, using cached results where possible.
|
||||
*
|
||||
* @since 3.1.1
|
||||
* @param \stdClass $user the user record.
|
||||
* @return bool true if the user is fully set up, false otherwise.
|
||||
*/
|
||||
protected function is_user_setup($user) {
|
||||
if (!isset($this->userssetupcache[$user->id])) {
|
||||
$this->userssetupcache[$user->id] = !user_not_fully_set_up($user);
|
||||
}
|
||||
return $this->userssetupcache[$user->id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines a user's access to a course with a given capability, using cached results where possible.
|
||||
*
|
||||
* @since 3.1.1
|
||||
* @param \stdClass $user the user record.
|
||||
* @param \stdClass $course the course record.
|
||||
* @param string $capability the capability to check.
|
||||
* @return bool true if the user can access the course with the specified capability, false otherwise.
|
||||
*/
|
||||
protected function user_can_access_course($user, $course, $capability) {
|
||||
if (!isset($this->courseaccesscache[$course->id][$user->id][$capability])) {
|
||||
$this->courseaccesscache[$course->id][$user->id][$capability] = can_access_course($course, $user, $capability, true);
|
||||
}
|
||||
return $this->courseaccesscache[$course->id][$user->id][$capability];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a partial subscription record, created from properties of the supplied recordset row object.
|
||||
* Intended to return a minimal record for specific use within this class and in subsequent access control calls only.
|
||||
*
|
||||
* @since 3.1.1
|
||||
* @param \stdClass $rowdata the row object.
|
||||
* @return \stdClass a partial subscription record.
|
||||
*/
|
||||
protected function get_subscription_from_rowdata($rowdata) {
|
||||
$sub = new \stdClass();
|
||||
$sub->id = $rowdata->subid;
|
||||
$sub->userid = $rowdata->subuserid;
|
||||
$sub->courseid = $rowdata->subcourseid;
|
||||
$sub->cmid = $rowdata->subcmid;
|
||||
$sub->inactivedate = $rowdata->subinactivedate;
|
||||
return $sub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a partial course record, created from properties of the supplied recordset row object.
|
||||
* Intended to return a minimal record for specific use within this class and in subsequent access control calls only.
|
||||
*
|
||||
* @since 3.1.1
|
||||
* @param \stdClass $rowdata the row object.
|
||||
* @return \stdClass a partial course record.
|
||||
*/
|
||||
protected function get_course_from_rowdata($rowdata) {
|
||||
$course = new \stdClass();
|
||||
$course->id = $rowdata->subcourseid;
|
||||
$course->visible = $rowdata->coursevisible;
|
||||
$course->cacherev = $rowdata->coursecacherev;
|
||||
return $course;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a partial user record, created from properties of the supplied recordset row object.
|
||||
* Intended to return a minimal record for specific use within this class and in subsequent access control calls only.
|
||||
*
|
||||
* @since 3.1.1
|
||||
* @param \stdClass $rowdata the row object.
|
||||
* @return \stdClass a partial user record.
|
||||
*/
|
||||
protected function get_user_from_rowdata($rowdata) {
|
||||
$user = new \stdClass();
|
||||
$user->id = $rowdata->userid;
|
||||
$user->firstname = $rowdata->userfirstname;
|
||||
$user->lastname = $rowdata->userlastname;
|
||||
$user->email = $rowdata->useremail;
|
||||
$user->suspended = $rowdata->usersuspended;
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@
|
||||
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="User id of the subscriber"/>
|
||||
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Timestamp of when this subscription was created"/>
|
||||
<FIELD NAME="lastnotificationsent" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Timestamp of the time when a notification was last sent for this subscription."/>
|
||||
<FIELD NAME="inactivedate" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
|
||||
@@ -32,5 +32,14 @@ $tasks = array(
|
||||
'day' => '*',
|
||||
'dayofweek' => '*',
|
||||
'month' => '*'
|
||||
),
|
||||
array(
|
||||
'classname' => 'tool_monitor\task\check_subscriptions',
|
||||
'blocking' => 0,
|
||||
'minute' => 'R',
|
||||
'hour' => 'R',
|
||||
'day' => '*',
|
||||
'dayofweek' => '*',
|
||||
'month' => '*'
|
||||
)
|
||||
);
|
||||
|
||||
@@ -59,5 +59,23 @@ function xmldb_tool_monitor_upgrade($oldversion) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2016052305) {
|
||||
|
||||
// Define field inactivedate to be added to tool_monitor_subscriptions.
|
||||
$table = new xmldb_table('tool_monitor_subscriptions');
|
||||
$field = new xmldb_field('inactivedate', XMLDB_TYPE_INTEGER, '10', null, true, null, 0, 'lastnotificationsent');
|
||||
|
||||
// Conditionally launch add field inactivedate.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Monitor savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2016052305, 'tool', 'monitor');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -99,4 +99,5 @@ $string['subhelp'] = 'Subscription details';
|
||||
$string['subhelp_help'] = 'This subscription listens for when the event \'{$a->eventname}\' has been triggered in \'{$a->moduleinstance}\' {$a->frequency} time(s) in {$a->minutes} minute(s).';
|
||||
$string['subscribeto'] = 'Subscribe to rule "{$a}"';
|
||||
$string['taskcleanevents'] = 'Removes any unnecessary event monitor events';
|
||||
$string['taskchecksubscriptions'] = 'Activate/deactivate invalid rule subscriptions';
|
||||
$string['unsubscribe'] = 'Unsubscribe';
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?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/>.
|
||||
defined('MOODLE_INTERNAL') || exit();
|
||||
|
||||
/**
|
||||
* Unit tests for the subscription class.
|
||||
* @since 3.1.1
|
||||
*
|
||||
* @package tool_monitor
|
||||
* @category test
|
||||
* @copyright 2016 Jake Dallimore <jrhdallimore@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_monitor_subscription_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* @var \tool_monitor\subscription $subscription object.
|
||||
*/
|
||||
private $subscription;
|
||||
|
||||
/**
|
||||
* Test set up.
|
||||
*/
|
||||
public function setUp() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Create the mock subscription.
|
||||
$sub = new stdClass();
|
||||
$sub->id = 100;
|
||||
$sub->name = 'My test rule';
|
||||
$sub->courseid = 20;
|
||||
$this->subscription = $this->getMock('\tool_monitor\subscription',null, array($sub));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the magic __isset method.
|
||||
*/
|
||||
public function test_magic_isset() {
|
||||
$this->assertEquals(true, isset($this->subscription->name));
|
||||
$this->assertEquals(true, isset($this->subscription->courseid));
|
||||
$this->assertEquals(false, isset($this->subscription->ruleid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the magic __get method.
|
||||
*/
|
||||
public function test_magic_get() {
|
||||
$this->assertEquals(20, $this->subscription->courseid);
|
||||
$this->setExpectedException('coding_exception');
|
||||
$this->subscription->ruleid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
<?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/>.
|
||||
defined('MOODLE_INTERNAL') || exit();
|
||||
|
||||
/**
|
||||
* Unit tests for the tool_monitor clean events task.
|
||||
* @since 3.1.1
|
||||
*
|
||||
* @package tool_monitor
|
||||
* @category test
|
||||
* @copyright 2016 Jake Dallimore <jrhdallimore@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_monitor_task_check_subscriptions_testcase extends advanced_testcase {
|
||||
|
||||
private $course;
|
||||
private $user;
|
||||
private $rule;
|
||||
private $subscription;
|
||||
private $teacherrole;
|
||||
private $studentrole;
|
||||
|
||||
/**
|
||||
* Test set up.
|
||||
*/
|
||||
public function setUp() {
|
||||
global $DB;
|
||||
set_config('enablemonitor', 1, 'tool_monitor');
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// All tests defined herein need a user, course, rule and subscription, so set these up.
|
||||
$this->user = $this->getDataGenerator()->create_user();
|
||||
$this->course = $this->getDataGenerator()->create_course();
|
||||
|
||||
$rule = new stdClass();
|
||||
$rule->userid = 2; // Rule created by admin.
|
||||
$rule->courseid = $this->course->id;
|
||||
$rule->plugin = 'mod_book';
|
||||
$rule->eventname = '\mod_book\event\course_module_viewed';
|
||||
$rule->timewindow = 500;
|
||||
$monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
|
||||
$this->rule = $monitorgenerator->create_rule($rule);
|
||||
|
||||
$sub = new stdClass();
|
||||
$sub->courseid = $this->course->id;
|
||||
$sub->userid = $this->user->id;
|
||||
$sub->ruleid = $this->rule->id;
|
||||
$this->subscription = $monitorgenerator->create_subscription($sub);
|
||||
|
||||
// Also set up a student and a teacher role for use in some tests.
|
||||
$this->teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
|
||||
$this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the subscription object from the DB.
|
||||
*
|
||||
* @return void.
|
||||
*/
|
||||
private function reload_subscription() {
|
||||
global $DB;
|
||||
$sub = $DB->get_record('tool_monitor_subscriptions', array('id' => $this->subscription->id));
|
||||
$this->subscription = new \tool_monitor\subscription($sub);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to confirm the task is named correctly.
|
||||
*/
|
||||
public function test_task_name() {
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$this->assertEquals(get_string('taskchecksubscriptions', 'tool_monitor'), $task->get_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to confirm that site level subscriptions are activated and deactivated according to system capabilities.
|
||||
*/
|
||||
public function test_site_level_subscription() {
|
||||
// Create a site level subscription.
|
||||
$monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
|
||||
$sub = new stdClass();
|
||||
$sub->userid = $this->user->id;
|
||||
$sub->ruleid = $this->rule->id;
|
||||
$this->subscription = $monitorgenerator->create_subscription($sub);
|
||||
|
||||
// Run the task.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// The subscription should be inactive as the user doesn't have the capability. Pass in the id only to refetch the data.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
|
||||
// Now, assign the user as a teacher role at system context.
|
||||
$this->getDataGenerator()->role_assign($this->teacherrole->id, $this->user->id, context_system::instance());
|
||||
|
||||
// Run the task.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// The subscription should be active now. Pass in the id only to refetch the data.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to confirm that if the module is disabled, no changes are made to active subscriptions.
|
||||
*/
|
||||
public function test_module_disabled() {
|
||||
set_config('enablemonitor', 0, 'tool_monitor');
|
||||
|
||||
// Subscription should be active to start with.
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
|
||||
// Run the task. Note, we never enrolled the user.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// The subscription should still be active. Pass in the id only to refetch the data.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to confirm an active, valid subscription stays active once the scheduled task is run.
|
||||
*/
|
||||
public function test_active_unaffected() {
|
||||
// Enrol the user as a teacher. This role should have the required capability.
|
||||
$this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
|
||||
|
||||
// Subscription should be active to start with.
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
|
||||
// Run the task.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// The subscription should still be active. Pass in the id only to refetch the data.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to confirm that a subscription for a user without an enrolment to the course is made inactive.
|
||||
*/
|
||||
public function test_course_enrolment() {
|
||||
// Subscription should be active until deactivated by the scheduled task. Remember, by default the test setup
|
||||
// doesn't enrol the user, so the first run of the task should deactivate it.
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
|
||||
// Run the task.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// The subscription should NOT be active. Pass in the id only to refetch the data.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
|
||||
// Enrol the user.
|
||||
$this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
|
||||
|
||||
// Run the task.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// Subscription should now be active again.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to confirm that subscriptions for enrolled users without the required capability are made inactive.
|
||||
*/
|
||||
public function test_enrolled_user_with_no_capability() {
|
||||
// Enrol the user. By default, students won't have the required capability.
|
||||
$this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->studentrole->id);
|
||||
|
||||
// The subscription should be active to start with. Pass in the id only to refetch the data.
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
|
||||
// Run the task.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// The subscription should NOT be active. Pass in the id only to refetch the data.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to confirm that subscriptions for users who fail can_access_course(), are deactivated.
|
||||
*/
|
||||
public function test_can_access_course() {
|
||||
// Enrol the user as a teacher. This role should have the required capability.
|
||||
$this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
|
||||
|
||||
// Strip the ability to see hidden courses, so we'll fail the check_subscriptions->user_can_access_course call.
|
||||
$context = \context_course::instance($this->course->id);
|
||||
assign_capability('moodle/course:viewhiddencourses', CAP_PROHIBIT, $this->teacherrole->id, $context);
|
||||
|
||||
// Subscription should be active to start with.
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
|
||||
// Hide the course.
|
||||
course_change_visibility($this->course->id, false);
|
||||
|
||||
// Run the task.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// The subscription should be inactive. Pass in the id only to refetch the data.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to confirm that subscriptions for enrolled users who don't have CM access, are deactivated.
|
||||
*/
|
||||
public function test_cm_access() {
|
||||
// Enrol the user as a student but grant to ability to subscribe. Students cannot view hidden activities.
|
||||
$context = \context_course::instance($this->course->id);
|
||||
assign_capability('tool/monitor:subscribe', CAP_ALLOW, $this->studentrole->id, $context);
|
||||
$this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->studentrole->id);
|
||||
|
||||
// Generate a course module.
|
||||
$book = $this->getDataGenerator()->create_module('book', array('course' => $this->course->id));
|
||||
|
||||
// And add a subscription to it.
|
||||
$sub = new stdClass();
|
||||
$sub->courseid = $this->course->id;
|
||||
$sub->userid = $this->user->id;
|
||||
$sub->ruleid = $this->rule->id;
|
||||
$sub->cmid = $book->cmid;
|
||||
$monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
|
||||
$this->subscription = $monitorgenerator->create_subscription($sub);
|
||||
|
||||
// The subscription should be active to start with. Pass in the id only to refetch the data.
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
|
||||
// Run the task.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// The subscription should still be active. Pass in the id only to refetch the data.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
|
||||
// Make the course module invisible, which should in turn make the subscription inactive.
|
||||
set_coursemodule_visible($book->cmid, false);
|
||||
|
||||
// Run the task.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// The subscription should NOT be active. Pass in the id only to refetch the data.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
|
||||
// Make the course module visible again.
|
||||
set_coursemodule_visible($book->cmid, true);
|
||||
|
||||
// Run the task.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// The subscription should be active. Pass in the id only to refetch the data.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to confirm that long term inactive subscriptions are removed entirely.
|
||||
*/
|
||||
public function test_stale_subscription_removal() {
|
||||
global $DB;
|
||||
// Manually set the inactivedate to 1 day older than the limit allowed.
|
||||
$daysold = 1 + \tool_monitor\subscription_manager::INACTIVE_SUBSCRIPTION_LIFESPAN_IN_DAYS;
|
||||
|
||||
$inactivedate = strtotime("-$daysold days", time());
|
||||
$DB->set_field('tool_monitor_subscriptions', 'inactivedate', $inactivedate, array('id' => $this->subscription->id));
|
||||
|
||||
// Subscription should be inactive to start with.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
|
||||
// Run the task.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// Subscription should now not exist at all.
|
||||
$this->assertEquals(false, $DB->record_exists('tool_monitor_subscriptions', array('id' => $this->subscription->id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to confirm that subscriptions for a partially set up user are deactivated.
|
||||
*/
|
||||
public function test_user_not_fully_set_up() {
|
||||
global $DB;
|
||||
|
||||
// Enrol the user as a teacher.
|
||||
$this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
|
||||
|
||||
// The subscription should be active to start.
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
|
||||
// Unset the user's email address, so we fail the check_subscriptions->is_user_setup() call.
|
||||
$DB->set_field('user', 'email', '', array('id' => $this->user->id));
|
||||
|
||||
// Run the task.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// The subscription should now be inactive.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to confirm that a suspended user's subscriptions are deactivated properly.
|
||||
*/
|
||||
public function test_suspended_user() {
|
||||
global $DB;
|
||||
|
||||
// Enrol the user as a teacher. This role should have the required capability.
|
||||
$this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
|
||||
|
||||
// Subscription should be active to start with.
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
|
||||
// Suspend the user.
|
||||
$DB->set_field('user', 'suspended', '1', array('id' => $this->user->id));
|
||||
|
||||
// Run the task.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// The subscription should now be inactive.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
|
||||
// Unsuspend the user.
|
||||
$DB->set_field('user', 'suspended', '0', array('id' => $this->user->id));
|
||||
|
||||
// Run the task.
|
||||
$task = new \tool_monitor\task\check_subscriptions();
|
||||
$task->execute();
|
||||
|
||||
// The subscription should now be active again.
|
||||
$this->reload_subscription();
|
||||
$this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$plugin->version = 2016052300; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2016052305; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2016051900; // Requires this Moodle version.
|
||||
$plugin->component = 'tool_monitor'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -121,7 +121,7 @@ echo $OUTPUT->box_end();
|
||||
|
||||
/// Rebuild course cache which might be incorrect now
|
||||
echo $OUTPUT->notification('Rebuilding course cache...', 'notifysuccess');
|
||||
rebuild_course_cache();
|
||||
rebuild_course_cache(0, true);
|
||||
echo $OUTPUT->notification('...finished', 'notifysuccess');
|
||||
|
||||
echo $OUTPUT->continue_button(new moodle_url('/admin/'));
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/ajax","core/log","core/notification","core/templates","core/config","core/str"],function(a,b,c,d,e,f,g){var h=function(a,b){var c="@template "+b,d=0,e=[];if(e=a.match(/{{!([\s\S]*?)}}/g),null!==e)for(d=0;d<e.length;d++){var f=e[d],g=f.indexOf(c);if(-1!==g){var h=g+c.length+1;return f=f.substr(h,f.length-2-h)}}return!1},i=function(b,f,i){g.get_string("templateselected","tool_templatelibrary",b).done(function(b){a('[data-region="displaytemplateheader"]').text(b)}).fail(d.exception);var j=h(f,b);j===!1&&(j=h(i,b)),j&&(f=j),a('[data-region="displaytemplatesource"]').text(f);var k=f.match(/Example context \(json\):([\s\S]*)/),l=!1;if(k){var m=k[1].trim();try{l=a.parseJSON(m)}catch(n){c.debug("Could not parse json example context for template."),c.debug(n)}}l?e.render(b,l).done(function(b,c){e.replaceNodeContents(a('[data-region="displaytemplateexample"]'),b,c)}).fail(d.exception):g.get_string("templatehasnoexample","tool_templatelibrary").done(function(b){a('[data-region="displaytemplateexample"]').text(b)}).fail(d.exception)},j=function(c){var e=c.split("/"),g=e.shift(),h=e.shift(),j=b.call([{methodname:"core_output_load_template",args:{component:g,template:h,themename:f.theme}},{methodname:"tool_templatelibrary_load_canonical_template",args:{component:g,template:h}}],!0,!1);a.when.apply(a,j).done(function(a,b){i(c,a,b)}).fail(d.exception)};return a('[data-region="list-templates"]').on("click","[data-templatename]",function(){var b=a(this).data("templatename");j(b)}),{}});
|
||||
define(["jquery","core/ajax","core/log","core/notification","core/templates","core/config","core/str"],function(a,b,c,d,e,f,g){var h=function(a,b){var c="@template "+b,d=0,e=[];if(e=a.match(/{{!([\s\S]*?)}}/g),null!==e)for(d=0;d<e.length;d++){var f=e[d],g=f.indexOf(c);if(-1!==g){var h=g+c.length+1;return f=f.substr(h,f.length-2-h)}}return!1},i=function(b,f,i){g.get_string("templateselected","tool_templatelibrary",b).done(function(b){a('[data-region="displaytemplateheader"]').text(b)}).fail(d.exception);var j=h(f,b);j===!1&&(j=h(i,b)),j&&(f=j),a('[data-region="displaytemplatesource"]').text(f);var k=f.match(/Example context \(json\):([\s\S]*)/),l=!1;if(k){var m=k[1].trim();try{l=a.parseJSON(m)}catch(n){c.debug("Could not parse json example context for template."),c.debug(n)}}l?e.render(b,l).done(function(b,c){e.replaceNodeContents(a('[data-region="displaytemplateexample"]'),b,c)}).fail(d.exception):g.get_string("templatehasnoexample","tool_templatelibrary").done(function(b){a('[data-region="displaytemplateexample"]').text(b)}).fail(d.exception)},j=function(c){var e=c.split("/"),g=e.shift(),h=e.shift(),j=b.call([{methodname:"core_output_load_template",args:{component:g,template:h,themename:f.theme}},{methodname:"tool_templatelibrary_load_canonical_template",args:{component:g,template:h}}],!0,!1);a.when.apply(a,j).done(function(a,b){i(c,a,b)}).fail(d.exception)};return a('[data-region="list-templates"]').on("click","[data-templatename]",function(b){var c=a(this).data("templatename");b.preventDefault(),j(c)}),{}});
|
||||
@@ -141,8 +141,9 @@ define(['jquery', 'core/ajax', 'core/log', 'core/notification', 'core/templates'
|
||||
};
|
||||
|
||||
// Add the event listeners.
|
||||
$('[data-region="list-templates"]').on('click', '[data-templatename]', function() {
|
||||
$('[data-region="list-templates"]').on('click', '[data-templatename]', function(e) {
|
||||
var templatename = $(this).data('templatename');
|
||||
e.preventDefault();
|
||||
loadTemplate(templatename);
|
||||
});
|
||||
|
||||
|
||||
@@ -52,5 +52,8 @@ function xmldb_auth_cas_upgrade($oldversion) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+26
-11
@@ -302,20 +302,27 @@ class auth_plugin_db extends auth_plugin_base {
|
||||
|
||||
// Find obsolete users.
|
||||
if (count($userlist)) {
|
||||
list($notin_sql, $params) = $DB->get_in_or_equal($userlist, SQL_PARAMS_NAMED, 'u', false);
|
||||
$params['authtype'] = $this->authtype;
|
||||
$sql = "SELECT u.*
|
||||
$remove_users = array();
|
||||
// All the drivers can cope with chunks of 10,000. See line 4491 of lib/dml/tests/dml_est.php
|
||||
$userlistchunks = array_chunk($userlist , 10000);
|
||||
foreach($userlistchunks as $userlistchunk) {
|
||||
list($notin_sql, $params) = $DB->get_in_or_equal($userlistchunk, SQL_PARAMS_NAMED, 'u', false);
|
||||
$params['authtype'] = $this->authtype;
|
||||
$sql = "SELECT u.id, u.username
|
||||
FROM {user} u
|
||||
WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid $suspendselect AND u.username $notin_sql";
|
||||
$params['mnethostid'] = $CFG->mnet_localhost_id;
|
||||
$remove_users = $remove_users + $DB->get_records_sql($sql, $params);
|
||||
}
|
||||
} else {
|
||||
$sql = "SELECT u.*
|
||||
$sql = "SELECT u.id, u.username
|
||||
FROM {user} u
|
||||
WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid $suspendselect";
|
||||
$params = array();
|
||||
$params['authtype'] = $this->authtype;
|
||||
$params['mnethostid'] = $CFG->mnet_localhost_id;
|
||||
$remove_users = $DB->get_records_sql($sql, $params);
|
||||
}
|
||||
$params['mnethostid'] = $CFG->mnet_localhost_id;
|
||||
$remove_users = $DB->get_records_sql($sql, $params);
|
||||
|
||||
if (!empty($remove_users)) {
|
||||
$trace->output(get_string('auth_dbuserstoremove','auth_db', count($remove_users)));
|
||||
@@ -358,12 +365,20 @@ class auth_plugin_db extends auth_plugin_base {
|
||||
|
||||
// Only go ahead if we actually have fields to update locally.
|
||||
if (!empty($updatekeys)) {
|
||||
list($in_sql, $params) = $DB->get_in_or_equal($userlist, SQL_PARAMS_NAMED, 'u', true);
|
||||
$params['authtype'] = $this->authtype;
|
||||
$sql = "SELECT u.id, u.username
|
||||
$update_users = array();
|
||||
// All the drivers can cope with chunks of 10,000. See line 4491 of lib/dml/tests/dml_est.php
|
||||
$userlistchunks = array_chunk($userlist , 10000);
|
||||
foreach($userlistchunks as $userlistchunk) {
|
||||
list($in_sql, $params) = $DB->get_in_or_equal($userlistchunk, SQL_PARAMS_NAMED, 'u', true);
|
||||
$params['authtype'] = $this->authtype;
|
||||
$params['mnethostid'] = $CFG->mnet_localhost_id;
|
||||
$sql = "SELECT u.id, u.username
|
||||
FROM {user} u
|
||||
WHERE u.auth=:authtype AND u.deleted=0 AND u.username {$in_sql}";
|
||||
if ($update_users = $DB->get_records_sql($sql, $params)) {
|
||||
WHERE u.auth = :authtype AND u.deleted = 0 AND u.mnethostid = :mnethostid AND u.username {$in_sql}";
|
||||
$update_users = $update_users + $DB->get_records_sql($sql, $params);
|
||||
}
|
||||
|
||||
if ($update_users) {
|
||||
$trace->output("User entries to update: ".count($update_users));
|
||||
|
||||
foreach ($update_users as $user) {
|
||||
|
||||
@@ -52,5 +52,8 @@ function xmldb_auth_ldap_upgrade($oldversion) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -40,5 +40,8 @@ function xmldb_auth_manual_upgrade($oldversion) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -40,5 +40,8 @@ function xmldb_auth_mnet_upgrade($oldversion) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -950,8 +950,11 @@ class backup_gradebook_structure_step extends backup_structure_step {
|
||||
$grade_setting = new backup_nested_element('grade_setting', 'id', array(
|
||||
'name', 'value'));
|
||||
|
||||
$gradebook_attributes = new backup_nested_element('attributes', null, array('calculations_freeze'));
|
||||
|
||||
// Build the tree
|
||||
$gradebook->add_child($gradebook_attributes);
|
||||
|
||||
$gradebook->add_child($grade_categories);
|
||||
$grade_categories->add_child($grade_category);
|
||||
|
||||
@@ -966,14 +969,15 @@ class backup_gradebook_structure_step extends backup_structure_step {
|
||||
$gradebook->add_child($grade_settings);
|
||||
$grade_settings->add_child($grade_setting);
|
||||
|
||||
// Define sources
|
||||
|
||||
// Add attribute with gradebook calculation freeze date if needed.
|
||||
$attributes = new stdClass();
|
||||
$gradebookcalculationfreeze = get_config('core', 'gradebook_calculations_freeze_' . $this->get_courseid());
|
||||
if ($gradebookcalculationfreeze) {
|
||||
$gradebook->add_attributes(array('calculations_freeze'));
|
||||
$gradebook->get_attribute('calculations_freeze')->set_value($gradebookcalculationfreeze);
|
||||
$attributes->calculations_freeze = $gradebookcalculationfreeze;
|
||||
}
|
||||
|
||||
// Define sources
|
||||
$gradebook_attributes->set_source_array([$attributes]);
|
||||
|
||||
//Include manual, category and the course grade item
|
||||
$grade_items_sql ="SELECT * FROM {grade_items}
|
||||
|
||||
@@ -181,7 +181,7 @@ abstract class restore_qtype_plugin extends restore_plugin {
|
||||
$info = new stdClass();
|
||||
$info->filequestionid = $oldquestionid;
|
||||
$info->dbquestionid = $newquestionid;
|
||||
$info->answer = $data->answertext;
|
||||
$info->answer = s($data->answertext);
|
||||
throw new restore_step_exception('error_question_answers_missing_in_db', $info);
|
||||
}
|
||||
$newitemid = $this->questionanswercache[$data->answertext];
|
||||
|
||||
@@ -121,6 +121,21 @@ class restore_gradebook_structure_step extends restore_structure_step {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Identify the backup we're dealing with.
|
||||
$backuprelease = floatval($this->get_task()->get_info()->backup_release); // The major version: 2.9, 3.0, ...
|
||||
$backupbuild = 0;
|
||||
preg_match('/(\d{8})/', $this->get_task()->get_info()->moodle_release, $matches);
|
||||
if (!empty($matches[1])) {
|
||||
$backupbuild = (int) $matches[1]; // The date of Moodle build at the time of the backup.
|
||||
}
|
||||
|
||||
// On older versions the freeze value has to be converted.
|
||||
// We do this from here as it is happening right before the file is read.
|
||||
// This only targets the backup files that can contain the legacy freeze.
|
||||
if ($backupbuild > 20150618 && ($backuprelease < 3.0 || $backupbuild < 20160527)) {
|
||||
$this->rewrite_step_backup_file_for_legacy_freeze($fullpath);
|
||||
}
|
||||
|
||||
// Arrived here, execute the step
|
||||
return true;
|
||||
}
|
||||
@@ -129,7 +144,7 @@ class restore_gradebook_structure_step extends restore_structure_step {
|
||||
$paths = array();
|
||||
$userinfo = $this->task->get_setting_value('users');
|
||||
|
||||
$paths[] = new restore_path_element('gradebook', '/gradebook');
|
||||
$paths[] = new restore_path_element('attributes', '/gradebook/attributes');
|
||||
$paths[] = new restore_path_element('grade_category', '/gradebook/grade_categories/grade_category');
|
||||
$paths[] = new restore_path_element('grade_item', '/gradebook/grade_items/grade_item');
|
||||
if ($userinfo) {
|
||||
@@ -141,7 +156,7 @@ class restore_gradebook_structure_step extends restore_structure_step {
|
||||
return $paths;
|
||||
}
|
||||
|
||||
protected function process_gradebook($data) {
|
||||
protected function process_attributes($data) {
|
||||
// For non-merge restore types:
|
||||
// Unset 'gradebook_calculations_freeze_' in the course and replace with the one from the backup.
|
||||
$target = $this->get_task()->get_target();
|
||||
@@ -581,6 +596,85 @@ class restore_gradebook_structure_step extends restore_structure_step {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite step definition to handle the legacy freeze attribute.
|
||||
*
|
||||
* In previous backups the calculations_freeze property was stored as an attribute of the
|
||||
* top level node <gradebook>. The backup API, however, do not process grandparent nodes.
|
||||
* It only processes definitive children, and their parent attributes.
|
||||
*
|
||||
* We had:
|
||||
*
|
||||
* <gradebook calculations_freeze="20160511">
|
||||
* <grade_categories>
|
||||
* <grade_category id="10">
|
||||
* <depth>1</depth>
|
||||
* ...
|
||||
* </grade_category>
|
||||
* </grade_categories>
|
||||
* ...
|
||||
* </gradebook>
|
||||
*
|
||||
* And this method will convert it to:
|
||||
*
|
||||
* <gradebook >
|
||||
* <attributes>
|
||||
* <calculations_freeze>20160511</calculations_freeze>
|
||||
* </attributes>
|
||||
* <grade_categories>
|
||||
* <grade_category id="10">
|
||||
* <depth>1</depth>
|
||||
* ...
|
||||
* </grade_category>
|
||||
* </grade_categories>
|
||||
* ...
|
||||
* </gradebook>
|
||||
*
|
||||
* Note that we cannot just load the XML file in memory as it could potentially be huge.
|
||||
* We can also completely ignore if the node <attributes> is already in the backup
|
||||
* file as it never existed before.
|
||||
*
|
||||
* @param string $filepath The absolute path to the XML file.
|
||||
* @return void
|
||||
*/
|
||||
protected function rewrite_step_backup_file_for_legacy_freeze($filepath) {
|
||||
$foundnode = false;
|
||||
$newfile = make_request_directory(true) . DIRECTORY_SEPARATOR . 'file.xml';
|
||||
$fr = fopen($filepath, 'r');
|
||||
$fw = fopen($newfile, 'w');
|
||||
if ($fr && $fw) {
|
||||
while (($line = fgets($fr, 4096)) !== false) {
|
||||
if (!$foundnode && strpos($line, '<gradebook ') === 0) {
|
||||
$foundnode = true;
|
||||
$matches = array();
|
||||
$pattern = '@calculations_freeze=.([0-9]+).@';
|
||||
if (preg_match($pattern, $line, $matches)) {
|
||||
$freeze = $matches[1];
|
||||
$line = preg_replace($pattern, '', $line);
|
||||
$line .= " <attributes>\n <calculations_freeze>$freeze</calculations_freeze>\n </attributes>\n";
|
||||
}
|
||||
}
|
||||
fputs($fw, $line);
|
||||
}
|
||||
if (!feof($fr)) {
|
||||
throw new restore_step_exception('Error while attempting to rewrite the gradebook step file.');
|
||||
}
|
||||
fclose($fr);
|
||||
fclose($fw);
|
||||
if (!rename($newfile, $filepath)) {
|
||||
throw new restore_step_exception('Error while attempting to rename the gradebook step file.');
|
||||
}
|
||||
} else {
|
||||
if ($fr) {
|
||||
fclose($fr);
|
||||
}
|
||||
if ($fw) {
|
||||
fclose($fw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<gradebook >
|
||||
<attributes>
|
||||
<calculations_freeze>20160511</calculations_freeze>
|
||||
</attributes>
|
||||
<grade_categories>
|
||||
<grade_category id="10">
|
||||
<depth>1</depth>
|
||||
</grade_category>
|
||||
</grade_categories>
|
||||
</gradebook>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<gradebook calculations_freeze="20160511">
|
||||
<grade_categories>
|
||||
<grade_category id="10">
|
||||
<depth>1</depth>
|
||||
</grade_category>
|
||||
</grade_categories>
|
||||
</gradebook>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<gradebook some_other_value="false" >
|
||||
<attributes>
|
||||
<calculations_freeze>20160511</calculations_freeze>
|
||||
</attributes>
|
||||
<grade_categories>
|
||||
<grade_category id="10">
|
||||
<depth>1</depth>
|
||||
</grade_category>
|
||||
</grade_categories>
|
||||
</gradebook>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<gradebook some_other_value="false" calculations_freeze="20160511">
|
||||
<grade_categories>
|
||||
<grade_category id="10">
|
||||
<depth>1</depth>
|
||||
</grade_category>
|
||||
</grade_categories>
|
||||
</gradebook>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<gradebook some_other_value="false" and_another_value="42">
|
||||
<attributes>
|
||||
<calculations_freeze>20160511</calculations_freeze>
|
||||
</attributes>
|
||||
<grade_categories>
|
||||
<grade_category id="10">
|
||||
<depth>1</depth>
|
||||
</grade_category>
|
||||
</grade_categories>
|
||||
</gradebook>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<gradebook some_other_value="false" calculations_freeze="20160511" and_another_value="42">
|
||||
<grade_categories>
|
||||
<grade_category id="10">
|
||||
<depth>1</depth>
|
||||
</grade_category>
|
||||
</grade_categories>
|
||||
</gradebook>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<gradebookplugin some_other_value="false" calculations_freeze="20160511" and_another_value="42">
|
||||
<grade_categories>
|
||||
<grade_category id="10">
|
||||
<depth>1</depth>
|
||||
</grade_category>
|
||||
</grade_categories>
|
||||
</gradebookplugin>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<gradebookplugin some_other_value="false" calculations_freeze="20160511" and_another_value="42">
|
||||
<grade_categories>
|
||||
<grade_category id="10">
|
||||
<depth>1</depth>
|
||||
</grade_category>
|
||||
</grade_categories>
|
||||
</gradebookplugin>
|
||||
@@ -0,0 +1,92 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Test for restore_stepslib.
|
||||
*
|
||||
* @package core_backup
|
||||
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
||||
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
|
||||
require_once($CFG->libdir . '/completionlib.php');
|
||||
|
||||
/**
|
||||
* Test for restore_stepslib.
|
||||
*
|
||||
* @package core_backup
|
||||
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_backup_restore_gradebook_structure_step_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Provide tests for rewrite_step_backup_file_for_legacy_freeze based upon fixtures.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rewrite_step_backup_file_for_legacy_freeze_provider() {
|
||||
$fixturesdir = realpath(__DIR__ . '/fixtures/rewrite_step_backup_file_for_legacy_freeze/');
|
||||
$tests = [];
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($fixturesdir),
|
||||
\RecursiveIteratorIterator::LEAVES_ONLY);
|
||||
|
||||
foreach ($iterator as $sourcefile) {
|
||||
$pattern = '/\.test$/';
|
||||
if (!preg_match($pattern, $sourcefile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$expectfile = preg_replace($pattern, '.expectation', $sourcefile);
|
||||
$test = array($sourcefile, $expectfile);
|
||||
$tests[basename($sourcefile)] = $test;
|
||||
}
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider rewrite_step_backup_file_for_legacy_freeze_provider
|
||||
* @param string $source The source file to test
|
||||
* @param string $expected The expected result of the transformation
|
||||
*/
|
||||
public function test_rewrite_step_backup_file_for_legacy_freeze($source, $expected) {
|
||||
$restore = $this->getMockBuilder('\restore_gradebook_structure_step')
|
||||
->setMethods(null)
|
||||
->disableOriginalConstructor()
|
||||
->getMock()
|
||||
;
|
||||
|
||||
// Copy the file somewhere as the rewrite_step_backup_file_for_legacy_freeze will write the file.
|
||||
$dir = make_request_directory(true);
|
||||
$filepath = $dir . DIRECTORY_SEPARATOR . 'file.xml';
|
||||
copy($source, $filepath);
|
||||
|
||||
$rc = new \ReflectionClass('\restore_gradebook_structure_step');
|
||||
$rcm = $rc->getMethod('rewrite_step_backup_file_for_legacy_freeze');
|
||||
$rcm->setAccessible(true);
|
||||
$rcm->invoke($restore, $filepath);
|
||||
|
||||
// Check the result.
|
||||
$this->assertFileEquals($expected, $filepath);
|
||||
}
|
||||
}
|
||||
@@ -82,5 +82,8 @@ function xmldb_block_badges_upgrade($oldversion, $block) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
@block @block_blog_recent
|
||||
Feature: Feature: Users can use the recent blog entries block to view recent blog entries.
|
||||
In order to enable the recent blog entries in a course
|
||||
As a teacher
|
||||
I can add recent blog entries block to a course
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email | idnumber |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com | T1 |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
|
||||
Scenario: Add the recent blogs block to a course when blogs are disabled
|
||||
Given I log in as "admin"
|
||||
And the following config values are set as admin:
|
||||
| enableblogs | 0 |
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
When I add the "Recent blog entries" block
|
||||
Then I should see "Blogging is disabled!" in the "Recent blog entries" "block"
|
||||
|
||||
Scenario: Add the recent blogs block to a course when there are not any blog posts
|
||||
Given I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
When I add the "Recent blog entries" block
|
||||
Then I should see "No recent entries" in the "Recent blog entries" "block"
|
||||
@@ -0,0 +1,117 @@
|
||||
@block @block_blog_menu @mod_assign @block_blog_recent
|
||||
Feature: Students can use the recent blog entries block to view recent entries on an activity page
|
||||
In order to enable the recent blog entries block an activity page
|
||||
As a teacher
|
||||
I can add the recent blog entries block to an activity page
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email | idnumber |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com | T1 |
|
||||
| student1 | Student | 1 | student1@example.com | S1 |
|
||||
| student2 | Student | 2 | student2@example.com | S2 |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add a "Assignment" to section "1" and I fill the form with:
|
||||
| Assignment name | Test assignment 1 |
|
||||
| Description | Offline text |
|
||||
| assignsubmission_file_enabled | 0 |
|
||||
And I follow "Test assignment 1"
|
||||
And I add the "Blog menu" block
|
||||
And I add the "Recent blog entries" block
|
||||
And I log out
|
||||
|
||||
Scenario: Students use the recent blog entries block to view blogs
|
||||
Given I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment 1"
|
||||
And I follow "Add an entry about this Assignment"
|
||||
When I set the following fields to these values:
|
||||
| Entry title | S1 First Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
Then I should see "S1 First Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
And I follow "Test assignment 1"
|
||||
And I should see "S1 First Blog"
|
||||
And I follow "S1 First Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
|
||||
Scenario: Students only see a few entries in the recent blog entries block
|
||||
Given I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment 1"
|
||||
And I follow "Add an entry about this Assignment"
|
||||
# Blog 1 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 First Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I wait "1" seconds
|
||||
And I follow "Test assignment 1"
|
||||
And I follow "Add an entry about this Assignment"
|
||||
# Blog 2 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 Second Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I wait "1" seconds
|
||||
And I should see "S1 Second Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
And I follow "Test assignment 1"
|
||||
And I follow "Add an entry about this Assignment"
|
||||
# Blog 3 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 Third Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I wait "1" seconds
|
||||
And I should see "S1 Third Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
And I follow "Test assignment 1"
|
||||
And I follow "Add an entry about this Assignment"
|
||||
# Blog 4 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 Fourth Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I wait "1" seconds
|
||||
And I should see "S1 Fourth Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
And I follow "Test assignment 1"
|
||||
And I follow "Add an entry about this Assignment"
|
||||
# Blog 5 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 Fifth Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I should see "S1 Fifth Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
When I follow "Test assignment 1"
|
||||
And I should not see "S1 First Blog"
|
||||
And I should see "S1 Second Blog"
|
||||
And I should see "S1 Third Blog"
|
||||
And I should see "S1 Fourth Blog"
|
||||
And I should see "S1 Fifth Blog"
|
||||
And I follow "S1 Fifth Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
Then I log out
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I follow "Test assignment 1"
|
||||
And I configure the "Recent blog entries" block
|
||||
And I set the following fields to these values:
|
||||
| id_config_numberofrecentblogentries | 2 |
|
||||
And I press "Save changes"
|
||||
And I should see "S1 Fourth Blog"
|
||||
And I should see "S1 Fifth Blog"
|
||||
@@ -0,0 +1,107 @@
|
||||
@block @block_blog_menu @block_blog_recent
|
||||
Feature: Students can use the recent blog entries block to view recent entries on a course page
|
||||
In order to enable the recent blog entries block a course page
|
||||
As a teacher
|
||||
I can add the recent blog entries block to a course page
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email | idnumber |
|
||||
| student1 | Student | 1 | student1@example.com | S1 |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com | T1 |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add the "Blog menu" block
|
||||
And I add the "Recent blog entries" block
|
||||
And I log out
|
||||
|
||||
Scenario: Students use the recent blog entries block to view blogs
|
||||
Given I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Add an entry about this course"
|
||||
When I set the following fields to these values:
|
||||
| Entry title | S1 First Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
Then I should see "S1 First Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
And I follow "C1"
|
||||
And I should see "S1 First Blog"
|
||||
And I follow "S1 First Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
|
||||
Scenario: Students only see a few entries in the recent blog entries block
|
||||
Given I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Add an entry about this course"
|
||||
# Blog 1 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 First Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I wait "1" seconds
|
||||
And I follow "C1"
|
||||
And I follow "Add an entry about this course"
|
||||
# Blog 2 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 Second Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I wait "1" seconds
|
||||
And I should see "S1 Second Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
And I follow "C1"
|
||||
And I follow "Add an entry about this course"
|
||||
# Blog 3 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 Third Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I wait "1" seconds
|
||||
And I should see "S1 Third Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
And I follow "C1"
|
||||
And I follow "Add an entry about this course"
|
||||
# Blog 4 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 Fourth Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I wait "1" seconds
|
||||
And I should see "S1 Fourth Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
And I follow "C1"
|
||||
And I follow "Add an entry about this course"
|
||||
# Blog 5 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 Fifth Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I should see "S1 Fifth Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
When I follow "C1"
|
||||
And I should not see "S1 First Blog"
|
||||
And I should see "S1 Second Blog"
|
||||
And I should see "S1 Third Blog"
|
||||
And I should see "S1 Fourth Blog"
|
||||
And I should see "S1 Fifth Blog"
|
||||
And I follow "S1 Fifth Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
Then I log out
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I configure the "Recent blog entries" block
|
||||
And I set the following fields to these values:
|
||||
| id_config_numberofrecentblogentries | 2 |
|
||||
And I press "Save changes"
|
||||
And I should see "S1 Fourth Blog"
|
||||
And I should see "S1 Fifth Blog"
|
||||
@@ -0,0 +1,96 @@
|
||||
@block @block_blog_recent
|
||||
Feature: Feature: Students can use the recent blog entries block to view recent entries on the frontpage
|
||||
In order to enable the recent blog entries block on the frontpage
|
||||
As an admin
|
||||
I can add the recent blog entries block to the frontpage
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email | idnumber |
|
||||
| student1 | Student | 1 | student1@example.com | S1 |
|
||||
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 "Recent blog entries" block
|
||||
And I log out
|
||||
|
||||
Scenario: Students use the recent blog entries block to view blogs
|
||||
Given I log in as "student1"
|
||||
And I am on site homepage
|
||||
And I navigate to "Site blogs" node in "Site pages"
|
||||
And I follow "Add a new entry"
|
||||
When I set the following fields to these values:
|
||||
| Entry title | S1 First Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
Then I should see "S1 First Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
And I am on site homepage
|
||||
And I should see "S1 First Blog"
|
||||
And I follow "S1 First Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
|
||||
Scenario: Students only see a few entries in the recent blog entries block
|
||||
Given I log in as "student1"
|
||||
And I am on site homepage
|
||||
And I navigate to "Site blogs" node in "Site pages"
|
||||
And I follow "Add a new entry"
|
||||
# Blog 1 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 First Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I wait "1" seconds
|
||||
And I follow "Add a new entry"
|
||||
# Blog 2 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 Second Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I wait "1" seconds
|
||||
And I should see "S1 Second Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
And I follow "Add a new entry"
|
||||
# Blog 3 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 Third Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I wait "1" seconds
|
||||
And I should see "S1 Third Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
And I follow "Add a new entry"
|
||||
# Blog 4 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 Fourth Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I wait "1" seconds
|
||||
And I should see "S1 Fourth Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
And I follow "Add a new entry"
|
||||
# Blog 5 of 5
|
||||
And I set the following fields to these values:
|
||||
| Entry title | S1 Fifth Blog |
|
||||
| Blog entry body | This is my awesome blog! |
|
||||
And I press "Save changes"
|
||||
And I should see "S1 Fifth Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
When I am on site homepage
|
||||
And I should not see "S1 First Blog"
|
||||
And I should see "S1 Second Blog"
|
||||
And I should see "S1 Third Blog"
|
||||
And I should see "S1 Fourth Blog"
|
||||
And I should see "S1 Fifth Blog"
|
||||
And I follow "S1 Fifth Blog"
|
||||
And I should see "This is my awesome blog!"
|
||||
Then I log out
|
||||
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 configure the "Recent blog entries" block
|
||||
And I set the following fields to these values:
|
||||
| id_config_numberofrecentblogentries | 2 |
|
||||
And I press "Save changes"
|
||||
And I should see "S1 Fourth Blog"
|
||||
And I should see "S1 Fifth Blog"
|
||||
@@ -82,5 +82,8 @@ function xmldb_block_calendar_month_upgrade($oldversion, $block) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -82,5 +82,8 @@ function xmldb_block_calendar_upcoming_upgrade($oldversion, $block) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
@block @block_comments
|
||||
Feature: Enable Block comments on the dashboard and view comments
|
||||
In order to enable the comments block on a the dashboard
|
||||
As a teacher
|
||||
I can add the comments block to my dashboard
|
||||
|
||||
Background:
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | Frist | teacher1@example.com |
|
||||
|
||||
Scenario: Add the comments block on the dashboard and add comments with Javascript disabled
|
||||
When I log in as "teacher1"
|
||||
And I press "Customise this page"
|
||||
And I add the "Comments" block
|
||||
And I follow "Show comments"
|
||||
And I add "I'm a comment from the teacher" comment to comments block
|
||||
Then I should see "I'm a comment from the teacher"
|
||||
|
||||
@javascript
|
||||
Scenario: Add the comments block on the dashboard and add comments with Javascript enabled
|
||||
When I log in as "teacher1"
|
||||
And I press "Customise this page"
|
||||
And I add the "Comments" block
|
||||
And I add "I'm a comment from the teacher" comment to comments block
|
||||
Then I should see "I'm a comment from the teacher"
|
||||
@@ -55,5 +55,8 @@ function xmldb_block_community_upgrade($oldversion) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -57,5 +57,8 @@ function xmldb_block_completionstatus_upgrade($oldversion, $block) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
@block @block_course_overview @mod_quiz
|
||||
Feature: View the quiz being due
|
||||
In order to know what quizzes are due
|
||||
As a student
|
||||
I can visit my dashboard
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| student2 | Student | 2 | student2@example.com |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
| Course 2 | C2 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C2 | student |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| teacher1 | C2 | editingteacher |
|
||||
And the following "activities" exist:
|
||||
| activity | course | idnumber | name | timeclose |
|
||||
| quiz | C1 | Q1A | Quiz 1A No deadline | 0 |
|
||||
| quiz | C1 | Q1B | Quiz 1B Past deadline | 1337 |
|
||||
| quiz | C1 | Q1C | Quiz 1C Future deadline | 9000000000 |
|
||||
| quiz | C1 | Q1D | Quiz 1D Future deadline | 9000000000 |
|
||||
| quiz | C1 | Q1E | Quiz 1E Future deadline | 9000000000 |
|
||||
| quiz | C2 | Q2A | Quiz 2A Future deadline | 9000000000 |
|
||||
And the following "question categories" exist:
|
||||
| contextlevel | reference | name |
|
||||
| Course | C1 | Test questions |
|
||||
And the following "questions" exist:
|
||||
| qtype | name | questiontext | questioncategory |
|
||||
| truefalse | First question | Answer the first question | Test questions |
|
||||
And quiz "Quiz 1A No deadline" contains the following questions:
|
||||
| question | page |
|
||||
| First question | 1 |
|
||||
And quiz "Quiz 1B Past deadline" contains the following questions:
|
||||
| question | page |
|
||||
| First question | 1 |
|
||||
And quiz "Quiz 1C Future deadline" contains the following questions:
|
||||
| question | page |
|
||||
| First question | 1 |
|
||||
And quiz "Quiz 1D Future deadline" contains the following questions:
|
||||
| question | page |
|
||||
| First question | 1 |
|
||||
And quiz "Quiz 1E Future deadline" contains the following questions:
|
||||
| question | page |
|
||||
| First question | 1 |
|
||||
And quiz "Quiz 2A Future deadline" contains the following questions:
|
||||
| question | page |
|
||||
| First question | 1 |
|
||||
|
||||
Scenario: View my quizzes that are due
|
||||
Given I log in as "student1"
|
||||
When I am on homepage
|
||||
Then I should see "You have quizzes that are due" in the "Course overview" "block"
|
||||
And I should see "Quiz 1C Future deadline" in the "Course overview" "block"
|
||||
And I should see "Quiz 1D Future deadline" in the "Course overview" "block"
|
||||
And I should see "Quiz 1E Future deadline" in the "Course overview" "block"
|
||||
And I should not see "Quiz 1A No deadline" in the "Course overview" "block"
|
||||
And I should not see "Quiz 1B Past deadline" in the "Course overview" "block"
|
||||
And I should not see "Quiz 2A Future deadline" in the "Course overview" "block"
|
||||
And I log out
|
||||
And I log in as "student2"
|
||||
And I should see "You have quizzes that are due" in the "Course overview" "block"
|
||||
And I should not see "Quiz 1C Future deadline" in the "Course overview" "block"
|
||||
And I should not see "Quiz 1D Future deadline" in the "Course overview" "block"
|
||||
And I should not see "Quiz 1E Future deadline" in the "Course overview" "block"
|
||||
And I should not see "Quiz 1A No deadline" in the "Course overview" "block"
|
||||
And I should not see "Quiz 1B Past deadline" in the "Course overview" "block"
|
||||
And I should see "Quiz 2A Future deadline" in the "Course overview" "block"
|
||||
|
||||
Scenario: View my quizzes that are due and never finished
|
||||
Given I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Quiz 1D Future deadline"
|
||||
And I press "Attempt quiz now"
|
||||
And I follow "Finish attempt ..."
|
||||
And I press "Submit all and finish"
|
||||
And I follow "Course 1"
|
||||
And I follow "Quiz 1E Future deadline"
|
||||
And I press "Attempt quiz now"
|
||||
When I am on homepage
|
||||
Then I should see "You have quizzes that are due" in the "Course overview" "block"
|
||||
And I should see "Quiz 1C Future deadline" in the "Course overview" "block"
|
||||
And I should see "Quiz 1E Future deadline" in the "Course overview" "block"
|
||||
And I should not see "Quiz 1A No deadline" in the "Course overview" "block"
|
||||
And I should not see "Quiz 1B Past deadline" in the "Course overview" "block"
|
||||
And I should not see "Quiz 1D Future deadline" in the "Course overview" "block"
|
||||
And I should not see "Quiz 2A Future deadline" in the "Course overview" "block"
|
||||
|
||||
@@ -57,5 +57,8 @@ function xmldb_block_course_summary_upgrade($oldversion, $block) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -60,19 +60,27 @@ class restore_glossary_random_block_task extends restore_block_task {
|
||||
if ($configdata = $DB->get_field('block_instances', 'configdata', array('id' => $blockid))) {
|
||||
$config = unserialize(base64_decode($configdata));
|
||||
if (!empty($config->glossary)) {
|
||||
// Get glossary mapping and replace it in config
|
||||
if ($glossarymap = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'glossary', $config->glossary)) {
|
||||
$mappedglossary = $DB->get_record('glossary', array('id' => $glossarymap->newitemid),
|
||||
'id,course,globalglossary', MUST_EXIST);
|
||||
$config->glossary = $mappedglossary->id;
|
||||
$config->courseid = $mappedglossary->course;
|
||||
$config->globalglossary = $mappedglossary->globalglossary;
|
||||
$configdata = base64_encode(serialize($config));
|
||||
$DB->set_field('block_instances', 'configdata', $configdata, array('id' => $blockid));
|
||||
// Get glossary mapping and replace it in config
|
||||
$config->glossary = $glossarymap->newitemid;
|
||||
} else if ($this->is_samesite()) {
|
||||
// We are restoring on the same site, check if glossary can be used in the block in this course.
|
||||
$glossaryid = $DB->get_field_sql("SELECT id FROM {glossary} " .
|
||||
"WHERE id = ? AND (course = ? OR globalglossary = 1)",
|
||||
[$config->glossary, $this->get_courseid()]);
|
||||
if (!$glossaryid) {
|
||||
unset($config->glossary);
|
||||
}
|
||||
} else {
|
||||
// The block refers to a glossary not present in the backup file.
|
||||
$DB->set_field('block_instances', 'configdata', '', array('id' => $blockid));
|
||||
unset($config->glossary);
|
||||
}
|
||||
// Unset config variables that are no longer used.
|
||||
unset($config->globalglossary);
|
||||
unset($config->courseid);
|
||||
// Save updated config.
|
||||
$configdata = base64_encode(serialize($config));
|
||||
$DB->set_field('block_instances', 'configdata', $configdata, array('id' => $blockid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,12 @@ define('BGR_NEXTALPHA', '3');
|
||||
|
||||
class block_glossary_random extends block_base {
|
||||
|
||||
/**
|
||||
* @var cm_info|stdClass has properties 'id' (course module id) and 'uservisible'
|
||||
* (whether the glossary is visible to the current user)
|
||||
*/
|
||||
protected $glossarycm = null;
|
||||
|
||||
function init() {
|
||||
$this->title = get_string('pluginname','block_glossary_random');
|
||||
}
|
||||
@@ -58,6 +64,11 @@ class block_glossary_random extends block_base {
|
||||
//check if it's time to put a new entry in cache
|
||||
if (time() > $this->config->nexttime) {
|
||||
|
||||
if (!($cm = $this->get_glossary_cm()) || !$cm->uservisible) {
|
||||
// Skip generating of the cache if we can't display anything to the current user.
|
||||
return false;
|
||||
}
|
||||
|
||||
// place glossary concept and definition in $pref->cache
|
||||
if (!$numberofentries = $DB->count_records('glossary_entries',
|
||||
array('glossaryid'=>$this->config->glossary, 'approved'=>1))) {
|
||||
@@ -65,20 +76,6 @@ class block_glossary_random extends block_base {
|
||||
$this->instance_config_commit();
|
||||
}
|
||||
|
||||
// Get glossary instance, if not found then return without error, as this will be handled in get_content.
|
||||
if (!$glossary = $DB->get_record('glossary', array('id' => $this->config->glossary))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->config->globalglossary = $glossary->globalglossary;
|
||||
|
||||
// Save course id in config, so we can get correct course module.
|
||||
$this->config->courseid = $glossary->course;
|
||||
|
||||
// Get module and context, to be able to rewrite urls
|
||||
if (! $cm = get_coursemodule_from_instance('glossary', $glossary->id, $this->config->courseid)) {
|
||||
return false;
|
||||
}
|
||||
$glossaryctx = context_module::instance($cm->id);
|
||||
|
||||
$limitfrom = 0;
|
||||
@@ -156,6 +153,67 @@ class block_glossary_random extends block_base {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the instance's configuration data with those currently in $this->config;
|
||||
*/
|
||||
function instance_config_commit($nolongerused = false) {
|
||||
// Unset config variables that are no longer used.
|
||||
unset($this->config->globalglossary);
|
||||
unset($this->config->courseid);
|
||||
parent::instance_config_commit($nolongerused);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if glossary is available - it should be either located in the same course or be global
|
||||
*
|
||||
* @return null|cm_info|stdClass object with properties 'id' (course module id) and 'uservisible'
|
||||
*/
|
||||
protected function get_glossary_cm() {
|
||||
global $DB;
|
||||
if (empty($this->config->glossary)) {
|
||||
// No glossary is configured.
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!empty($this->glossarycm)) {
|
||||
return $this->glossarycm;
|
||||
}
|
||||
|
||||
if (!empty($this->page->course->id)) {
|
||||
// First check if glossary belongs to the current course (we don't need to make any DB queries to find it).
|
||||
$modinfo = get_fast_modinfo($this->page->course);
|
||||
if (isset($modinfo->instances['glossary'][$this->config->glossary])) {
|
||||
$this->glossarycm = $modinfo->instances['glossary'][$this->config->glossary];
|
||||
if ($this->glossarycm->uservisible) {
|
||||
// The glossary is in the same course and is already visible to the current user,
|
||||
// no need to check if it is global, save on DB query.
|
||||
return $this->glossarycm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find course module id for the given glossary, only if it is global.
|
||||
$cm = $DB->get_record_sql("SELECT cm.id, cm.visible AS uservisible
|
||||
FROM {course_modules} cm
|
||||
JOIN {modules} md ON md.id = cm.module
|
||||
JOIN {glossary} g ON g.id = cm.instance
|
||||
WHERE g.id = :instance AND md.name = :modulename AND g.globalglossary = 1",
|
||||
['instance' => $this->config->glossary, 'modulename' => 'glossary']);
|
||||
|
||||
if ($cm) {
|
||||
// This is a global glossary, create an object with properties 'id' and 'uservisible'. We don't need any
|
||||
// other information so why bother retrieving it. Full access check is skipped for global glossaries for
|
||||
// performance reasons.
|
||||
$this->glossarycm = $cm;
|
||||
} else if (empty($this->glossarycm)) {
|
||||
// Glossary does not exist. Remove it in the config so we don't repeat this check again later.
|
||||
$this->config->glossary = 0;
|
||||
$this->instance_config_commit();
|
||||
}
|
||||
|
||||
return $this->glossarycm;
|
||||
}
|
||||
|
||||
function instance_allow_multiple() {
|
||||
// Are you going to allow multiple instances of each block?
|
||||
// If yes, then it is assumed that the block WILL USE per-instance configuration
|
||||
@@ -163,81 +221,35 @@ class block_glossary_random extends block_base {
|
||||
}
|
||||
|
||||
function get_content() {
|
||||
global $USER, $CFG, $DB;
|
||||
|
||||
if (empty($this->config->glossary)) {
|
||||
$this->content = new stdClass();
|
||||
if ($this->user_can_edit()) {
|
||||
$this->content->text = get_string('notyetconfigured','block_glossary_random');
|
||||
} else {
|
||||
$this->content->text = '';
|
||||
}
|
||||
$this->content->footer = '';
|
||||
if ($this->content !== null) {
|
||||
return $this->content;
|
||||
}
|
||||
$this->content = (object)['text' => '', 'footer' => ''];
|
||||
|
||||
require_once($CFG->dirroot.'/course/lib.php');
|
||||
|
||||
// If $this->config->globalglossary is not set then get glossary info from db.
|
||||
if (!isset($this->config->globalglossary)) {
|
||||
if (!$glossary = $DB->get_record('glossary', array('id' => $this->config->glossary))) {
|
||||
return '';
|
||||
} else {
|
||||
$this->config->courseid = $glossary->course;
|
||||
$this->config->globalglossary = $glossary->globalglossary;
|
||||
$this->instance_config_commit();
|
||||
}
|
||||
}
|
||||
|
||||
$modinfo = get_fast_modinfo($this->config->courseid);
|
||||
// If deleted glossary or non-global glossary on different course page, then reset.
|
||||
if (!isset($modinfo->instances['glossary'][$this->config->glossary])
|
||||
|| ((empty($this->config->globalglossary) && ($this->config->courseid != $this->page->course->id)))) {
|
||||
$this->config->glossary = 0;
|
||||
$this->config->cache = '';
|
||||
$this->instance_config_commit();
|
||||
|
||||
$this->content = new stdClass();
|
||||
if (!$cm = $this->get_glossary_cm()) {
|
||||
if ($this->user_can_edit()) {
|
||||
$this->content->text = get_string('notyetconfigured','block_glossary_random');
|
||||
} else {
|
||||
$this->content->text = '';
|
||||
$this->content->text = get_string('notyetconfigured', 'block_glossary_random');
|
||||
}
|
||||
$this->content->footer = '';
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
$cm = $modinfo->instances['glossary'][$this->config->glossary];
|
||||
if (!has_capability('mod/glossary:view', context_module::instance($cm->id))) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (empty($this->config->cache)) {
|
||||
$this->config->cache = '';
|
||||
}
|
||||
|
||||
if ($this->content !== NULL) {
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
$this->content = new stdClass();
|
||||
|
||||
// Show glossary if visible and place links in footer.
|
||||
if ($cm->visible) {
|
||||
if ($cm->uservisible) {
|
||||
// Show glossary if visible and place links in footer.
|
||||
$this->content->text = $this->config->cache;
|
||||
if (has_capability('mod/glossary:write', context_module::instance($cm->id))) {
|
||||
$this->content->footer = '<a href="'.$CFG->wwwroot.'/mod/glossary/edit.php?cmid='.$cm->id
|
||||
.'" title="'.$this->config->addentry.'">'.$this->config->addentry.'</a><br />';
|
||||
} else {
|
||||
$this->content->footer = '';
|
||||
$this->content->footer = html_writer::link(new moodle_url('/mod/glossary/edit.php', ['cmid' => $cm->id]),
|
||||
format_string($this->config->addentry)) . '<br/>';
|
||||
}
|
||||
|
||||
$this->content->footer .= '<a href="'.$CFG->wwwroot.'/mod/glossary/view.php?id='.$cm->id
|
||||
.'" title="'.$this->config->viewglossary.'">'.$this->config->viewglossary.'</a>';
|
||||
|
||||
// Otherwise just place some text, no link.
|
||||
$this->content->footer .= html_writer::link(new moodle_url('/mod/glossary/view.php', ['id' => $cm->id]),
|
||||
format_string($this->config->viewglossary));
|
||||
} else {
|
||||
$this->content->footer = $this->config->invisible;
|
||||
// Otherwise just place some text, no link.
|
||||
$this->content->footer = format_string($this->config->invisible);
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
@block @block_glossary_random
|
||||
Feature: Random glossary entry block linking to global glossary
|
||||
In order to show the entries from glossary
|
||||
As a teacher
|
||||
I can add the random glossary entry to a course page
|
||||
|
||||
Background:
|
||||
Given the following "courses" exist:
|
||||
| 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 "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Sam1 | Student1 | student1@example.com |
|
||||
| teacher1 | Terry1 | Teacher1 | teacher1@example.com |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
|
||||
Scenario: View random (last) entry in the global glossary
|
||||
When I log in as "admin"
|
||||
And I am on site homepage
|
||||
And I follow "Course 2"
|
||||
And I follow "Tips and Tricks"
|
||||
And I press "Add a new entry"
|
||||
And I set the following fields to these values:
|
||||
| Concept | Never come late |
|
||||
| Definition | Come in time for your classes |
|
||||
And I press "Save changes"
|
||||
And I log out
|
||||
# As a teacher add a block to the course page linking to the global glossary.
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add the "Random glossary entry" block
|
||||
And I configure the "block_glossary_random" block
|
||||
And I set the following fields to these values:
|
||||
| Title | Tip of the day |
|
||||
| Take entries from this glossary | Tips and Tricks |
|
||||
| How a new entry is chosen | Last modified entry |
|
||||
And I press "Save changes"
|
||||
Then I should see "Never come late" in the "Tip of the day" "block"
|
||||
And I should not see "Add a new entry" in the "Tip of the day" "block"
|
||||
And I should see "View all entries" in the "Tip of the day" "block"
|
||||
And I log out
|
||||
# Student who can't see the module is still able to view entries in this block (because the glossary was marked as global)
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I should see "Never come late" in the "Tip of the day" "block"
|
||||
And I should not see "Add a new entry" in the "Tip of the day" "block"
|
||||
And I should see "View all entries" in the "Tip of the day" "block"
|
||||
And I log out
|
||||
|
||||
Scenario: Removing the global glossary that is used in random glossary block
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add the "Random glossary entry" block
|
||||
And I configure the "block_glossary_random" block
|
||||
And I set the following fields to these values:
|
||||
| Title | Tip of the day |
|
||||
| Take entries from this glossary | Tips and Tricks |
|
||||
| How a new entry is chosen | Last modified entry |
|
||||
And I press "Save changes"
|
||||
And I log out
|
||||
And I log in as "admin"
|
||||
And I am on site homepage
|
||||
And I follow "Course 2"
|
||||
And I follow "Tips and Tricks"
|
||||
And I follow "Edit settings"
|
||||
And I set the field "globalglossary" to "0"
|
||||
And I press "Save and return to course"
|
||||
And I am on site homepage
|
||||
And I follow "Course 1"
|
||||
Then I should see "Please configure this block using the edit icon." in the "Tip of the day" "block"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And "Tip of the day" "block" should not exist
|
||||
And I log out
|
||||
@@ -42,5 +42,8 @@ function xmldb_block_html_upgrade($oldversion) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ $url = new moodle_url('/blocks/lp/competencies_to_review.php');
|
||||
$PAGE->set_context(context_user::instance($USER->id));
|
||||
$PAGE->set_url($url);
|
||||
$PAGE->set_title($toreviewstr);
|
||||
$PAGE->set_pagelayout('standard');
|
||||
$PAGE->navbar->add($toreviewstr, $url);
|
||||
|
||||
$output = $PAGE->get_renderer('block_lp');
|
||||
|
||||
@@ -35,6 +35,7 @@ $url = new moodle_url('/blocks/lp/plans_to_review.php');
|
||||
$PAGE->set_context(context_user::instance($USER->id));
|
||||
$PAGE->set_url($url);
|
||||
$PAGE->set_title($toreviewstr);
|
||||
$PAGE->set_pagelayout('standard');
|
||||
$PAGE->navbar->add($toreviewstr, $url);
|
||||
|
||||
$output = $PAGE->get_renderer('block_lp');
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
@block @block_messages
|
||||
Feature: The messages block allows users to list new messages an a course
|
||||
In order to enable the messages block in a course
|
||||
As a teacher
|
||||
I can add the messages block to a course and view my messages
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email | idnumber |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com | T1 |
|
||||
| student1 | Student | 1 | student1@example.com | S1 |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
|
||||
Scenario: View the block by a user with messaging disabled.
|
||||
Given the following config values are set as admin:
|
||||
| messaging | 0 |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
When I turn editing mode on
|
||||
And I add the "Messages" block
|
||||
Then I should see "Messaging is disabled on this site" in the "Messages" "block"
|
||||
|
||||
Scenario: View the block by a user who does not have any messages.
|
||||
Given I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
When I turn editing mode on
|
||||
And I add the "Messages" block
|
||||
Then I should see "No messages waiting" in the "Messages" "block"
|
||||
|
||||
Scenario: View the block by a user who has messages.
|
||||
Given I log in as "student1"
|
||||
And I follow "Messages" in the user menu
|
||||
And I send "This is message 1" message to "Teacher 1" user
|
||||
And I send "This is message 2" message to "Teacher 1" user
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
When I turn editing mode on
|
||||
And I add the "Messages" block
|
||||
Then I should see "Student 1" in the "Messages" "block"
|
||||
|
||||
Scenario: Use the block to send a message to a user.
|
||||
Given I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add the "Messages" block
|
||||
And I follow "Messages"
|
||||
And I send "This is message 1" message to "Student 1" user
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
Then I should see "Teacher 1" in the "Messages" "block"
|
||||
@@ -0,0 +1,48 @@
|
||||
@block @block_messages
|
||||
Feature: The messages block allows users to list new messages on the dashboard
|
||||
In order to enable the messages block on the dashboard
|
||||
As a user
|
||||
I can add the messages block to a my dashboard and view my messages
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email | idnumber |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com | T1 |
|
||||
| student1 | Student | 1 | student1@example.com | S1 |
|
||||
|
||||
Scenario: View the block by a user with messaging disabled.
|
||||
Given the following config values are set as admin:
|
||||
| messaging | 0 |
|
||||
And I log in as "teacher1"
|
||||
And I press "Customise this page"
|
||||
When I add the "Messages" block
|
||||
Then I should see "Messaging is disabled on this site" in the "Messages" "block"
|
||||
|
||||
Scenario: View the block by a user who does not have any messages.
|
||||
Given I log in as "teacher1"
|
||||
And I press "Customise this page"
|
||||
When I add the "Messages" block
|
||||
Then I should see "No messages waiting" in the "Messages" "block"
|
||||
|
||||
Scenario: View the block by a user who has messages.
|
||||
Given I log in as "student1"
|
||||
And I follow "Messages" in the user menu
|
||||
And I send "This is message 1" message to "Teacher 1" user
|
||||
And I send "This is message 2" message to "Teacher 1" user
|
||||
And I log out
|
||||
When I log in as "teacher1"
|
||||
And I press "Customise this page"
|
||||
And I add the "Messages" block
|
||||
Then I should see "Student 1" in the "Messages" "block"
|
||||
|
||||
Scenario: Use the block to send a message to a user.
|
||||
Given I log in as "teacher1"
|
||||
And I press "Customise this page"
|
||||
And I add the "Messages" block
|
||||
And I follow "Messages"
|
||||
And I send "This is message 1" message to "Student 1" user
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I press "Customise this page"
|
||||
And I add the "Messages" block
|
||||
Then I should see "Teacher 1" in the "Messages" "block"
|
||||
@@ -0,0 +1,56 @@
|
||||
@block @block_messages
|
||||
Feature: The messages block allows users to list new messages on the frontpage
|
||||
In order to enable the messages block on the frontpage
|
||||
As an admin
|
||||
I can add the messages block to a the frontpage and view my messages
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email | idnumber |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com | T1 |
|
||||
| student1 | Student | 1 | student1@example.com | S1 |
|
||||
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 "Messages" block
|
||||
And I log out
|
||||
|
||||
Scenario: View the block by a user with messaging disabled.
|
||||
Given the following config values are set as admin:
|
||||
| messaging | 0 |
|
||||
And I log in as "admin"
|
||||
And I am on site homepage
|
||||
When I navigate to "Turn editing on" node in "Front page settings"
|
||||
And I should see "Messaging is disabled on this site" in the "Messages" "block"
|
||||
Then I navigate to "Turn editing off" node in "Front page settings"
|
||||
And I should not see "Messaging is disabled on this site"
|
||||
|
||||
Scenario: View the block by a user who does not have any messages.
|
||||
Given I log in as "teacher1"
|
||||
When I am on site homepage
|
||||
Then I should see "No messages waiting" in the "Messages" "block"
|
||||
|
||||
Scenario: Try to view the block as a guest user.
|
||||
Given I log in as "guest"
|
||||
When I am on site homepage
|
||||
Then I should not see "Messages"
|
||||
|
||||
Scenario: View the block by a user who has messages.
|
||||
Given I log in as "student1"
|
||||
And I follow "Messages" in the user menu
|
||||
And I send "This is message 1" message to "Teacher 1" user
|
||||
And I send "This is message 2" message to "Teacher 1" user
|
||||
And I log out
|
||||
When I log in as "teacher1"
|
||||
And I am on site homepage
|
||||
Then I should see "Student 1" in the "Messages" "block"
|
||||
|
||||
Scenario: Use the block to send a message to a user.
|
||||
Given I log in as "teacher1"
|
||||
And I am on site homepage
|
||||
And I follow "Messages"
|
||||
And I send "This is message 1" message to "Student 1" user
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I am on site homepage
|
||||
Then I should see "Teacher 1" in the "Messages" "block"
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/tree"],function(a,b){return{init:function(){new b(".block_navigation .block_tree")}}});
|
||||
define(["jquery","core/tree"],function(a,b){return{init:function(a){var c=new b(".block_navigation .block_tree");c.finishExpandingGroup=function(c){b.prototype.finishExpandingGroup.call(this,c),Y.use("moodle-core-event",function(){Y.Global.fire(M.core.globalEvents.BLOCK_CONTENT_UPDATED,{instanceid:a})})},c.collapseGroup=function(c){b.prototype.collapseGroup.call(this,c),Y.use("moodle-core-event",function(){Y.Global.fire(M.core.globalEvents.BLOCK_CONTENT_UPDATED,{instanceid:a})})}}}});
|
||||
@@ -23,8 +23,24 @@
|
||||
*/
|
||||
define(['jquery', 'core/tree'], function($, Tree) {
|
||||
return {
|
||||
init: function() {
|
||||
new Tree(".block_navigation .block_tree");
|
||||
init: function(instanceid) {
|
||||
var navTree = new Tree(".block_navigation .block_tree");
|
||||
navTree.finishExpandingGroup = function(item) {
|
||||
Tree.prototype.finishExpandingGroup.call(this, item);
|
||||
Y.use('moodle-core-event', function() {
|
||||
Y.Global.fire(M.core.globalEvents.BLOCK_CONTENT_UPDATED, {
|
||||
instanceid: instanceid
|
||||
});
|
||||
});
|
||||
};
|
||||
navTree.collapseGroup = function(item) {
|
||||
Tree.prototype.collapseGroup.call(this, item);
|
||||
Y.use('moodle-core-event', function() {
|
||||
Y.Global.fire(M.core.globalEvents.BLOCK_CONTENT_UPDATED, {
|
||||
instanceid: instanceid
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -108,8 +108,11 @@ class block_navigation extends block_base {
|
||||
*/
|
||||
function get_required_javascript() {
|
||||
parent::get_required_javascript();
|
||||
$arguments = array(
|
||||
'instanceid' => $this->instance->id
|
||||
);
|
||||
$this->page->requires->string_for_js('viewallcourses', 'moodle');
|
||||
$this->page->requires->js_call_amd('block_navigation/navblock', 'init', array());
|
||||
$this->page->requires->js_call_amd('block_navigation/navblock', 'init', $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -64,5 +64,8 @@ function xmldb_block_navigation_upgrade($oldversion, $block) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
.block_navigation .block_tree ul {margin-left: 18px;}
|
||||
.block_navigation .block_tree p.hasicon {text-indent: -21px; padding-left: 21px;}
|
||||
.block_navigation .block_tree p.hasicon img {width: 16px; height: 16px; margin-top: 3px; margin-right: 5px; vertical-align: top;}
|
||||
.block_navigation .block_tree p.hasicon.visibleifjs {display: block;}
|
||||
|
||||
.block_navigation .block_tree .tree_item {cursor:pointer; padding-left: 0;margin:3px 0px; background-position: 0 50%; background-repeat: no-repeat;}
|
||||
.block_navigation .block_tree .tree_item.branch {padding-left: 21px;}
|
||||
|
||||
@@ -81,7 +81,6 @@ class block_news_items extends block_base {
|
||||
$groupmode = groups_get_activity_groupmode($cm);
|
||||
$currentgroup = groups_get_activity_group($cm, true);
|
||||
|
||||
|
||||
if (forum_user_can_post_discussion($forum, $currentgroup, $groupmode, $cm, $context)) {
|
||||
$text .= '<div class="newlink"><a href="'.$CFG->wwwroot.'/mod/forum/post.php?forum='.$forum->id.'">'.
|
||||
get_string('addanewtopic', 'forum').'</a>...</div>';
|
||||
@@ -96,7 +95,8 @@ class block_news_items extends block_base {
|
||||
// This sort will ignore pinned posts as we want the most recent.
|
||||
$sort = forum_get_default_sort_order(true, 'p.modified', 'd', false);
|
||||
if (! $discussions = forum_get_discussions($cm, $sort, false,
|
||||
$currentgroup, $this->page->course->newsitems) ) {
|
||||
-1, $this->page->course->newsitems,
|
||||
false, -1, 0, FORUM_POSTS_ALL_USER_GROUPS) ) {
|
||||
$text .= '('.get_string('nonews', 'forum').')';
|
||||
$this->content->text = $text;
|
||||
return $this->content;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
@block @block_online_users
|
||||
Feature: The online users block allow you to see who is currently online
|
||||
In order to enable the online users block on an course page
|
||||
As a teacher
|
||||
I can add the online users block to a course page
|
||||
|
||||
Background:
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| student2 | Student | 2 | student2@example.com |
|
||||
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
|
||||
Scenario: Add the online users on course page and see myself
|
||||
Given I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
When I add the "Online users" block
|
||||
Then I should see "Teacher 1" in the "Online users" "block"
|
||||
|
||||
Scenario: Add the online users on course page and see other logged in users
|
||||
Given I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add the "Online users" block
|
||||
And I log out
|
||||
And I log in as "student2"
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
Then I should see "Teacher 1" in the "Online users" "block"
|
||||
And I should see "Student 1" in the "Online users" "block"
|
||||
And I should not see "Student 2" in the "Online users" "block"
|
||||
@@ -0,0 +1,26 @@
|
||||
@block @block_online_users
|
||||
Feature: The online users block allow you to see who is currently online
|
||||
In order to use the online users block on the dashboard
|
||||
As a user
|
||||
I can view the online users block on my dashboard
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| student2 | Student | 2 | student2@example.com |
|
||||
|
||||
Scenario: View the online users block on the dashboard and see myself
|
||||
Given I log in as "teacher1"
|
||||
Then I should see "Teacher 1" in the "Online users" "block"
|
||||
|
||||
Scenario: View the online users block on the dashboard and see other logged in users
|
||||
Given I log in as "student2"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I log out
|
||||
When I log in as "teacher1"
|
||||
Then I should see "Teacher 1" in the "Online users" "block"
|
||||
And I should see "Student 1" in the "Online users" "block"
|
||||
And I should see "Student 2" in the "Online users" "block"
|
||||
@@ -0,0 +1,48 @@
|
||||
@block @block_online_users
|
||||
Feature: The online users block allow you to see who is currently online
|
||||
In order to enable the online users block on the front page page
|
||||
As an admin
|
||||
I can add the online users block to the front page page
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| student2 | Student | 2 | student2@example.com |
|
||||
|
||||
Scenario: View the online users block on the front page and see myself
|
||||
Given I log in as "admin"
|
||||
And I am on site homepage
|
||||
And I navigate to "Turn editing on" node in "Front page settings"
|
||||
When I add the "Online users" block
|
||||
Then I should see "Admin User" in the "Online users" "block"
|
||||
|
||||
Scenario: View the online users block on the front page as a logged in user
|
||||
Given 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 "Online users" block
|
||||
And I log out
|
||||
And I log in as "student2"
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I am on site homepage
|
||||
Then I should see "Admin User" in the "Online users" "block"
|
||||
And I should see "Student 1" in the "Online users" "block"
|
||||
And I should see "Student 2" in the "Online users" "block"
|
||||
|
||||
Scenario: View the online users block on the front page as a guest
|
||||
Given 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 "Online users" block
|
||||
And I log out
|
||||
And I log in as "student2"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I log out
|
||||
When I log in as "guest"
|
||||
And I am on site homepage
|
||||
Then I should see "Admin User" in the "Online users" "block"
|
||||
And I should see "Student 1" in the "Online users" "block"
|
||||
And I should see "Student 2" in the "Online users" "block"
|
||||
@@ -0,0 +1,29 @@
|
||||
@block @block_private_files @file_upload @javascript
|
||||
Feature: The private files block allows users to store files privately in moodle
|
||||
In order to store a private file in moodle
|
||||
As a teacher
|
||||
I can upload the file to my private files area using the private files block in an activity
|
||||
|
||||
Scenario: Upload a file to the private files block in an activity
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro |
|
||||
| page | C1 | page1 | Test page name | Test page description |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I follow "Test page name"
|
||||
And I add the "Private files" block
|
||||
And I should see "No files available" in the "Private files" "block"
|
||||
When I follow "Manage private files..."
|
||||
And I upload "blocks/private_files/tests/fixtures/testfile.txt" file to "Files" filemanager
|
||||
And I press "Save changes"
|
||||
Then I should see "testfile.txt" in the "Private files" "block"
|
||||
@@ -0,0 +1,25 @@
|
||||
@block @block_private_files @file_upload @javascript
|
||||
Feature: The private files block allows users to store files privately in moodle
|
||||
In order to store a private file in moodle
|
||||
As a teacher
|
||||
I can upload the file to my private files area using the private files block in a course
|
||||
|
||||
Scenario: Upload a file to the private files block from a course
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add the "Private files" block
|
||||
And I should see "No files available" in the "Private files" "block"
|
||||
When I follow "Manage private files..."
|
||||
And I upload "blocks/private_files/tests/fixtures/testfile.txt" file to "Files" filemanager
|
||||
And I press "Save changes"
|
||||
Then I should see "testfile.txt" in the "Private files" "block"
|
||||
@@ -0,0 +1,17 @@
|
||||
@block @block_private_files @_file_upload @javascript
|
||||
Feature: The private files block allows users to store files privately in moodle
|
||||
In order to store a private file in moodle
|
||||
As a user
|
||||
I can upload the file to my private files area using the private files block on the dashboard
|
||||
|
||||
Scenario: Upload a file to the private files block from the dashboard
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
And I log in as "teacher1"
|
||||
And "Private files" "block" should exist
|
||||
And I should see "No files available" in the "Private files" "block"
|
||||
When I follow "Manage private files..."
|
||||
And I upload "blocks/private_files/tests/fixtures/testfile.txt" file to "Files" filemanager
|
||||
And I press "Save changes"
|
||||
Then I should see "testfile.txt" in the "Private files" "block"
|
||||
@@ -0,0 +1,34 @@
|
||||
@block @block_private_files @_file_upload
|
||||
Feature: The private files block allows users to store files privately in moodle
|
||||
In order to store a private file in moodle
|
||||
As a teacher
|
||||
I can upload the file to my private files area using the private files block from the front page
|
||||
|
||||
Background:
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
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 "Private files" block
|
||||
And I log out
|
||||
|
||||
Scenario: Try to view the private files block as a guest
|
||||
Given I log in as "guest"
|
||||
When I am on site homepage
|
||||
Then "Private files" "block" should not exist
|
||||
|
||||
@javascript
|
||||
Scenario: Upload a file to the private files block from the frontpage
|
||||
Given I log in as "teacher1"
|
||||
And I am on site homepage
|
||||
And "Private files" "block" should exist
|
||||
And I should see "No files available" in the "Private files" "block"
|
||||
When I follow "Manage private files..."
|
||||
And I upload "blocks/private_files/tests/fixtures/testfile.txt" file to "Files" filemanager
|
||||
And I press "Save changes"
|
||||
Then I should see "testfile.txt" in the "Private files" "block"
|
||||
@@ -0,0 +1 @@
|
||||
This is a test file
|
||||
@@ -107,5 +107,8 @@ function xmldb_block_quiz_results_upgrade($oldversion, $block) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -56,5 +56,8 @@ function xmldb_block_recent_activity_upgrade($oldversion, $block) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -53,5 +53,8 @@ function xmldb_block_rss_client_upgrade($oldversion) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -58,5 +58,8 @@ function xmldb_block_section_links_upgrade($oldversion, $block) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -57,5 +57,8 @@ function xmldb_block_selfcompletion_upgrade($oldversion, $block) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/tree"],function(a,b){return{init:function(a){var c=new b(".block_settings .block_tree");if(a){var d=c.treeRoot.find("#"+a),e=d.children("a").first();e.replaceWith('<span tabindex="0">'+e.html()+"</span>")}}}});
|
||||
define(["jquery","core/tree"],function(a,b){return{init:function(a,c){var d=new b(".block_settings .block_tree");if(c){var e=d.treeRoot.find("#"+c),f=e.children("a").first();f.replaceWith('<span tabindex="0">'+f.html()+"</span>")}d.finishExpandingGroup=function(c){b.prototype.finishExpandingGroup.call(this,c),Y.use("moodle-core-event",function(){Y.Global.fire(M.core.globalEvents.BLOCK_CONTENT_UPDATED,{instanceid:a})})},d.collapseGroup=function(c){b.prototype.collapseGroup.call(this,c),Y.Global.fire(M.core.globalEvents.BLOCK_CONTENT_UPDATED,{instanceid:a}),Y.use("moodle-core-event",function(){Y.Global.fire(M.core.globalEvents.BLOCK_CONTENT_UPDATED,{instanceid:a})})}}}});
|
||||
@@ -16,20 +16,39 @@
|
||||
/**
|
||||
* Load the settings block tree javscript
|
||||
*
|
||||
* @module block_navigation/navblock
|
||||
* @module block_settings/settingsblock
|
||||
* @package core
|
||||
* @copyright 2015 John Okely <john@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/tree'], function($, Tree) {
|
||||
return {
|
||||
init: function(siteAdminNodeId) {
|
||||
init: function(instanceid, siteAdminNodeId) {
|
||||
var adminTree = new Tree(".block_settings .block_tree");
|
||||
if (siteAdminNodeId) {
|
||||
var siteAdminNode = adminTree.treeRoot.find('#' + siteAdminNodeId);
|
||||
var siteAdminLink = siteAdminNode.children('a').first();
|
||||
siteAdminLink.replaceWith('<span tabindex="0">' + siteAdminLink.html() + '</span>');
|
||||
}
|
||||
adminTree.finishExpandingGroup = function(item) {
|
||||
Tree.prototype.finishExpandingGroup.call(this, item);
|
||||
Y.use('moodle-core-event', function() {
|
||||
Y.Global.fire(M.core.globalEvents.BLOCK_CONTENT_UPDATED, {
|
||||
instanceid: instanceid
|
||||
});
|
||||
});
|
||||
};
|
||||
adminTree.collapseGroup = function(item) {
|
||||
Tree.prototype.collapseGroup.call(this, item);
|
||||
Y.Global.fire(M.core.globalEvents.BLOCK_CONTENT_UPDATED, {
|
||||
instanceid: instanceid
|
||||
});
|
||||
Y.use('moodle-core-event', function() {
|
||||
Y.Global.fire(M.core.globalEvents.BLOCK_CONTENT_UPDATED, {
|
||||
instanceid: instanceid
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -91,18 +91,13 @@ class block_settings extends block_base {
|
||||
|
||||
function get_required_javascript() {
|
||||
global $PAGE;
|
||||
$adminnodeid = null;
|
||||
$adminnode = $PAGE->settingsnav->find('siteadministration', navigation_node::TYPE_SITE_ADMIN);
|
||||
if (!empty($adminnode)) {
|
||||
$adminnodeid = $adminnode->id;
|
||||
}
|
||||
parent::get_required_javascript();
|
||||
$arguments = array(
|
||||
'id' => $this->instance->id,
|
||||
'instance' => $this->instance->id,
|
||||
'candock' => $this->instance_can_be_docked()
|
||||
'instanceid' => $this->instance->id,
|
||||
'adminnodeid' => $adminnode ? $adminnode->id : null
|
||||
);
|
||||
$this->page->requires->js_call_amd('block_settings/settingsblock', 'init', array($adminnodeid));
|
||||
$this->page->requires->js_call_amd('block_settings/settingsblock', 'init', $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -64,5 +64,8 @@ function xmldb_block_settings_upgrade($oldversion, $block) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.block_settings .block_tree ul {margin-left: 18px;}
|
||||
.block_settings .block_tree p.hasicon {text-indent: -21px; padding-left: 21px;}
|
||||
.block_settings .block_tree p.hasicon img {width: 16px; height: 16px; margin-top: 3px; margin-right: 5px; vertical-align: top;}
|
||||
.block_settings .block_tree p.hasicon.visibleifjs {display: block;}
|
||||
|
||||
.block_settings .block_tree .tree_item.branch {padding-left: 21px;}
|
||||
.block_settings .block_tree .tree_item {cursor:pointer; margin:3px 0px; background-position: 0 50%; background-repeat: no-repeat;}
|
||||
|
||||
@@ -34,6 +34,11 @@ list($context, $course, $cm) = get_context_info_array($contextid);
|
||||
require_login($course, true, $cm);
|
||||
require_sesskey();
|
||||
|
||||
if (!$course) {
|
||||
// Require_login() does not set context if called without a $course, do it manually.
|
||||
$PAGE->set_context($context);
|
||||
}
|
||||
|
||||
$action = optional_param('action', '', PARAM_ALPHA);
|
||||
$area = optional_param('area', '', PARAM_AREA);
|
||||
$content = optional_param('content', '', PARAM_RAW);
|
||||
@@ -48,7 +53,9 @@ if ($action !== 'add') {
|
||||
|
||||
$cmt = new stdClass;
|
||||
$cmt->contextid = $contextid;
|
||||
$cmt->courseid = $course->id;
|
||||
if ($course) {
|
||||
$cmt->courseid = $course->id;
|
||||
}
|
||||
$cmt->cm = $cm;
|
||||
$cmt->area = $area;
|
||||
$cmt->itemid = $itemid;
|
||||
|
||||
+5
-1
@@ -153,7 +153,11 @@ class comment {
|
||||
} else if (!empty($options->courseid)) {
|
||||
$this->courseid = $options->courseid;
|
||||
} else {
|
||||
$this->courseid = SITEID;
|
||||
if ($coursecontext = $this->context->get_course_context(false)) {
|
||||
$this->courseid = $coursecontext->instanceid;
|
||||
} else {
|
||||
$this->courseid = SITEID;
|
||||
}
|
||||
}
|
||||
|
||||
// setup coursemodule
|
||||
|
||||
@@ -4166,11 +4166,8 @@ class api {
|
||||
return array();
|
||||
}
|
||||
|
||||
$params = array(
|
||||
'usercompetencyid' => $usercompetency->get_id(),
|
||||
'contextid' => context_course::instance($courseid)->id
|
||||
);
|
||||
return evidence::get_records($params, $sort, $order, $skip, $limit);
|
||||
$context = context_course::instance($courseid);
|
||||
return evidence::get_records_for_usercompetency($usercompetency->get_id(), $context, $sort, $order, $skip, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user