Compare commits
269 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b90b0e6a8a | |||
| 58a55fcd40 | |||
| c37dd0ecbd | |||
| 84462c3e71 | |||
| 129b2186ac | |||
| 7eb6482266 | |||
| 9e4c3f858f | |||
| a7bbba41bf | |||
| bfc911b928 | |||
| 9766906cc5 | |||
| 0864e3adb3 | |||
| 86de73cb14 | |||
| b3e6ce7cfe | |||
| 94836d56b6 | |||
| 3477b9c1c5 | |||
| 6ee405e107 | |||
| 869b26f3e3 | |||
| d1f085a771 | |||
| 22ac74f686 | |||
| 72a51148e9 | |||
| 7e6d3caab7 | |||
| 0b15830a44 | |||
| 6c34850db6 | |||
| ed81c8df64 | |||
| efa1cab75a | |||
| aa99d2ebac | |||
| 9c4d90cb52 | |||
| 6a15f3890e | |||
| 4a4bf70e8b | |||
| 3aba658a40 | |||
| ed154e71b0 | |||
| 06d46b875b | |||
| 478b7146e5 | |||
| 5febdbe6a2 | |||
| 09e068e8bb | |||
| d17d7263fd | |||
| 0afaaad6dd | |||
| 556cdde71a | |||
| f24eb73cc5 | |||
| 01576f2100 | |||
| f39e564141 | |||
| ef1ecd2ad3 | |||
| c7e549cda4 | |||
| 5ab0e5351b | |||
| 1eacff88b1 | |||
| e3189a8d18 | |||
| c7a9221ef2 | |||
| c95773e2e3 | |||
| b61fae245c | |||
| a69cf7f929 | |||
| 147b16d552 | |||
| ce1a7659bc | |||
| 6c7b5a35a6 | |||
| 601e5c6d83 | |||
| 89b16e0bef | |||
| dc1d2cbc73 | |||
| 6be0f7be06 | |||
| 78fd0486c8 | |||
| 88e822b7aa | |||
| ba484e50c8 | |||
| 3667cf8429 | |||
| 6de86a8a2b | |||
| 3521e03fd2 | |||
| 16534d538e | |||
| 5c32629d04 | |||
| f99ae1651a | |||
| d4389e4a0e | |||
| 11acdd8532 | |||
| 5a9cc63733 | |||
| 1632e59179 | |||
| 1056e8ba25 | |||
| bb7163fc2b | |||
| eb5037adaf | |||
| e25151c437 | |||
| 9d3320dd6f | |||
| 2f8fd40195 | |||
| 72f1926ed9 | |||
| 121012741d | |||
| 457d2b5e2e | |||
| c696306936 | |||
| ef80cb5890 | |||
| 5ecae9254a | |||
| 05f35b612d | |||
| 45947bca5f | |||
| bf99f67aae | |||
| 6cd8ffb6c0 | |||
| b64c2aeb13 | |||
| 8ed6cc973c | |||
| 7d58caa23a | |||
| 3bf5ec446f | |||
| e2c7dd3d43 | |||
| daa7fe6afa | |||
| 4f2e19bb73 | |||
| 81ad3f094d | |||
| 9f83c03275 | |||
| 2cfb12db0e | |||
| d6ac705d63 | |||
| 0872d00857 | |||
| f6559c6eaf | |||
| 9ebd08c452 | |||
| e1a05fd2ee | |||
| 260d45cdb1 | |||
| 63c3cb0312 | |||
| 9e1671f1ea | |||
| a20cab3aad | |||
| 687f8a8479 | |||
| f3dc49171b | |||
| bc72c006b1 | |||
| 75103c45dc | |||
| a0e0aaadcc | |||
| 7f6ea348ef | |||
| 3977956e75 | |||
| 34d69085c0 | |||
| 88cef90bec | |||
| f077d83df1 | |||
| 0a7e544418 | |||
| b235008ebe | |||
| 9c13055211 | |||
| 28bbf01761 | |||
| 33467d709c | |||
| f3142c10a7 | |||
| e23d81ca40 | |||
| 1f81646cbb | |||
| 6417d9c239 | |||
| 398323186d | |||
| 7e0858ce24 | |||
| 1f0b64e4b8 | |||
| 2170957e21 | |||
| 47c28805bf | |||
| 52705b73f5 | |||
| cd4916f0e7 | |||
| 8f942e6e59 | |||
| 2fb08b1a82 | |||
| f4be9af77e | |||
| 9c06ad7c0e | |||
| 98b361c489 | |||
| 0f635409c2 | |||
| b4489cb423 | |||
| c0b23f5e20 | |||
| 9f5c27e117 | |||
| 9f02db13e2 | |||
| 3cd414a328 | |||
| e675506687 | |||
| b460bbc0c5 | |||
| cf48a979ae | |||
| 873fbf39c7 | |||
| 138ff31ef0 | |||
| d8e2154b8c | |||
| b3da8767ad | |||
| 1d74ef1255 | |||
| fc01a52823 | |||
| 70bb1b7d0a | |||
| 871e392987 | |||
| d84283e8cc | |||
| 9f1768c098 | |||
| 4babd8cdb7 | |||
| 312318201b | |||
| 3d9ac7137e | |||
| 838e230d54 | |||
| c26ac2e85d | |||
| ee3504f673 | |||
| 31256fae74 | |||
| 0c91f5a454 | |||
| 1ab21607a5 | |||
| 05fc7360c9 | |||
| 4c3211871d | |||
| 95f7d16801 | |||
| 400b2086ff | |||
| 7ef99eb8cf | |||
| ab4b2404c7 | |||
| d859c2875c | |||
| d14ff96dfa | |||
| bc53f961e7 | |||
| 91b2170fdb | |||
| fadadf8a64 | |||
| 65be624c43 | |||
| 3e5d4ba208 | |||
| dfea1a4f0d | |||
| 2409994d32 | |||
| 05dd1161a4 | |||
| 7ed61f310b | |||
| 15b8b5cdf0 | |||
| e6eb619c08 | |||
| 8826294d5d | |||
| cf2780d5c2 | |||
| 154bf3d9f5 | |||
| 768d683beb | |||
| 7f8aca035d | |||
| 8db1cdf462 | |||
| 48dc7ef01e | |||
| d782e76640 | |||
| e693c811f0 | |||
| d2f37aab56 | |||
| 8d08094ac3 | |||
| 8483f7adef | |||
| 4cffe42b56 | |||
| 84935b9585 | |||
| bf8f9164c0 | |||
| 64b5af4b58 | |||
| 959721b58a | |||
| 746fa817c1 | |||
| cfce223e9b | |||
| 375d8ffb56 | |||
| f89a47eff8 | |||
| 6a16596f78 | |||
| 7e22b75c28 | |||
| 7d547f726f | |||
| 75c9e608ce | |||
| fa8591ea3e | |||
| ae8c5e16e9 | |||
| 4fe95b0e61 | |||
| 5425233b37 | |||
| dcc561762b | |||
| 6307f8dd89 | |||
| 1a3e679835 | |||
| 6cfdd1bbae | |||
| 56a368facf | |||
| 4842ae967c | |||
| 6f62e05a96 | |||
| 6f23e028dd | |||
| ff013438d6 | |||
| 3e0941fb5b | |||
| e667c191f8 | |||
| 4566a44f14 | |||
| 8b2c0d969d | |||
| 32d337a934 | |||
| 4e1d36c8bc | |||
| 87c2aae85c | |||
| 8c6831f0fe | |||
| 5dbc672825 | |||
| b97d313c11 | |||
| ee0b0b4fc8 | |||
| 5b707aa422 | |||
| 519ba9e103 | |||
| c3a318e16a | |||
| faf63d076d | |||
| b1fe51cf0d | |||
| 651a97efc0 | |||
| cd8af2d23a | |||
| 8b75bb1772 | |||
| b672263e04 | |||
| 1b506c9639 | |||
| c463d72821 | |||
| 162053448e | |||
| 2034d440af | |||
| 70f7efe0fe | |||
| f1447db751 | |||
| 7fc01a94e2 | |||
| 568e72e7dc | |||
| 5d4db3dce3 | |||
| e5c7b50d85 | |||
| 9fda41582c | |||
| bddacfd5d7 | |||
| f3d9759375 | |||
| 1d776476b6 | |||
| fca9778023 | |||
| 482de4e370 | |||
| 91f43dd47f | |||
| fa9ba48533 | |||
| 141657a483 | |||
| c8be5a9ab3 | |||
| 507f737266 | |||
| 6d75761b94 | |||
| 52a9113c6d | |||
| 94f703e1d1 | |||
| d9ceef45de | |||
| 1ad332bbde | |||
| d23efa1ff2 | |||
| bf1ebf3b44 |
@@ -13,14 +13,14 @@ env:
|
||||
|
||||
jobs:
|
||||
Grunt:
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checking out code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Configuring node & npm
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
@@ -54,17 +54,17 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-22.04
|
||||
php: 7.2
|
||||
db: mysqli
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-22.04
|
||||
php: 7.4
|
||||
db: pgsql
|
||||
|
||||
steps:
|
||||
- name: Setting up DB mysql
|
||||
if: ${{ matrix.db == 'mysqli' }}
|
||||
uses: johanmeiring/mysql-action@tmpfs-patch
|
||||
uses: moodlehq/mysql-action@v1
|
||||
with:
|
||||
collation server: utf8mb4_bin
|
||||
mysql version: 5.7
|
||||
@@ -72,6 +72,8 @@ jobs:
|
||||
mysql user: test
|
||||
mysql password: test
|
||||
use tmpfs: true
|
||||
tmpfs size: '1024M'
|
||||
extra conf: --skip-log-bin
|
||||
|
||||
- name: Setting up DB pgsql
|
||||
if: ${{ matrix.db == 'pgsql' }}
|
||||
@@ -83,7 +85,7 @@ jobs:
|
||||
postgresql password: test
|
||||
|
||||
- name: Configuring git vars
|
||||
uses: rlespinasse/github-slug-action@v3.x
|
||||
uses: rlespinasse/github-slug-action@v4
|
||||
|
||||
- name: Setting up PHP ${{ matrix.php }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
@@ -92,7 +94,7 @@ jobs:
|
||||
coverage: none
|
||||
|
||||
- name: Checking out code from ${{ env.GITHUB_REF_SLUG }}
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setting up PHPUnit
|
||||
env:
|
||||
|
||||
+3
-1
@@ -81,7 +81,8 @@ install:
|
||||
sudo service mysql stop
|
||||
sudo mv /var/lib/mysql /mnt/ramdisk
|
||||
sudo ln -s /mnt/ramdisk/mysql /var/lib/mysql
|
||||
sudo service mysql restart
|
||||
echo -e "[mysqld]\nskip-log-bin" | sudo tee -a /etc/mysql/my.cnf
|
||||
sudo service mysql start
|
||||
fi
|
||||
- >
|
||||
if [ "$DB" = 'pgsql' ];
|
||||
@@ -92,6 +93,7 @@ install:
|
||||
sudo mv /var/lib/postgresql /mnt/ramdisk
|
||||
sudo ln -s /mnt/ramdisk/postgresql /var/lib/postgresql
|
||||
sudo service postgresql start 9.6
|
||||
sh -c 'until pg_isready -p 5432; do echo "Waiting for the DB to be up..."; sleep 2; done'
|
||||
fi
|
||||
- >
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
|
||||
+1
-1
@@ -142,7 +142,7 @@ module.exports = function(grunt) {
|
||||
const path = require('path');
|
||||
const tasks = {};
|
||||
const async = require('async');
|
||||
const DOMParser = require('xmldom').DOMParser;
|
||||
const DOMParser = require('@xmldom/xmldom').DOMParser;
|
||||
const xpath = require('xpath');
|
||||
const semver = require('semver');
|
||||
const watchman = require('fb-watchman');
|
||||
|
||||
+53
-35
@@ -1,48 +1,66 @@
|
||||
MOODLE TRADEMARKS POLICY
|
||||
========================
|
||||
|
||||
The word "Moodle" is trademarked in many countries around the world. The word
|
||||
was originally coined as an acronym: Modular Object-Oriented Dynamic Learning
|
||||
Environment. The trademark is owned by Martin Dougiamas, Founder and Director
|
||||
of Moodle.
|
||||
The Moodle Trademark
|
||||
--------------------
|
||||
|
||||
The law obligates trademark owners to police their marks and prevent the use of
|
||||
confusingly similar names by third parties. Through this policy we’d like to
|
||||
make it clear how Moodle-related projects, organisations, and people can use
|
||||
the Moodle trademark. We’d also like to be clear about how use of the word is
|
||||
restricted when used to promote commercial Moodle services. We do this to
|
||||
protect the very business model that allows us to continue developing Moodle
|
||||
for you.
|
||||
The brand and word "Moodle" has trade mark protection in many countries around
|
||||
the world. Various Moodle™ trade marks are owned by either Moodle Pty Ltd or
|
||||
one of its associated entities. The law obligates trade mark owners to police
|
||||
their trade marks and prevent the use of confusingly similar names by
|
||||
third parties.
|
||||
|
||||
Allowed uses
|
||||
------------
|
||||
We have created a more concise and thorough set of Trademark Guidelines
|
||||
(https://moodle.com/wp-content/uploads/2023/02/Moodle-Trademark-Guidelines-2023-4.pdf)
|
||||
to help you understand how to comply with Moodle's trade mark requirements.
|
||||
The Trademark Guidelines will also assist our Moodle Certified Partners and the
|
||||
community to understand some of the principles that inform Moodle's stance
|
||||
whenever potential infringement has been detected by us or our community.
|
||||
|
||||
The following uses don’t require any permission at all:
|
||||
We need to be clear about use of the word "Moodle" and how we restrict its use
|
||||
when promoting commercial Moodle™ branded services. The proper use of
|
||||
trade marks reliably assures consumers about the quality of the associated
|
||||
products or services and where they have been derived from. Because the
|
||||
Moodle LMS is made available under the open source ‘GNU General Public License'
|
||||
that permits you to modify the copyrighted software, the distribution of such
|
||||
modified software in combination with Moodle trade marks can potentially
|
||||
mislead others in the community. To be clear, the GNU GPL does not include an
|
||||
implied right or licence to use Moodle's trade marks.
|
||||
|
||||
* Referring to the software or the Moodle project.
|
||||
* Describing your own Moodle implementation (including within corporate
|
||||
settings).
|
||||
We do all this to protect the very business model that allows us to continue
|
||||
developing various Moodle software solutions for you.
|
||||
|
||||
Allowed uses of "Moodle"
|
||||
------------------------
|
||||
|
||||
The following uses don't require any permission at all:
|
||||
|
||||
* Referring to the software or the Moodle project as the "Moodle™ software"
|
||||
or the "Moodle™ project"
|
||||
* Describing your own implementation of the Moodle™ software platform
|
||||
(including within corporate settings).
|
||||
* Describing a Moodle-based community hub.
|
||||
* Describing some software you’ve made that integrates with Moodle
|
||||
(eg a Moodle integration feature on another system).
|
||||
* Describing certain software you've made that integrates with Moodle™
|
||||
branded software (e.g. a Moodle™ integration feature on another system).
|
||||
|
||||
Restricted uses
|
||||
---------------
|
||||
Restricted uses of "Moodle"
|
||||
---------------------------
|
||||
|
||||
The following uses are generally prohibited without explicit and direct
|
||||
permission being granted to you by Moodle Pty Ltd. We do this to protect the
|
||||
Moodle project from software and sites which could confuse people. Please
|
||||
contact us to ask for permission in writing.
|
||||
Moodle™ project from software and sites which could confuse people. If in
|
||||
doubt, reach out for clarification at <trademarks@moodle.com>
|
||||
|
||||
* You can’t use "Moodle" in the name of your software (including Mobile apps)
|
||||
* You can’t use "Moodle" in your company name
|
||||
* You can’t use "Moodle" in your domain name
|
||||
* You can’t use "Moodle" in advertising-related keywords (such as Adsense)
|
||||
* You can’t use "Moodle" to describe services around Moodle (such as hosting,
|
||||
training, support, consulting, course creation services, theme development,
|
||||
customisation, installation, integration and certification). This applies
|
||||
even if you do not charge for the services. Note that usually only Moodle
|
||||
Partners have this permission.
|
||||
|
||||
For information about the Moodle Partner Certification Mark as well as for how
|
||||
to contact us, please see <https://moodle.com/trademarks/>.
|
||||
* You can't use the word "Moodle" or any Moodle logos for commercial purposes
|
||||
without prior written permission from Moodle HQ.
|
||||
* You can't use "Moodle" in the name of your software (including Mobile apps).
|
||||
* You can't use "Moodle" in your company name.
|
||||
* You can't use "Moodle" in your domain name.
|
||||
* You can't use "Moodle" in advertising-related keywords (such as Adsense).
|
||||
* You can't use "Moodle" to describe services around Moodle
|
||||
(such as hosting, training, support, consulting, course creation services,
|
||||
theme development, customisation, installation, integration, analytics
|
||||
and certification) that cause consumers to be confused that you are
|
||||
associated with Moodle HQ if you are not.
|
||||
This applies even if you do not charge for the services.
|
||||
Note that usually only Moodle Partners have this permission.
|
||||
|
||||
+191
-5
@@ -3371,6 +3371,7 @@
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.3.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_81" message="unsupportedphpversion81" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
@@ -3557,6 +3558,7 @@
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.3.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_81" message="unsupportedphpversion81" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
@@ -3781,11 +3783,6 @@
|
||||
<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" />
|
||||
@@ -3927,4 +3924,193 @@
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="4.2" requires="3.11.8">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="10.6.7" />
|
||||
<VENDOR name="mysql" version="8.0" />
|
||||
<VENDOR name="postgres" version="13" />
|
||||
<VENDOR name="mssql" version="14.0" />
|
||||
<VENDOR name="oracle" version="19" />
|
||||
</DATABASE>
|
||||
<PHP version="8.0.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="mbstringrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
<PHP_EXTENSION name="sodium" level="required"/>
|
||||
<PHP_EXTENSION name="exif" level="optional"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="sixtyfourbitsrequired" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_max_input_vars" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_admin_dir_usage" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="iscustomadminwarnings" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_xmlrpc_usage" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mod_assignment" level="required">
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
define("tool_behat/steps",["exports","core/ajax","core/templates","core/pending"],(function(_exports,_ajax,_templates,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_ajax=_interopRequireDefault(_ajax),_templates=_interopRequireDefault(_templates),_pending=_interopRequireDefault(_pending);
|
||||
/**
|
||||
* Enhancements for the step definitions page.
|
||||
*
|
||||
* @module tool_behat/steps
|
||||
* @copyright 2022 Catalyst IT EU
|
||||
* @author Mark Johnson <mark.johnson@catalyst-eu.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
var getRequiredFieldsContent=function(entityData){var _entityData$required;return null!==(_entityData$required=entityData.required)&&void 0!==_entityData$required&&_entityData$required.length?_templates.default.renderForPromise("tool_behat/steprequiredfields",{fields:entityData.required}):Promise.resolve({html:"",js:""})};_exports.init=function(){var fn,_ref;document.addEventListener("change",(fn=regeneratorRuntime.mark((function _callee(e){var entityElement,stepElement,pendingPromise,entityData,_yield$getRequiredFie,html,js,stepRequiredFields;return regeneratorRuntime.wrap((function(_context){for(;;)switch(_context.prev=_context.next){case 0:if(entityElement=e.target.closest(".entities"),stepElement=e.target.closest(".stepcontent"),entityElement&&stepElement){_context.next=4;break}return _context.abrupt("return");case 4:return pendingPromise=new _pending.default("tool_behat/steps:change"),_context.next=7,entityType=e.target.value,_ajax.default.call([{methodname:"tool_behat_get_entity_generator",args:{entitytype:entityType}}])[0];case 7:return entityData=_context.sent,_context.next=10,getRequiredFieldsContent(entityData);case 10:if(_yield$getRequiredFie=_context.sent,html=_yield$getRequiredFie.html,js=_yield$getRequiredFie.js,!(stepRequiredFields=stepElement.querySelector(".steprequiredfields"))){_context.next=19;break}return _context.next=17,_templates.default.replaceNode(stepRequiredFields,html,js);case 17:_context.next=21;break;case 19:return _context.next=21,_templates.default.appendNodeContents(stepElement,html,js);case 21:pendingPromise.resolve();case 22:case"end":return _context.stop()}var entityType}),_callee)})),_ref=function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))},function(_x){return _ref.apply(this,arguments)}))}}));
|
||||
|
||||
//# sourceMappingURL=steps.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"steps.min.js","sources":["../src/steps.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\nimport Ajax from 'core/ajax';\nimport Templates from 'core/templates';\nimport PendingJS from 'core/pending';\n\n/**\n * Enhancements for the step definitions page.\n *\n * @module tool_behat/steps\n * @copyright 2022 Catalyst IT EU\n * @author Mark Johnson <mark.johnson@catalyst-eu.net>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Call the get_entity_generator web service function\n *\n * Takes the name of an entity generator and returns an object containing a list of the required fields.\n *\n * @param {String} entityType\n * @returns {Promise}\n */\nconst getGeneratorEntities = (entityType) => Ajax.call([{\n methodname: 'tool_behat_get_entity_generator',\n args: {entitytype: entityType}\n}])[0];\n\n/**\n * Render HTML for required fields\n *\n * Takes the entity data returned from getGeneratorEntities and renders the HTML to display the required fields.\n *\n * @param {String} entityData\n * @return {Promise}\n */\nconst getRequiredFieldsContent = (entityData) => {\n if (!entityData.required?.length) {\n return Promise.resolve({\n html: '',\n js: ''\n });\n }\n return Templates.renderForPromise('tool_behat/steprequiredfields', {fields: entityData.required});\n};\n\nexport const init = () => {\n // When an entity is selected in the \"the following exist\" step, fetch and display the required fields.\n document.addEventListener('change', async(e) => {\n const entityElement = e.target.closest('.entities');\n const stepElement = e.target.closest('.stepcontent');\n if (!entityElement || !stepElement) {\n return;\n }\n\n const pendingPromise = new PendingJS('tool_behat/steps:change');\n\n const entityData = await getGeneratorEntities(e.target.value);\n const {html, js} = await getRequiredFieldsContent(entityData);\n\n const stepRequiredFields = stepElement.querySelector('.steprequiredfields');\n if (stepRequiredFields) {\n await Templates.replaceNode(stepRequiredFields, html, js);\n } else {\n await Templates.appendNodeContents(stepElement, html, js);\n }\n pendingPromise.resolve();\n });\n};\n"],"names":["getRequiredFieldsContent","entityData","required","_entityData$required","length","Templates","renderForPromise","fields","Promise","resolve","html","js","document","addEventListener","e","entityElement","target","closest","stepElement","pendingPromise","PendingJS","entityType","value","Ajax","call","methodname","args","entitytype","stepRequiredFields","querySelector","replaceNode","appendNodeContents"],"mappings":";;;;;;;;;IAiDMA,yBAA2B,SAACC,yEACzBA,WAAWC,0CAAXC,qBAAqBC,OAMnBC,mBAAUC,iBAAiB,gCAAiC,CAACC,OAAQN,WAAWC,WAL5EM,QAAQC,QAAQ,CACnBC,KAAM,GACNC,GAAI,oBAMI,uBAEhBC,SAASC,iBAAiB,sCAAU,iBAAMC,oNAChCC,cAAgBD,EAAEE,OAAOC,QAAQ,aACjCC,YAAcJ,EAAEE,OAAOC,QAAQ,gBAChCF,eAAkBG,kFAIjBC,eAAiB,IAAIC,iBAAU,2CAhCfC,WAkCwBP,EAAEE,OAAOM,MAlClBC,cAAKC,KAAK,CAAC,CACpDC,WAAY,kCACZC,KAAM,CAACC,WAAYN,eACnB,iBA+BUpB,0CACmBD,yBAAyBC,2DAA3CS,2BAAAA,KAAMC,yBAAAA,KAEPiB,mBAAqBV,YAAYW,cAAc,wEAE3CxB,mBAAUyB,YAAYF,mBAAoBlB,KAAMC,mEAEhDN,mBAAU0B,mBAAmBb,YAAaR,KAAMC,YAE1DQ,eAAeV,mDA3CM,IAACY"}
|
||||
@@ -0,0 +1,82 @@
|
||||
// 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/>.
|
||||
|
||||
import Ajax from 'core/ajax';
|
||||
import Templates from 'core/templates';
|
||||
import PendingJS from 'core/pending';
|
||||
|
||||
/**
|
||||
* Enhancements for the step definitions page.
|
||||
*
|
||||
* @module tool_behat/steps
|
||||
* @copyright 2022 Catalyst IT EU
|
||||
* @author Mark Johnson <mark.johnson@catalyst-eu.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Call the get_entity_generator web service function
|
||||
*
|
||||
* Takes the name of an entity generator and returns an object containing a list of the required fields.
|
||||
*
|
||||
* @param {String} entityType
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const getGeneratorEntities = (entityType) => Ajax.call([{
|
||||
methodname: 'tool_behat_get_entity_generator',
|
||||
args: {entitytype: entityType}
|
||||
}])[0];
|
||||
|
||||
/**
|
||||
* Render HTML for required fields
|
||||
*
|
||||
* Takes the entity data returned from getGeneratorEntities and renders the HTML to display the required fields.
|
||||
*
|
||||
* @param {String} entityData
|
||||
* @return {Promise}
|
||||
*/
|
||||
const getRequiredFieldsContent = (entityData) => {
|
||||
if (!entityData.required?.length) {
|
||||
return Promise.resolve({
|
||||
html: '',
|
||||
js: ''
|
||||
});
|
||||
}
|
||||
return Templates.renderForPromise('tool_behat/steprequiredfields', {fields: entityData.required});
|
||||
};
|
||||
|
||||
export const init = () => {
|
||||
// When an entity is selected in the "the following exist" step, fetch and display the required fields.
|
||||
document.addEventListener('change', async(e) => {
|
||||
const entityElement = e.target.closest('.entities');
|
||||
const stepElement = e.target.closest('.stepcontent');
|
||||
if (!entityElement || !stepElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pendingPromise = new PendingJS('tool_behat/steps:change');
|
||||
|
||||
const entityData = await getGeneratorEntities(e.target.value);
|
||||
const {html, js} = await getRequiredFieldsContent(entityData);
|
||||
|
||||
const stepRequiredFields = stepElement.querySelector('.steprequiredfields');
|
||||
if (stepRequiredFields) {
|
||||
await Templates.replaceNode(stepRequiredFields, html, js);
|
||||
} else {
|
||||
await Templates.appendNodeContents(stepElement, html, js);
|
||||
}
|
||||
pendingPromise.resolve();
|
||||
});
|
||||
};
|
||||
@@ -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/>.
|
||||
|
||||
/**
|
||||
* Return data about an entity generator.
|
||||
*
|
||||
* @package tool_behat
|
||||
* @copyright 2022 onwards Catalyst IT EU {@link https://catalyst-eu.net}
|
||||
* @author Mark Johnson <mark.johnson@catalyst-eu.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_behat\external;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
|
||||
/**
|
||||
* External function for getting properties of entity generators.
|
||||
*/
|
||||
class get_entity_generator extends \external_api {
|
||||
|
||||
/**
|
||||
* Define parameters for external function.
|
||||
*
|
||||
* The parameter is either in the format 'entity' or 'component_name > entity'. There is no appropriate param type for a
|
||||
* string like this containing angle brackets, so we will do PARAM_RAW. The value will be parsed by
|
||||
* behat_data_generators::parse_entity_type, which validates the format of the parameter and throws an exception if it is not
|
||||
* correct.
|
||||
*
|
||||
* @return \external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): \external_function_parameters {
|
||||
return new \external_function_parameters([
|
||||
'entitytype' => new \external_value(PARAM_RAW, 'Entity type that can be created by a generator.'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of the required fields for a given entity type.
|
||||
*
|
||||
* @param string $entitytype
|
||||
* @return array
|
||||
*/
|
||||
public static function execute(string $entitytype): array {
|
||||
global $CFG;
|
||||
|
||||
// Ensure we can load Behat and Facebook namespaces in behat libraries.
|
||||
require_once("{$CFG->dirroot}/vendor/autoload.php");
|
||||
require_once("{$CFG->libdir}/tests/behat/behat_data_generators.php");
|
||||
|
||||
$params = self::validate_parameters(self::execute_parameters(), ['entitytype' => $entitytype]);
|
||||
$context = \context_system::instance();
|
||||
self::validate_context($context);
|
||||
require_capability('moodle/site:config', $context);
|
||||
|
||||
$generators = new \behat_data_generators();
|
||||
$entity = $generators->get_entity($params['entitytype']);
|
||||
return ['required' => $entity['required']];
|
||||
}
|
||||
|
||||
/**
|
||||
* Define return values.
|
||||
*
|
||||
* Return required fields
|
||||
*
|
||||
* @return \external_single_structure
|
||||
*/
|
||||
public static function execute_returns(): \external_single_structure {
|
||||
return new \external_single_structure([
|
||||
'required' => new \external_multiple_structure(
|
||||
new \external_value(PARAM_TEXT, 'Required field'),
|
||||
'Required fields',
|
||||
VALUE_OPTIONAL
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Define web service functions for tool_behat
|
||||
*
|
||||
* @package tool_behat
|
||||
* @copyright 2022 onwards Catalyst IT EU {@link https://catalyst-eu.net}
|
||||
* @author Mark Johnson <mark.johnson@catalyst-eu.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$functions = [
|
||||
'tool_behat_get_entity_generator' => [
|
||||
'classname' => 'tool_behat\\external\\get_entity_generator',
|
||||
'methodname' => 'execute',
|
||||
'description' => 'Get the generator details for an entity',
|
||||
'type' => 'read',
|
||||
'ajax' => true,
|
||||
'capabilities' => 'moodle/site:config'
|
||||
]
|
||||
];
|
||||
@@ -56,6 +56,7 @@ if ($components) {
|
||||
$form = new steps_definitions_form(null, array('components' => $componentswithsteps));
|
||||
|
||||
// Output contents.
|
||||
$PAGE->requires->js_call_amd('tool_behat/steps', 'init');
|
||||
$renderer = $PAGE->get_renderer('tool_behat');
|
||||
echo $renderer->render_stepsdefinitions($steps, $form);
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/behat/classes/behat_generator_base.php');
|
||||
|
||||
/**
|
||||
* Renderer for behat tool web features
|
||||
*
|
||||
@@ -106,6 +108,32 @@ class tool_behat_renderer extends plugin_renderer_base {
|
||||
},
|
||||
$stepsdefinitions
|
||||
);
|
||||
|
||||
$elementstrings = [];
|
||||
$count = 1;
|
||||
$stepsdefinitions = preg_replace_callback('/(the following ")ELEMENT\d?_STRING(" exist:)/',
|
||||
function($matches) use (&$elementstrings, &$count) {
|
||||
// Replace element type arguments with a user-friendly select.
|
||||
if (empty($elementstrings)) {
|
||||
$behatgenerators = new behat_data_generators();
|
||||
$componententities = $behatgenerators->get_all_entities();
|
||||
ksort($componententities);
|
||||
$elementstrings = [];
|
||||
foreach ($componententities as $component => $entities) {
|
||||
asort($entities);
|
||||
foreach ($entities as $entity) {
|
||||
$string = ($component === 'core') ? $entity : $component . ' > ' . $entity;
|
||||
$elementstrings[$string] = $string;
|
||||
}
|
||||
}
|
||||
}
|
||||
$select = html_writer::select($elementstrings, 'entities' . $count, '', ['' => 'choosedots'],
|
||||
['class' => 'entities']);
|
||||
$count++;
|
||||
return $matches[1] . $select . $matches[2];
|
||||
},
|
||||
$stepsdefinitions
|
||||
);
|
||||
}
|
||||
|
||||
// Steps definitions.
|
||||
|
||||
@@ -29,3 +29,9 @@
|
||||
#page-admin-tool-behat-index .steps-definitions .stepregex {
|
||||
color: #060;
|
||||
}
|
||||
|
||||
#page-admin-tool-behat-index .steprequiredfields {
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{{!
|
||||
This file is part of Moodle - https://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template tool_behat/steprequiredfields
|
||||
|
||||
Display a Gherkin-style table row showing required columns for a step.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"fields": [
|
||||
"user",
|
||||
"role",
|
||||
"context"
|
||||
]
|
||||
}
|
||||
}}
|
||||
<pre class="steprequiredfields">
|
||||
|{{#fields}} {{{.}}} |{{/fields}}
|
||||
</pre>
|
||||
@@ -30,10 +30,9 @@ Feature: Verify that all form fields values can be get and set
|
||||
| student2 | G2 |
|
||||
| student3 | G2 |
|
||||
And the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro | firstpagetitle | wikimode | visible |
|
||||
| wiki | C1 | wiki1 | Test this one | Test this one | Test this one | collaborative | 0 |
|
||||
And I log in as "admin"
|
||||
And I am on "Course 1" course homepage
|
||||
| activity | course | idnumber | name | firstpagetitle | wikimode | visible |
|
||||
| wiki | C1 | wiki1 | Test this one | Test this one | collaborative | 0 |
|
||||
And I am on the "Course 1" course page logged in as admin
|
||||
And I navigate to "Reset" in current page administration
|
||||
# Select (multi-select) - Checking "the select box should contain".
|
||||
And I expand all fieldsets
|
||||
@@ -61,16 +60,14 @@ Feature: Verify that all form fields values can be get and set
|
||||
# Checkbox - Checking "I set the field" and "The field matches value" ticked.
|
||||
And I set the field "Force format" to "1"
|
||||
And I press "Save and return to course"
|
||||
And I am on the "Test this one" "wiki activity" page
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test this one" "wiki activity editing" page
|
||||
And I expand all fieldsets
|
||||
And the field "Force format" matches value "1"
|
||||
And the field "Force format" does not match value ""
|
||||
# Checkbox - Checking "I set the field" and "The field matches value" unticked.
|
||||
And I set the field "Force format" to ""
|
||||
And I press "Save and return to course"
|
||||
And I am on the "Test this one" "wiki activity" page
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test this one" "wiki activity editing" page
|
||||
And I expand all fieldsets
|
||||
And the field "Force format" matches value ""
|
||||
And the field "Force format" does not match value "1"
|
||||
@@ -78,8 +75,7 @@ Feature: Verify that all form fields values can be get and set
|
||||
And I set the following fields to these values:
|
||||
| Force format | 1 |
|
||||
And I press "Save and return to course"
|
||||
And I am on the "Test this one" "wiki activity" page
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test this one" "wiki activity editing" page
|
||||
And I expand all fieldsets
|
||||
And the following fields match these values:
|
||||
| Force format | 1 |
|
||||
@@ -89,8 +85,7 @@ Feature: Verify that all form fields values can be get and set
|
||||
And I set the following fields to these values:
|
||||
| Force format | |
|
||||
And I press "Save and return to course"
|
||||
And I am on the "Test this one" "wiki activity" page
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test this one" "wiki activity editing" page
|
||||
And I expand all fieldsets
|
||||
And the following fields match these values:
|
||||
| Force format | |
|
||||
@@ -136,12 +131,13 @@ Feature: Verify that all form fields values can be get and set
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And the field "Course ID number" matches value "Course id number"
|
||||
|
||||
Scenario: with JS disabled all form fields getters and setters works as expected
|
||||
|
||||
@javascript
|
||||
Scenario: with JS enabled all form fields getters and setters works as expected
|
||||
Then I am on "Course 1" course homepage
|
||||
And I navigate to "Users > Groups" in current page administration
|
||||
Given the following "activities" exist:
|
||||
| activity | course | name |
|
||||
| lesson | C1 | Test lesson |
|
||||
When I am on "Course 1" course homepage
|
||||
Then I navigate to "Users > Groups" in current page administration
|
||||
# Select (multi-select & AJAX) - Checking "I set the field" and "select box should contain".
|
||||
And I set the field "groups" to "Group 2"
|
||||
And the "members" select box should contain "Student 2 (s2@example.com)"
|
||||
@@ -152,11 +148,8 @@ Feature: Verify that all form fields values can be get and set
|
||||
And the "members" select box should contain "Student 2 (s2@example.com)"
|
||||
And the "members" select box should not contain "Student 3 (s3@example.com)"
|
||||
# Checkbox (AJAX) - Checking "I set the field" and "I set the following fields to these values".
|
||||
And I am on "Course 1" course homepage
|
||||
And I add a "Lesson" to section "1"
|
||||
And I am on the "Test lesson" "lesson activity editing" page
|
||||
And I set the following fields to these values:
|
||||
| Name | Test lesson |
|
||||
| Description | Test lesson description |
|
||||
| available[enabled] | 1 |
|
||||
And I set the field "deadline[enabled]" to "1"
|
||||
# Checkbox (AJAX) - Checking "the field matches value" before saving.
|
||||
|
||||
@@ -13,6 +13,10 @@ Feature: List the system steps definitions
|
||||
Scenario: Accessing the list
|
||||
Then I should see "Step definitions"
|
||||
And I should not see "There aren't steps definitions matching this filter"
|
||||
And I should not see "the following \"ELEMENT_STRING\" exist:"
|
||||
And "entities1" "select" should exist
|
||||
And the "entities1" select box should contain "users"
|
||||
And the "entities1" select box should contain "mod_assign > submissions"
|
||||
|
||||
@javascript
|
||||
Scenario: Filtering by type
|
||||
@@ -34,3 +38,11 @@ Feature: List the system steps definitions
|
||||
Then I should not see "There aren't steps definitions matching this filter"
|
||||
And I should see "Checks the provided element and selector type exists in the current page."
|
||||
And I should see "Checks that an element and selector type exists in another element and selector type on the current page."
|
||||
|
||||
@javascript
|
||||
Scenario: Get required fields
|
||||
Given I set the field "entities1" to "users"
|
||||
And I should see "| username |"
|
||||
When I set the field "entities1" to "mod_quiz > user overrides"
|
||||
Then I should not see "| username |"
|
||||
And I should see "| quiz | user |"
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Unit tests for get_entity_generator web service
|
||||
*
|
||||
* @package tool_behat
|
||||
* @copyright 2022 onwards Catalyst IT EU {@link https://catalyst-eu.net}
|
||||
* @author Mark Johnson <mark.johnson@catalyst-eu.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_behat\external;
|
||||
|
||||
/**
|
||||
* Tests for get_entity_generator web service
|
||||
*
|
||||
* @covers \tool_behat\external\get_entity_generator
|
||||
*/
|
||||
class get_entity_generator_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Log in as admin
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUp(): void {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the generator for a core entity.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_execute_core_entity(): void {
|
||||
$generator = get_entity_generator::execute('users');
|
||||
$this->assertEquals(['required' => ['username']], $generator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the generator for the plugin entity.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_execute_plugin_entity(): void {
|
||||
$generator = get_entity_generator::execute('mod_book > chapters');
|
||||
$this->assertEquals(['required' => ['book', 'title', 'content']], $generator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the generator for an entity with no required fields.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_execute_no_requried(): void {
|
||||
$generator = get_entity_generator::execute('mod_forum > posts');
|
||||
$this->assertEquals(['required' => []], $generator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to get the generator for a core entity that does not exist.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_execute_invalid_entity(): void {
|
||||
$this->expectException('coding_exception');
|
||||
get_entity_generator::execute('foo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to get a generator form a plugin that does not exist.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_execute_invalid_plugin(): void {
|
||||
$this->expectException('coding_exception');
|
||||
get_entity_generator::execute('foo > bar');
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to get a generator for an entity that does not exist, from a plugin that does.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_execute_invalid_plugin_entity(): void {
|
||||
$this->expectException('coding_exception');
|
||||
get_entity_generator::execute('mod_book > bar');
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2020061500; // The current plugin version (Date: YYYYMMDDXX)
|
||||
$plugin->version = 2020061501; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2020060900; // Requires this Moodle version
|
||||
$plugin->component = 'tool_behat'; // Full name of the plugin (used for diagnostics)
|
||||
|
||||
@@ -79,6 +79,19 @@ class locale {
|
||||
* @return string|false Returns the new current locale, or FALSE on error.
|
||||
*/
|
||||
protected function set_locale(int $category = LC_ALL, string $locale = '0') {
|
||||
return setlocale($category, $locale);
|
||||
if (strlen($locale) <= 255 || PHP_OS_FAMILY === 'BSD' || PHP_OS_FAMILY === 'Darwin') {
|
||||
// We can set the whole locale all together.
|
||||
return setlocale($category, $locale);
|
||||
}
|
||||
|
||||
// Too long locale with linux or windows, let's split it into known and supported categories.
|
||||
$split = explode(';', $locale);
|
||||
foreach ($split as $element) {
|
||||
[$category, $value] = explode('=', $element);
|
||||
if (defined($category)) { // Only if the category exists, there are OS differences.
|
||||
setlocale(constant($category), $value);
|
||||
}
|
||||
}
|
||||
return setlocale(LC_ALL, 0); // Finally, return the complete configured locale.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
/**
|
||||
* Tests for \tool_langimport\locale class.
|
||||
*
|
||||
* @coversDefaultClass \tool_langimport\locale
|
||||
* @copyright 2018 Université Rennes 2 {@link https://www.univ-rennes2.fr}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@@ -34,6 +35,7 @@ class locale_testcase extends \advanced_testcase {
|
||||
/**
|
||||
* Test that \tool_langimport\locale::check_locale_availability() works as expected.
|
||||
*
|
||||
* @covers ::check_locale_availability
|
||||
* @return void
|
||||
*/
|
||||
public function test_check_locale_availability() {
|
||||
@@ -68,4 +70,100 @@ class locale_testcase extends \advanced_testcase {
|
||||
$this->expectException(coding_exception::class);
|
||||
$locale->check_locale_availability('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test \tool_langimport\locale::set_locale() own logic.
|
||||
*
|
||||
* We have to explicitly test set_locale() own logic and results,
|
||||
* that effectively sets the current locale, so we need to restore
|
||||
* the original locale after every test (ugly, from a purist unit test
|
||||
* point of view, but needed).
|
||||
*
|
||||
* @dataProvider set_locale_provider
|
||||
* @covers ::set_locale
|
||||
*
|
||||
* @param string $set locale string to be set.
|
||||
* @param string $ret expected results returned after setting the locale.
|
||||
*/
|
||||
public function test_set_locale(string $set, string $ret) {
|
||||
// Make set_locale() public.
|
||||
$loc = new \tool_langimport\locale();
|
||||
$rc = new \ReflectionClass(\tool_langimport\locale::class);
|
||||
$rm = $rc->getMethod('set_locale');
|
||||
$rm->setAccessible(true);
|
||||
|
||||
// Capture current locale for later restore (funnily, using the set_locale() method itself.
|
||||
$originallocale = $rm->invokeArgs($loc, [LC_ALL, 0]);
|
||||
|
||||
// Assert we get the locale defined as expected.
|
||||
$this->assertEquals($ret, $rm->invokeArgs($loc, [LC_ALL, $set]));
|
||||
|
||||
// We have finished, restore the original locale, so this doesn't affect other tests at distance.
|
||||
// (again, funnily, using the very same set_locale() method).
|
||||
$rm->invokeArgs($loc, [LC_ALL, $originallocale]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for test_set_locale().
|
||||
*
|
||||
* Provides a locale to be set (as 'set') and a expected return value (as 'ret'). Note that
|
||||
* some of the locales are OS dependent, so only the ones matching the OS will be provided.
|
||||
*
|
||||
* We make extensive use of the en_AU.UTF-8/English_Australia.1252 locale that is mandatory to
|
||||
* be installed in any system running PHPUnit tests.
|
||||
*/
|
||||
public function set_locale_provider(): array {
|
||||
// Let's list the allowed categories by OS.
|
||||
$bsdallowed = ['LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME'];
|
||||
$winallowed = ['LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME'];
|
||||
$linuxallowed = [
|
||||
'LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME',
|
||||
'LC_PAPER', 'LC_NAME', 'LC_ADDRESS', 'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION'
|
||||
];
|
||||
|
||||
// The base locale name is also OS dependent.
|
||||
$baselocale = get_string('locale', 'langconfig');
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$baselocale = get_string('localewin', 'langconfig');
|
||||
}
|
||||
|
||||
// Here we'll go accumulating cases to be provided.
|
||||
$cases = [];
|
||||
|
||||
// First, the simplest case, just pass a locale name, without categories.
|
||||
$cases['rawlocale'] = [
|
||||
'set' => $baselocale,
|
||||
'ret' => $baselocale,
|
||||
];
|
||||
|
||||
// Now, let's fill ALL LC categories, we should get back the locale name if all them are set with same value.
|
||||
// Note that this case is the one that, under Linux only, covers the changes performed to the set_locale() method.
|
||||
// Pick the correct categories depending on the OS.
|
||||
$oscategories = $bsdallowed; // Default to BSD/Dawrwin ones because they are the standard 6 supported by PHP.
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$oscategories = $winallowed;
|
||||
} else if (PHP_OS_FAMILY === 'Linux') {
|
||||
$oscategories = $linuxallowed;
|
||||
}
|
||||
|
||||
$localestr = '';
|
||||
foreach ($oscategories as $category) {
|
||||
// Format is different by OS too, so let build the string conditionally.
|
||||
if (PHP_OS_FAMILY === 'BSD' || PHP_OS_FAMILY === 'Darwin') {
|
||||
// BSD uses slashes (/) separated list of the 6 values in exact order.
|
||||
$localestr .= '/' . $baselocale;
|
||||
} else {
|
||||
// Linux/Windows use semicolon (;) separated list of category=value pairs.
|
||||
$localestr .= ';' . $category . '=' . $baselocale;
|
||||
}
|
||||
}
|
||||
$cases['allcategories'] = [
|
||||
'set' => trim($localestr, ';/'),
|
||||
'ret' => $baselocale,
|
||||
];
|
||||
|
||||
// Return all the built cases.
|
||||
return $cases;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,3 +33,27 @@ Feature: Licence manager
|
||||
And "This is the site default licence" "icon" should exist in the "cc" "table_row"
|
||||
And "Enable licence" "icon" should not exist in the "cc" "table_row"
|
||||
And "This is the site default licence" "icon" should not exist in the "public" "table_row"
|
||||
|
||||
@javascript @_file_upload
|
||||
Scenario Outline: User licence preference is remembered depending of setting value
|
||||
Given the following config values are set as admin:
|
||||
| sitedefaultlicense | cc |
|
||||
| rememberuserlicensepref | <rememberuserlicensepref> |
|
||||
And I log in as "admin"
|
||||
And I follow "Manage private files..."
|
||||
And I follow "Add..."
|
||||
And I follow "Upload a file"
|
||||
And the field with xpath "//select[@name='license']" matches value "Creative Commons"
|
||||
And I click on "Close" "button" in the "File picker" "dialogue"
|
||||
When I upload "lib/tests/fixtures/empty.txt" file to "Files" filemanager as:
|
||||
| Save as | empty_copy.txt |
|
||||
| license | Public domain |
|
||||
And I press "Save changes"
|
||||
And I follow "Manage private files..."
|
||||
And I follow "Add..."
|
||||
Then the field with xpath "//select[@name='license']" matches value "<expectedlicence>"
|
||||
|
||||
Examples:
|
||||
| rememberuserlicensepref | expectedlicence |
|
||||
| 0 | Creative Commons |
|
||||
| 1 | Public domain |
|
||||
|
||||
@@ -32,8 +32,7 @@ class logstore_database_store_testcase extends advanced_testcase {
|
||||
* Tests log writing.
|
||||
*
|
||||
* @param bool $jsonformat True to test with JSON format
|
||||
* @dataProvider test_log_writing_provider
|
||||
* @throws moodle_exception
|
||||
* @dataProvider log_writing_provider
|
||||
*/
|
||||
public function test_log_writing(bool $jsonformat) {
|
||||
global $DB, $CFG;
|
||||
@@ -251,9 +250,9 @@ class logstore_database_store_testcase extends advanced_testcase {
|
||||
* Returns different JSON format settings so the test can be run with JSON format either on or
|
||||
* off.
|
||||
*
|
||||
* @return [bool] Array of true/false
|
||||
* @return bool[] Array of true/false
|
||||
*/
|
||||
public static function test_log_writing_provider(): array {
|
||||
public static function log_writing_provider(): array {
|
||||
return [
|
||||
[false],
|
||||
[true]
|
||||
@@ -310,17 +309,16 @@ class logstore_database_store_testcase extends advanced_testcase {
|
||||
$logmanager = get_log_manager();
|
||||
$allreports = \core_component::get_plugin_list('report');
|
||||
|
||||
$supportedreports = array(
|
||||
'report_log' => '/report/log',
|
||||
'report_loglive' => '/report/loglive'
|
||||
);
|
||||
|
||||
// Make sure all supported reports are installed.
|
||||
$expectedreports = array_keys(array_intersect_key($allreports, $supportedreports));
|
||||
$reports = $logmanager->get_supported_reports('logstore_database');
|
||||
$reports = array_keys($reports);
|
||||
$expectedreports = array_intersect_key([
|
||||
'log' => 'report_log',
|
||||
'loglive' => 'report_loglive',
|
||||
], $allreports);
|
||||
|
||||
$supportedreports = $logmanager->get_supported_reports('logstore_database');
|
||||
|
||||
foreach ($expectedreports as $expectedreport) {
|
||||
$this->assertContains($expectedreport, $reports);
|
||||
$this->assertArrayHasKey($expectedreport, $supportedreports);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,20 +257,19 @@ class logstore_legacy_store_testcase extends advanced_testcase {
|
||||
$logmanager = get_log_manager();
|
||||
$allreports = \core_component::get_plugin_list('report');
|
||||
|
||||
$supportedreports = array(
|
||||
'report_log' => '/report/log',
|
||||
'report_loglive' => '/report/loglive',
|
||||
'report_outline' => '/report/outline',
|
||||
'report_participation' => '/report/participation',
|
||||
'report_stats' => '/report/stats'
|
||||
);
|
||||
|
||||
// Make sure all supported reports are installed.
|
||||
$expectedreports = array_keys(array_intersect_key($allreports, $supportedreports));
|
||||
$reports = $logmanager->get_supported_reports('logstore_legacy');
|
||||
$reports = array_keys($reports);
|
||||
$expectedreports = array_intersect_key([
|
||||
'log' => 'report_log',
|
||||
'loglive' => 'report_loglive',
|
||||
'outline' => 'report_outline',
|
||||
'participation' => 'report_participation',
|
||||
'stats' => 'report_stats'
|
||||
], $allreports);
|
||||
|
||||
$supportedreports = $logmanager->get_supported_reports('logstore_legacy');
|
||||
|
||||
foreach ($expectedreports as $expectedreport) {
|
||||
$this->assertContains($expectedreport, $reports);
|
||||
$this->assertArrayHasKey($expectedreport, $supportedreports);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,8 +37,7 @@ class logstore_standard_store_testcase extends advanced_testcase {
|
||||
* Tests log writing.
|
||||
*
|
||||
* @param bool $jsonformat True to test with JSON format
|
||||
* @dataProvider test_log_writing_provider
|
||||
* @throws moodle_exception
|
||||
* @dataProvider log_writing_provider
|
||||
*/
|
||||
public function test_log_writing(bool $jsonformat) {
|
||||
global $DB;
|
||||
@@ -222,9 +221,9 @@ class logstore_standard_store_testcase extends advanced_testcase {
|
||||
* Returns different JSON format settings so the test can be run with JSON format either on or
|
||||
* off.
|
||||
*
|
||||
* @return [bool] Array of true/false
|
||||
* @return bool[] Array of true/false
|
||||
*/
|
||||
public static function test_log_writing_provider(): array {
|
||||
public static function log_writing_provider(): array {
|
||||
return [
|
||||
[false],
|
||||
[true]
|
||||
@@ -238,20 +237,19 @@ class logstore_standard_store_testcase extends advanced_testcase {
|
||||
$logmanager = get_log_manager();
|
||||
$allreports = \core_component::get_plugin_list('report');
|
||||
|
||||
$supportedreports = array(
|
||||
'report_log' => '/report/log',
|
||||
'report_loglive' => '/report/loglive',
|
||||
'report_outline' => '/report/outline',
|
||||
'report_participation' => '/report/participation',
|
||||
'report_stats' => '/report/stats'
|
||||
);
|
||||
|
||||
// Make sure all supported reports are installed.
|
||||
$expectedreports = array_keys(array_intersect_key($allreports, $supportedreports));
|
||||
$reports = $logmanager->get_supported_reports('logstore_standard');
|
||||
$reports = array_keys($reports);
|
||||
$expectedreports = array_intersect_key([
|
||||
'log' => 'report_log',
|
||||
'loglive' => 'report_loglive',
|
||||
'outline' => 'report_outline',
|
||||
'participation' => 'report_participation',
|
||||
'stats' => 'report_stats'
|
||||
], $allreports);
|
||||
|
||||
$supportedreports = $logmanager->get_supported_reports('logstore_standard');
|
||||
|
||||
foreach ($expectedreports as $expectedreport) {
|
||||
$this->assertContains($expectedreport, $reports);
|
||||
$this->assertArrayHasKey($expectedreport, $supportedreports);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,7 +365,7 @@ class logstore_standard_store_testcase extends advanced_testcase {
|
||||
* Tests the decode_other function can cope with both JSON and PHP serialized format.
|
||||
*
|
||||
* @param mixed $value Value to encode and decode
|
||||
* @dataProvider test_decode_other_provider
|
||||
* @dataProvider decode_other_provider
|
||||
*/
|
||||
public function test_decode_other($value) {
|
||||
$this->assertEquals($value, \logstore_standard\log\store::decode_other(serialize($value)));
|
||||
@@ -386,7 +384,7 @@ class logstore_standard_store_testcase extends advanced_testcase {
|
||||
*
|
||||
* @return array Array of parameters
|
||||
*/
|
||||
public function test_decode_other_provider(): array {
|
||||
public function decode_other_provider(): array {
|
||||
return [
|
||||
[['info' => 'd2819896', 'logurl' => 'discuss.php?d=2819896']],
|
||||
[null],
|
||||
@@ -399,8 +397,7 @@ class logstore_standard_store_testcase extends advanced_testcase {
|
||||
* Checks that backup and restore of log data works correctly.
|
||||
*
|
||||
* @param bool $jsonformat True to test with JSON format
|
||||
* @dataProvider test_log_writing_provider
|
||||
* @throws moodle_exception
|
||||
* @dataProvider log_writing_provider
|
||||
*/
|
||||
public function test_backup_restore(bool $jsonformat) {
|
||||
global $DB;
|
||||
|
||||
@@ -77,9 +77,10 @@
|
||||
ucw.registerEvents('[data-region="user-competency-status"]');
|
||||
ucw.on('status-changed', infoReloader.reload.bind(infoReloader));
|
||||
ucw.on('error-occured', infoReloader.reload.bind(infoReloader));
|
||||
|
||||
var inlineGrader = new mod('#rate_{{uniqid}}', '{{competency.scaleid}}', '{{competency.competency.id}}', '{{user.id}}', '{{plan.id}}', '', '{{#str}}chooserating, tool_lp{{/str}}');
|
||||
inlineGrader.on('competencyupdated', infoReloader.reload.bind(infoReloader));
|
||||
{{#cangrade}}
|
||||
var inlineGrader = new mod('#rate_{{uniqid}}', '{{competency.scaleid}}', '{{competency.competency.id}}', '{{user.id}}', '{{plan.id}}', '', '{{#str}}chooserating, tool_lp{{/str}}');
|
||||
inlineGrader.on('competencyupdated', infoReloader.reload.bind(infoReloader));
|
||||
{{/cangrade}}
|
||||
});
|
||||
{{/js}}
|
||||
{{/usercompetency}}
|
||||
|
||||
@@ -95,13 +95,14 @@
|
||||
{{#js}}
|
||||
require(['jquery', 'tool_lp/grade_user_competency_inline', 'tool_lp/user_competency_info'], function($, mod, info) {
|
||||
|
||||
var inlineGrader = new mod('#rate_{{uniqid}}', '{{competency.scaleid}}', '{{competency.competency.id}}', '{{user.id}}', '', '{{course.id}}', '{{#str}}chooserating, tool_lp{{/str}}');
|
||||
var competencyElement = $('[data-region-id="{{uniqid}}"]');
|
||||
|
||||
var displayuser = ('{{displayuser}}' == 'true') ? true : false;
|
||||
var infoReloader = new info(competencyElement, '{{competency.competency.id}}', '{{user.id}}', '', '{{course.id}}', displayuser);
|
||||
|
||||
inlineGrader.on('competencyupdated', infoReloader.reload.bind(infoReloader));
|
||||
{{#cangrade}}
|
||||
var inlineGrader = new mod('#rate_{{uniqid}}', '{{competency.scaleid}}', '{{competency.competency.id}}', '{{user.id}}', '', '{{course.id}}', '{{#str}}chooserating, tool_lp{{/str}}');
|
||||
inlineGrader.on('competencyupdated', infoReloader.reload.bind(infoReloader));
|
||||
{{/cangrade}}
|
||||
});
|
||||
|
||||
{{/js}}
|
||||
|
||||
@@ -144,9 +144,7 @@ Feature: Manage plan workflow
|
||||
| name | user | description | status | reviewer |
|
||||
| Test-Plan3 | user1 | Description of plan 3 for user 1 | active | manager1 |
|
||||
| Test-Plan4 | user1 | Description of plan 4 for user 1 | active | manager1 |
|
||||
And I log in as "manager1"
|
||||
And I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
And I follow "User 1"
|
||||
And I am on the "user1" "user > profile" page logged in as "manager1"
|
||||
And I follow "Learning plans"
|
||||
And I should see "List of learning plans"
|
||||
And I follow "Learning plans"
|
||||
@@ -163,9 +161,7 @@ Feature: Manage plan workflow
|
||||
| name | user | description | status | reviewer |
|
||||
| Test-Plan3 | user1 | Description of plan 3 for user 1 | active | manager1 |
|
||||
| Test-Plan4 | user1 | Description of plan 4 for user 1 | active | manager1 |
|
||||
And I log in as "manager1"
|
||||
And I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
And I follow "User 1"
|
||||
And I am on the "user1" "user > profile" page logged in as "manager1"
|
||||
And I follow "Learning plans"
|
||||
And I should see "List of learning plans"
|
||||
And I follow "Learning plans"
|
||||
@@ -185,9 +181,7 @@ Feature: Manage plan workflow
|
||||
| name | user | description | status | reviewer |
|
||||
| Test-Plan3 | user1 | Description of plan 3 for user 1 | complete | manager1 |
|
||||
| Test-Plan4 | user1 | Description of plan 4 for user 1 | complete | manager1 |
|
||||
And I log in as "manager1"
|
||||
And I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
And I follow "User 1"
|
||||
And I am on the "user1" "user > profile" page logged in as "manager1"
|
||||
And I follow "Learning plans"
|
||||
And I should see "List of learning plans"
|
||||
And I follow "Learning plans"
|
||||
|
||||
@@ -69,22 +69,28 @@ class tool_mobile_api_testcase extends externallib_advanced_testcase {
|
||||
*/
|
||||
public function test_get_potential_config_issues() {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/message/lib.php');
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
|
||||
// Set non-SSL wwwroot, to avoid spurious certificate checking.
|
||||
$CFG->wwwroot = 'http://www.example.com';
|
||||
$CFG->userquota = '73289234723498234723423489273423497234234';
|
||||
$CFG->debugdisplay = 1;
|
||||
|
||||
set_config('debugauthdb', 1, 'auth_db');
|
||||
set_config('debugdb', 1, 'enrol_database');
|
||||
$expectedissues = array('nohttpsformobilewarning', 'invaliduserquotawarning', 'adodbdebugwarning', 'displayerrorswarning');
|
||||
|
||||
// Get potential issues, obtain their keys for comparison.
|
||||
$issues = api::get_potential_config_issues();
|
||||
$this->assertCount(count($expectedissues), $issues);
|
||||
foreach ($issues as $issue) {
|
||||
$this->assertTrue(in_array($issue[0], $expectedissues));
|
||||
}
|
||||
$issuekeys = array_column($issues, 0);
|
||||
|
||||
$this->assertEqualsCanonicalizing([
|
||||
'nohttpsformobilewarning',
|
||||
'invaliduserquotawarning',
|
||||
'adodbdebugwarning',
|
||||
'displayerrorswarning',
|
||||
], $issuekeys);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -241,9 +241,7 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
|
||||
And I set the field "I agree to the This site policy" to "1"
|
||||
And I press "Next"
|
||||
And I log out
|
||||
And I log in as "admin"
|
||||
And I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
And I follow "Manager"
|
||||
And I am on the "manager" "user > profile" page logged in as "admin"
|
||||
And I follow "Log in as"
|
||||
And I press "Continue"
|
||||
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
|
||||
|
||||
@@ -562,9 +562,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| user1 | User | 1 | user1@example.com |
|
||||
When I log in as "admin"
|
||||
And I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
And I follow "User 1"
|
||||
When I am on the "user1" "user > profile" page logged in as "admin"
|
||||
And I follow "Log in as"
|
||||
Then I should see "You are logged in as User 1"
|
||||
And I press "Continue"
|
||||
@@ -603,8 +601,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
And I set the field "I agree to the This site policy" to "1"
|
||||
And I set the field "I agree to the This privacy policy" to "1"
|
||||
And I press "Next"
|
||||
And I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
And I follow "User 1"
|
||||
And I am on the "user1" "user > profile" page
|
||||
And I follow "Log in as"
|
||||
Then I should see "You are logged in as User 1"
|
||||
And I press "Continue"
|
||||
|
||||
@@ -386,7 +386,7 @@ class availability_completion_condition_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Tests the is_available and get_description functions for previous activity option.
|
||||
*
|
||||
* @dataProvider test_previous_activity_data
|
||||
* @dataProvider previous_activity_data
|
||||
* @param int $grade the current assign grade (0 for none)
|
||||
* @param int $condition true for complete, false for incomplete
|
||||
* @param string $mark activity to mark as complete
|
||||
@@ -475,7 +475,7 @@ class availability_completion_condition_testcase extends advanced_testcase {
|
||||
$this->assertRegExp($description, $information);
|
||||
}
|
||||
|
||||
public function test_previous_activity_data(): array {
|
||||
public function previous_activity_data(): array {
|
||||
// Assign grade, condition, activity to complete, activity to test, result, resultnot, description.
|
||||
return [
|
||||
'Missing previous activity complete' => [
|
||||
@@ -567,7 +567,7 @@ class availability_completion_condition_testcase extends advanced_testcase {
|
||||
* Tests the is_available and get_description functions for
|
||||
* previous activity option in course sections.
|
||||
*
|
||||
* @dataProvider test_section_previous_activity_data
|
||||
* @dataProvider section_previous_activity_data
|
||||
* @param int $condition condition value
|
||||
* @param bool $mark if Page 1 must be mark as completed
|
||||
* @param string $section section to add the availability
|
||||
@@ -640,7 +640,7 @@ class availability_completion_condition_testcase extends advanced_testcase {
|
||||
|
||||
}
|
||||
|
||||
public function test_section_previous_activity_data(): array {
|
||||
public function section_previous_activity_data(): array {
|
||||
return [
|
||||
// Condition, Activity completion, section to test, result, resultnot, description.
|
||||
'Completion complete Section with no previous activity' => [
|
||||
|
||||
@@ -197,7 +197,12 @@ class condition extends \core_availability\condition {
|
||||
$this->customfield);
|
||||
}
|
||||
} else {
|
||||
$translatedfieldname = get_user_field_name($this->standardfield);
|
||||
$standardfields = self::get_standard_profile_fields();
|
||||
if (array_key_exists($this->standardfield, $standardfields)) {
|
||||
$translatedfieldname = $standardfields[$this->standardfield];
|
||||
} else {
|
||||
$translatedfieldname = get_string('missing', 'availability_profile', $this->standardfield);
|
||||
}
|
||||
}
|
||||
$context = \context_course::instance($course->id);
|
||||
$a = new \stdClass();
|
||||
@@ -321,6 +326,33 @@ class condition extends \core_availability\condition {
|
||||
return $fieldconditionmet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of standard user profile fields used by the condition
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function get_standard_profile_fields(): array {
|
||||
return [
|
||||
'firstname' => get_user_field_name('firstname'),
|
||||
'lastname' => get_user_field_name('lastname'),
|
||||
'email' => get_user_field_name('email'),
|
||||
'city' => get_user_field_name('city'),
|
||||
'country' => get_user_field_name('country'),
|
||||
'url' => get_user_field_name('url'),
|
||||
'icq' => get_user_field_name('icq'),
|
||||
'skype' => get_user_field_name('skype'),
|
||||
'aim' => get_user_field_name('aim'),
|
||||
'yahoo' => get_user_field_name('yahoo'),
|
||||
'msn' => get_user_field_name('msn'),
|
||||
'idnumber' => get_user_field_name('idnumber'),
|
||||
'institution' => get_user_field_name('institution'),
|
||||
'department' => get_user_field_name('department'),
|
||||
'phone1' => get_user_field_name('phone1'),
|
||||
'phone2' => get_user_field_name('phone2'),
|
||||
'address' => get_user_field_name('address')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data about custom profile fields. Cached statically in current
|
||||
* request.
|
||||
@@ -472,6 +504,11 @@ class condition extends \core_availability\condition {
|
||||
$valuefield = 'data';
|
||||
$default = $customfield->defaultdata;
|
||||
} else {
|
||||
$standardfields = self::get_standard_profile_fields();
|
||||
if (!array_key_exists($this->standardfield, $standardfields)) {
|
||||
// If the field isn't found, nobody matches.
|
||||
return [];
|
||||
}
|
||||
$values = $DB->get_records_select('user', 'id ' . $sql, $params,
|
||||
'', 'id, '. $this->standardfield);
|
||||
$valuefield = $this->standardfield;
|
||||
@@ -595,6 +632,11 @@ class condition extends \core_availability\condition {
|
||||
$where = "(ud.data IS NOT NULL AND $condition)";
|
||||
}
|
||||
} else {
|
||||
$standardfields = self::get_standard_profile_fields();
|
||||
if (!array_key_exists($this->standardfield, $standardfields)) {
|
||||
// If the field isn't found, nobody matches.
|
||||
return ['SELECT id FROM {user} WHERE 0 = 1', []];
|
||||
}
|
||||
$tablesql = "JOIN {user} u ON u.id = userids.id";
|
||||
list ($where, $mainparams) = $this->get_condition_sql(
|
||||
'u.' . $this->standardfield);
|
||||
|
||||
@@ -42,26 +42,9 @@ class frontend extends \core_availability\frontend {
|
||||
|
||||
protected function get_javascript_init_params($course, \cm_info $cm = null,
|
||||
\section_info $section = null) {
|
||||
|
||||
// Standard user fields.
|
||||
$standardfields = array(
|
||||
'firstname' => get_user_field_name('firstname'),
|
||||
'lastname' => get_user_field_name('lastname'),
|
||||
'email' => get_user_field_name('email'),
|
||||
'city' => get_user_field_name('city'),
|
||||
'country' => get_user_field_name('country'),
|
||||
'url' => get_user_field_name('url'),
|
||||
'icq' => get_user_field_name('icq'),
|
||||
'skype' => get_user_field_name('skype'),
|
||||
'aim' => get_user_field_name('aim'),
|
||||
'yahoo' => get_user_field_name('yahoo'),
|
||||
'msn' => get_user_field_name('msn'),
|
||||
'idnumber' => get_user_field_name('idnumber'),
|
||||
'institution' => get_user_field_name('institution'),
|
||||
'department' => get_user_field_name('department'),
|
||||
'phone1' => get_user_field_name('phone1'),
|
||||
'phone2' => get_user_field_name('phone2'),
|
||||
'address' => get_user_field_name('address')
|
||||
);
|
||||
$standardfields = condition::get_standard_profile_fields();
|
||||
\core_collator::asort($standardfields);
|
||||
|
||||
// Custom fields.
|
||||
|
||||
@@ -39,7 +39,7 @@ $string['requires_notendswith'] = 'Your <strong>{$a->field}</strong> does not en
|
||||
$string['requires_notisequalto'] = 'Your <strong>{$a->field}</strong> is not <strong>{$a->value}</strong>';
|
||||
$string['requires_notstartswith'] = 'Your <strong>{$a->field}</strong> does not start with <strong>{$a->value}</strong>';
|
||||
$string['requires_startswith'] = 'Your <strong>{$a->field}</strong> starts with <strong>{$a->value}</strong>';
|
||||
$string['missing'] = '(Missing custom field: {$a})';
|
||||
$string['missing'] = '(Missing field: {$a})';
|
||||
$string['title'] = 'User profile';
|
||||
$string['op_contains'] = 'contains';
|
||||
$string['op_doesnotcontain'] = 'doesn\'t contain';
|
||||
|
||||
@@ -48,7 +48,7 @@ class backup_files_edit_form extends moodleform {
|
||||
$mform->setType('component', PARAM_COMPONENT);
|
||||
|
||||
$mform->addElement('hidden', 'returnurl', $this->_customdata['returnurl']);
|
||||
$mform->setType('returnurl', PARAM_URL);
|
||||
$mform->setType('returnurl', PARAM_LOCALURL);
|
||||
|
||||
$this->add_action_buttons(true, get_string('savechanges'));
|
||||
$this->set_data($this->_customdata['data']);
|
||||
|
||||
@@ -643,7 +643,7 @@ class backup_ui_stage_complete extends backup_ui_stage_final {
|
||||
}
|
||||
$output .= $renderer->get_samesite_notification();
|
||||
$output .= $renderer->notification(get_string('executionsuccess', 'backup'), 'notifysuccess');
|
||||
$output .= $renderer->continue_button($restorerul);
|
||||
$output .= $renderer->continue_button($restorerul, 'get');
|
||||
$output .= $renderer->box_end();
|
||||
|
||||
return $output;
|
||||
|
||||
@@ -553,7 +553,8 @@ class core_backup_renderer extends plugin_renderer_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a continue button
|
||||
* Displays a continue button, overriding core renderer method of the same in order
|
||||
* to override submission method of the button form
|
||||
*
|
||||
* @param string|moodle_url $url
|
||||
* @param string $method
|
||||
@@ -566,7 +567,6 @@ class core_backup_renderer extends plugin_renderer_base {
|
||||
if ($method != 'post') {
|
||||
$method = 'get';
|
||||
}
|
||||
$url->param('sesskey', sesskey());
|
||||
$button = new single_button($url, get_string('continue'), $method, true);
|
||||
$button->class = 'continuebutton';
|
||||
return $this->render($button);
|
||||
|
||||
@@ -1161,8 +1161,10 @@ class restore_ui_stage_complete extends restore_ui_stage_process {
|
||||
$html .= $renderer->notification(get_string('restorefileweremissing', 'backup'), 'notifyproblem');
|
||||
}
|
||||
$html .= $renderer->notification(get_string('restoreexecutionsuccess', 'backup'), 'notifysuccess');
|
||||
$html .= $renderer->continue_button(new moodle_url('/course/view.php', array(
|
||||
'id' => $this->get_ui()->get_controller()->get_courseid())), 'get');
|
||||
|
||||
$courseurl = course_get_url($this->get_ui()->get_controller()->get_courseid());
|
||||
$html .= $renderer->continue_button($courseurl, 'get');
|
||||
|
||||
$html .= $renderer->box_end();
|
||||
|
||||
return $html;
|
||||
|
||||
@@ -146,6 +146,7 @@ Feature: My overview block pagination
|
||||
| student1 | C12 | student |
|
||||
| student1 | C13 | student |
|
||||
When I log in as "student1"
|
||||
And I wait until ".block_myoverview [data-control='next']" "css_element" exists
|
||||
And I click on "[data-control='next']" "css_element" in the "Course overview" "block"
|
||||
Then the "class" attribute of ".block_myoverview [data-control='next']" "css_element" should contain "disabled"
|
||||
And I log out
|
||||
|
||||
@@ -14,23 +14,16 @@ Feature: Block appearances
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "admin"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Survey" to section "1" and I fill the form with:
|
||||
| Name | Test survey name |
|
||||
| Survey type | ATTLS (20 item version) |
|
||||
| Description | Test survey description |
|
||||
And I add a "Book" to section "1" and I fill the form with:
|
||||
| Name | Test book name |
|
||||
| Description | Test book description |
|
||||
And I follow "Test book name"
|
||||
And I set the following fields to these values:
|
||||
| Chapter title | Book title |
|
||||
| Content | Book content test test |
|
||||
And I press "Save changes"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And the following "activities" exist:
|
||||
| activity | course | name | element |
|
||||
| survey | C1 | Test survey name | 4 |
|
||||
| book | C1 | Test book name | |
|
||||
And the following "mod_book > chapter" exists:
|
||||
| book | Test book name |
|
||||
| title | Book title |
|
||||
| content | Book content test test |
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
And I turn editing mode on
|
||||
And I add the "Comments" block
|
||||
And I configure the "Comments" block
|
||||
And I set the following fields to these values:
|
||||
|
||||
@@ -14,21 +14,14 @@ Feature: Block region moving
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "admin"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Survey" to section "1" and I fill the form with:
|
||||
| Name | Test survey name |
|
||||
| Survey type | ATTLS (20 item version) |
|
||||
| Description | Test survey description |
|
||||
And I add a "Book" to section "1" and I fill the form with:
|
||||
| Name | Test book name |
|
||||
| Description | Test book description |
|
||||
And I follow "Test book name"
|
||||
And I set the following fields to these values:
|
||||
| Chapter title | Book title |
|
||||
| Content | Book content test test |
|
||||
And I press "Save changes"
|
||||
And I log out
|
||||
And the following "activities" exist:
|
||||
| activity | name | course | template |
|
||||
| survey | Test survey name | C1 | 4 |
|
||||
| book | Test book name | C1 | |
|
||||
And the following "mod_book > chapter" exists:
|
||||
| book | Test book name |
|
||||
| title | Book title |
|
||||
| content | Book content test test |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add the "Comments" block
|
||||
|
||||
@@ -64,7 +64,7 @@ class blog_edit_external_form extends moodleform {
|
||||
$mform->setDefault('id', 0);
|
||||
|
||||
$mform->addElement('hidden', 'returnurl');
|
||||
$mform->setType('returnurl', PARAM_URL);
|
||||
$mform->setType('returnurl', PARAM_LOCALURL);
|
||||
$mform->setDefault('returnurl', 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -2628,7 +2628,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_get_calendar_event_by_id_prevent_read_other_users_events_data_provider(): array {
|
||||
public function get_calendar_event_by_id_prevent_read_other_users_events_data_provider(): array {
|
||||
$syscontext = context_system::instance();
|
||||
$managerrole = 'manager';
|
||||
return [
|
||||
@@ -2643,7 +2643,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
|
||||
* Prevent user from reading other user's event.
|
||||
*
|
||||
* @covers \core_calendar_external::get_calendar_event_by_id
|
||||
* @dataProvider test_get_calendar_event_by_id_prevent_read_other_users_events_data_provider
|
||||
* @dataProvider get_calendar_event_by_id_prevent_read_other_users_events_data_provider
|
||||
*
|
||||
* @param bool $isadminevent Is admin's event
|
||||
* @param bool $isadmin Is current user admin user
|
||||
@@ -2699,7 +2699,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_edit_or_delete_other_users_events_data_provider(): array {
|
||||
public function edit_or_delete_other_users_events_data_provider(): array {
|
||||
$syscontext = context_system::instance();
|
||||
$managerrole = 'manager';
|
||||
return [
|
||||
@@ -2713,7 +2713,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
|
||||
/**
|
||||
* Test the behavior of deleting other users' user events.
|
||||
*
|
||||
* @dataProvider test_edit_or_delete_other_users_events_data_provider
|
||||
* @dataProvider edit_or_delete_other_users_events_data_provider
|
||||
* @covers \core_calendar_external::delete_calendar_events
|
||||
* @param bool $isadmin Whether the current user is admin.
|
||||
* @param bool $isadminevent Whether it's an admin event or not.
|
||||
@@ -2760,7 +2760,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
|
||||
/**
|
||||
* Test the behavior of editing other users' user events
|
||||
*
|
||||
* @dataProvider test_edit_or_delete_other_users_events_data_provider
|
||||
* @dataProvider edit_or_delete_other_users_events_data_provider
|
||||
* @covers \core_calendar_external::submit_create_update_form
|
||||
* @param bool $isadmin Whether the current user is admin.
|
||||
* @param bool $isadminevent Whether it's an admin event or not.
|
||||
|
||||
@@ -56,7 +56,7 @@ class core_calendar_std_proxy_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Test proxying.
|
||||
*
|
||||
* @dataProvider test_proxy_testcases()
|
||||
* @dataProvider proxy_testcases
|
||||
* @param int $id Object ID.
|
||||
* @param string $member Object member to retrieve.
|
||||
* @param mixed $expected Expected value of member.
|
||||
@@ -72,7 +72,7 @@ class core_calendar_std_proxy_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Test setting values with a base class.
|
||||
*
|
||||
* @dataProvider test_proxy_testcases()
|
||||
* @dataProvider proxy_testcases
|
||||
* @param int $id Object ID.
|
||||
* @param string $member Object member to retrieve.
|
||||
* @param mixed $storedvalue Value as would be stored externally.
|
||||
@@ -93,7 +93,7 @@ class core_calendar_std_proxy_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Test getting a non existant member.
|
||||
*
|
||||
* @dataProvider test_get_set_testcases()
|
||||
* @dataProvider get_set_testcases
|
||||
* @param int $id ID of the object being proxied.
|
||||
*/
|
||||
public function test_get_invalid_member($id) {
|
||||
@@ -108,7 +108,7 @@ class core_calendar_std_proxy_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Test get proxied instance.
|
||||
*
|
||||
* @dataProvider test_get_set_testcases()
|
||||
* @dataProvider get_set_testcases
|
||||
* @param int $id Object ID.
|
||||
*/
|
||||
public function test_get_proxied_instance($id) {
|
||||
@@ -122,7 +122,7 @@ class core_calendar_std_proxy_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Test cases for proxying test.
|
||||
*/
|
||||
public function test_proxy_testcases() {
|
||||
public function proxy_testcases() {
|
||||
return [
|
||||
'Object 1 member 1' => [
|
||||
1,
|
||||
@@ -160,7 +160,7 @@ class core_calendar_std_proxy_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Test cases for getting and setting tests.
|
||||
*/
|
||||
public function test_get_set_testcases() {
|
||||
public function get_set_testcases() {
|
||||
return [
|
||||
'Object 1' => [1],
|
||||
'Object 2' => [5]
|
||||
|
||||
+5
-5
@@ -7,13 +7,13 @@
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "7.5.*",
|
||||
"phpunit/dbunit": "4.0.*",
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"behat/mink": "~1.8",
|
||||
"mikey179/vfsstream": "1.6.*",
|
||||
"behat/mink": "^1.10.0",
|
||||
"behat/mink-extension": "~2.3",
|
||||
"behat/mink-goutte-driver": "~1.2",
|
||||
"behat/mink-goutte-driver": "^1.3",
|
||||
"symfony/process": "^4.0 || ^5.0",
|
||||
"behat/behat": "3.10.*",
|
||||
"oleg-andreyev/mink-phpwebdriver": "^1.0"
|
||||
"behat/behat": "3.11.*",
|
||||
"oleg-andreyev/mink-phpwebdriver": "^1.2.1"
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-0": {
|
||||
|
||||
Generated
+385
-338
File diff suppressed because it is too large
Load Diff
@@ -70,6 +70,10 @@ $CFG->dboptions = array(
|
||||
// can be removed for MySQL (by default it will
|
||||
// use 'utf8mb4_unicode_ci'. This option should
|
||||
// be removed for all other databases.
|
||||
// 'extrainfo' => [], // Extra information for the DB driver, e.g. SQL Server,
|
||||
// has additional configuration according to its environment,
|
||||
// which the administrator can specify to alter and
|
||||
// override any connection options.
|
||||
// 'fetchbuffersize' => 100000, // On PostgreSQL, this option sets a limit
|
||||
// on the number of rows that are fetched into
|
||||
// memory when doing a large recordset query
|
||||
|
||||
@@ -14,7 +14,7 @@ Feature: Sections can be edited and deleted in topics format
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber | section |
|
||||
| assign | Test assignment name | Test assignment description | C1 | assign1 | 0 |
|
||||
| book | Test book name | Test book description | C1 | book1 | 1 |
|
||||
| book | Test book name | | C1 | book1 | 1 |
|
||||
| chat | Test chat name | Test chat description | C1 | chat1 | 4 |
|
||||
| choice | Test choice name | Test choice description | C1 | choice1 | 5 |
|
||||
And the following "course enrolments" exist:
|
||||
|
||||
@@ -14,7 +14,7 @@ Feature: Sections can be edited and deleted in weeks format
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber | section |
|
||||
| assign | Test assignment name | Test assignment description | C1 | assign1 | 0 |
|
||||
| book | Test book name | Test book description | C1 | book1 | 1 |
|
||||
| book | Test book name | | C1 | book1 | 1 |
|
||||
| chat | Test chat name | Test chat description | C1 | chat1 | 4 |
|
||||
| choice | Test choice name | Test choice description | C1 | choice1 | 5 |
|
||||
And the following "course enrolments" exist:
|
||||
|
||||
@@ -34,7 +34,7 @@ require_once($CFG->dirroot.'/course/lib.php');
|
||||
|
||||
$id = required_param('id', PARAM_INT);
|
||||
$switchrole = optional_param('switchrole', -1, PARAM_INT);
|
||||
$returnurl = optional_param('returnurl', '', PARAM_RAW);
|
||||
$returnurl = optional_param('returnurl', '', PARAM_LOCALURL);
|
||||
|
||||
if (strpos($returnurl, '?') === false) {
|
||||
// Looks like somebody did not set proper page url, better go to course page.
|
||||
@@ -43,7 +43,7 @@ if (strpos($returnurl, '?') === false) {
|
||||
if (strpos($returnurl, $CFG->wwwroot) !== 0) {
|
||||
$returnurl = $CFG->wwwroot.$returnurl;
|
||||
}
|
||||
$returnurl = clean_param($returnurl, PARAM_URL);
|
||||
$returnurl = clean_param($returnurl, PARAM_LOCALURL);
|
||||
}
|
||||
|
||||
$PAGE->set_url('/course/switchrole.php', array('id'=>$id, 'switchrole'=>$switchrole));
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
@core @core_course
|
||||
Feature: Teacher can change the course format
|
||||
In order to change course format
|
||||
As a teacher
|
||||
I should be able to edit a course
|
||||
|
||||
@javascript
|
||||
Scenario: Teacher can change the course format
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
# Course format is initially set to Topics format
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | format | startdate |
|
||||
| Course 1 | C1 | topics | ## 1 day ago ## |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
# Confirm that course format is Topics
|
||||
When I am on the "Course 1" course page logged in as teacher1
|
||||
Then I should see "Topic 1"
|
||||
And I am on the "Course 1" "course editing" page
|
||||
And I expand all fieldsets
|
||||
# Fields that appear for Topics format exist
|
||||
# Also confirm contents of Hidden sections and Course layout select box
|
||||
And I should see "Hidden sections"
|
||||
And the "Hidden sections" select box should contain "Hidden sections are shown in collapsed form"
|
||||
And the "Hidden sections" select box should contain "Hidden sections are completely invisible"
|
||||
# Hidden sections default value is 0 (Hidden sections are shown in collapsed form)
|
||||
And the field "Hidden sections" matches value "0"
|
||||
And I should see "Course layout"
|
||||
And the "Course layout" select box should contain "Show all sections on one page"
|
||||
And the "Course layout" select box should contain "Show one section per page"
|
||||
# Course layout default value is 0 (Show all sections on one page)
|
||||
And the field "Course layout" matches value "0"
|
||||
# Set course format to Single activity format
|
||||
And I set the field "Format" to "Single activity format"
|
||||
And I expand all fieldsets
|
||||
# Confirm that fields that appear for Single activity format appears
|
||||
And I should see "Type of activity"
|
||||
And I set the field "Type of activity" to "Glossary"
|
||||
And I press "Save and display"
|
||||
And I set the field "Name" to "Glossary 1"
|
||||
And I press "Save and display"
|
||||
# Confirm that course page displays single activity of type Glossary
|
||||
And I should see "Browse the glossary using this index"
|
||||
And I should not see "Topic 1"
|
||||
And I am on the "Course 1" "course editing" page
|
||||
And I expand all fieldsets
|
||||
# Set course format to Weekly format
|
||||
And I set the field "Format" to "Weekly format"
|
||||
And I expand all fieldsets
|
||||
# Confirm that fields that appear for Weekly format appears
|
||||
# Also confirm contents of Hidden sections and Course layout select box
|
||||
And I should see "Hidden sections"
|
||||
And the "Hidden sections" select box should contain "Hidden sections are shown in collapsed form"
|
||||
And the "Hidden sections" select box should contain "Hidden sections are completely invisible"
|
||||
# Hidden sections default value is 0 (Hidden sections are shown in collapsed form)
|
||||
And the field "Hidden sections" matches value "0"
|
||||
And I should see "Course layout"
|
||||
And the "Course layout" select box should contain "Show all sections on one page"
|
||||
And the "Course layout" select box should contain "Show one section per page"
|
||||
# Course layout default value is 0 (Show all sections on one page)
|
||||
And the field "Course layout" matches value "0"
|
||||
And I press "Save and display"
|
||||
# Confirm that course page displays weekly sections
|
||||
And I should see "This week"
|
||||
And I should not see "Browse the glossary using this index"
|
||||
And I am on the "Course 1" "course editing" page
|
||||
And I expand all fieldsets
|
||||
# Set course format to Social format
|
||||
And I set the field "Format" to "Social format"
|
||||
# Confirm that fields that appear for Social format appears
|
||||
And I expand all fieldsets
|
||||
And I should see "Number of discussions"
|
||||
And the field "Number of discussions" matches value "10"
|
||||
And I press "Save and display"
|
||||
# Confirm that course page displays a forum
|
||||
And I should see "There are no discussion topics yet in this forum"
|
||||
And I should not see "This week"
|
||||
@@ -33,7 +33,6 @@ use core_privacy\local\request\approved_contextlist;
|
||||
use core_privacy\local\request\contextlist;
|
||||
use core_privacy\local\request\writer;
|
||||
use core_privacy\manager;
|
||||
use Horde\Socket\Client\Exception;
|
||||
|
||||
/**
|
||||
* Class provider
|
||||
@@ -196,7 +195,7 @@ class provider implements
|
||||
$field = array_key_exists($record->fieldid, $fields) ? $fields[$record->fieldid] : null;
|
||||
$data = data_controller::create(0, $record, $field);
|
||||
self::export_customfield_data($data, array_merge($subcontext, [$record->id]));
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
// We store some data that we can not initialise controller for. We still need to export it.
|
||||
self::export_customfield_data_unknown($record, $field, array_merge($subcontext, [$record->id]));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@customfield @customfield_textarea @javascript @_file_upload
|
||||
# This feature has Atto-specific steps. See MDL-75913 for further details.
|
||||
@customfield @customfield_textarea @javascript @_file_upload @editor_atto
|
||||
Feature: Default value for the textarea custom field can contain images
|
||||
In order to see images on custom fields
|
||||
As a manager
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
@enrol @enrol_cohort
|
||||
Feature: Cohort enrolment management
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher001 | Teacher | 001 | teacher001@example.com |
|
||||
| s1 | Sandra | Cole | s1@example.com |
|
||||
| s2 | John | Smith | s2@example.com |
|
||||
| s4 | Jane | Doe | s4@example.com |
|
||||
| s3 | Bianca | McAfee | s3@example.com |
|
||||
| s5 | Abigail | Wyatt | s5@example.com |
|
||||
And the following "cohorts" exist:
|
||||
| name | idnumber | visible | Context |
|
||||
| Alpha1 | A1 | 1 | 3 |
|
||||
| Beta2 | B1 | 1 | 3 |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | format | startdate |
|
||||
| Course 001 | C001 | weeks | ##1 month ago## |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role | timestart |
|
||||
| teacher001 | C001 | editingteacher | ##1 month ago## |
|
||||
And the following "cohort members" exist:
|
||||
| user | cohort |
|
||||
| s1 | A1 |
|
||||
| s2 | A1 |
|
||||
| s3 | B1 |
|
||||
| s5 | B1 |
|
||||
And I am on the "Course 001" course page logged in as teacher001
|
||||
|
||||
@javascript
|
||||
Scenario: Add groups in the course cohort enrolment
|
||||
When I add "Cohort sync" enrolment method with:
|
||||
| Cohort | A1 |
|
||||
| customint2 | -1 |
|
||||
Then I should see "Cohort sync (Alpha1 - Student)"
|
||||
And I am on "Course 001" course homepage
|
||||
And I navigate to "Users > Groups" in current page administration
|
||||
# Confirm that group was created and corresponding group members are present
|
||||
And I set the field "groups[]" to "Alpha1 cohort (2)"
|
||||
And the "members" select box should contain "Sandra Cole (s1@example.com)"
|
||||
And the "members" select box should contain "John Smith (s2@example.com)"
|
||||
And I log in as "admin"
|
||||
And I navigate to "Users > Accounts > Cohorts" in site administration
|
||||
And I click on "Assign" "link" in the "Alpha1" "table_row"
|
||||
And I should see "Cohort 'Alpha1' members"
|
||||
And I should see "Removing users from a cohort may result in unenrolling of users from multiple courses which includes deleting of user settings, grades, group membership and other user information from affected courses."
|
||||
# Remove user s4 from cohort
|
||||
And I set the field "removeselect[]" to "John Smith (s2@example.com)"
|
||||
And I click on "Remove" "button"
|
||||
# Add user s4 to the cohort.
|
||||
And I set the field "addselect_searchtext" to "s4"
|
||||
And I set the field "addselect[]" to "Jane Doe (s4@example.com)"
|
||||
And I click on "Add" "button"
|
||||
And the "removeselect[]" select box should contain "Sandra Cole (s1@example.com)"
|
||||
And the "removeselect[]" select box should contain "Jane Doe (s4@example.com)"
|
||||
And the "removeselect[]" select box should not contain "John Smith (s2@example.com)"
|
||||
And I trigger cron
|
||||
And I am on "Course 001" course homepage
|
||||
And I navigate to course participants
|
||||
# Verifies students 1 and 4 are in the cohort and student 2 is not any more.
|
||||
And the following should exist in the "participants" table:
|
||||
| First name / Last name | Email address | Roles | Groups |
|
||||
| Sandra Cole | s1@example.com | Student | Alpha1 cohort |
|
||||
| Jane Doe | s4@example.com | Student | Alpha1 cohort |
|
||||
And the following should not exist in the "participants" table:
|
||||
| First name / Last name | Email address | Roles | Groups |
|
||||
| John Smith | s2@example.com | Student | Alpha1 cohort |
|
||||
|
||||
@javascript
|
||||
Scenario: Course cohort enrolment creates a new group
|
||||
When I add "Cohort sync" enrolment method with:
|
||||
| Cohort | B1 |
|
||||
And I click on "Edit" "link" in the "Beta2" "table_row"
|
||||
And I set the field "Add to group" to "Create new group"
|
||||
And I click on "Save changes" "button"
|
||||
And I am on "Course 001" course homepage
|
||||
And I navigate to "Users > Groups" in current page administration
|
||||
And I set the field "groups[]" to "Beta2 cohort (2)"
|
||||
Then the "members" select box should contain "Bianca McAfee (s3@example.com)"
|
||||
And the "members" select box should contain "Abigail Wyatt (s5@example.com)"
|
||||
@@ -28,9 +28,8 @@ Feature: Users can be added to multiple groups at once
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Participants"
|
||||
And I click on "Edit groups for \"Student 1\"" "link" in the "student1" "table_row"
|
||||
And I click on ".form-autocomplete-downarrow" "css_element" in the "student1" "table_row"
|
||||
And I open the autocomplete suggestions list in the "student1" "table_row"
|
||||
And I click on "Group 1" item in the autocomplete list
|
||||
And I click on ".form-autocomplete-downarrow" "css_element" in the "student1" "table_row"
|
||||
And I click on "Group 2" item in the autocomplete list
|
||||
And I press the escape key
|
||||
And I click on "Save changes" "link" in the "student1" "table_row"
|
||||
|
||||
@@ -26,9 +26,8 @@ Feature: Add a new custom file type
|
||||
| Custom description | Moodle rules |
|
||||
And I press "Save changes"
|
||||
And I should see "application/x-moodle-rules"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
And I turn editing mode on
|
||||
When I add a "File" to section "1" and I fill the form with:
|
||||
| Name | Test file |
|
||||
| Select files | files/tests/fixtures/custom_filetype.mdlr |
|
||||
|
||||
@@ -237,7 +237,7 @@ class filter_algebra extends moodle_text_filter {
|
||||
$DB->insert_record("cache_filters", $texcache, false);
|
||||
$text = str_replace( $matches[0][$i], filter_algebra_image($filename, $texexp, '', '', $align), $text);
|
||||
} else {
|
||||
$text = str_replace( $matches[0][$i],"<b>Undetermined error:</b> ",$text);
|
||||
$text = str_replace( $matches[0][$i],"<b>Undetermined error:</b> " . $matches[0][$i], $text);
|
||||
}
|
||||
} else {
|
||||
$text = str_replace( $matches[0][$i], filter_algebra_image($filename, $texcache->rawtext), $text);
|
||||
|
||||
@@ -123,7 +123,7 @@ class filter_data extends moodle_text_filter {
|
||||
usort($contents, 'filter_data::sort_entries_by_length');
|
||||
|
||||
foreach ($contents as $content) {
|
||||
$href_tag_begin = '<a class="data autolink dataid'.$content->dataid.'" title="'.$content->content.'" '.
|
||||
$href_tag_begin = '<a class="data autolink dataid'.$content->dataid.'" title="'.s($content->content).'" '.
|
||||
'href="'.$CFG->wwwroot.'/mod/data/view.php?d='.$content->dataid.
|
||||
'&rid='.$content->recordid.'">';
|
||||
$contentlist[] = new filterobject($content->content, $href_tag_begin, '</a>', false, true);
|
||||
|
||||
@@ -49,7 +49,8 @@ Feature: Render H5P content using filters
|
||||
And I wait until the page is ready
|
||||
Then ".h5p-iframe" "css_element" should not exist
|
||||
|
||||
@javascript
|
||||
# This scenario has Atto-specific steps. See MDL-75913 for further details.
|
||||
@javascript @editor_atto
|
||||
Scenario: Render a local H5P file as admin
|
||||
Given I log in as "admin"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
@@ -83,7 +84,8 @@ Feature: Render H5P content using filters
|
||||
And I should not see "you don't have access"
|
||||
And I should see "Lorum ipsum"
|
||||
|
||||
@javascript
|
||||
# This scenario has Atto-specific steps. See MDL-75913 for further details.
|
||||
@javascript @editor_atto
|
||||
Scenario: Render a local H5P file as teacher
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
@@ -107,7 +109,8 @@ Feature: Render H5P content using filters
|
||||
Then I should see "Note that the libraries may exist in the file you uploaded, but you're not allowed to upload new libraries."
|
||||
And I should see "missing-required-library"
|
||||
|
||||
@javascript
|
||||
# This scenario has Atto-specific steps. See MDL-75913 for further details.
|
||||
@javascript @editor_atto
|
||||
Scenario: Render a local H5P file with existing libraries
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
|
||||
@@ -14,15 +14,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Provides the {@link filter_mathjaxloader_filter_testcase} class.
|
||||
*
|
||||
* @package filter_mathjaxloader
|
||||
* @category test
|
||||
* @copyright 2017 David Mudrák <david@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
@@ -31,6 +22,8 @@ require_once($CFG->dirroot.'/filter/mathjaxloader/filter.php');
|
||||
/**
|
||||
* Unit tests for the MathJax loader filter.
|
||||
*
|
||||
* @package filter_mathjaxloader
|
||||
* @category test
|
||||
* @copyright 2017 David Mudrak <david@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@@ -42,7 +35,7 @@ class filter_mathjaxloader_filter_testcase extends advanced_testcase {
|
||||
* @param string $moodlelangcode the user's current language
|
||||
* @param string $mathjaxlangcode the mathjax language to be used for the moodle language
|
||||
*
|
||||
* @dataProvider test_map_language_code_expected_mappings
|
||||
* @dataProvider map_language_code_expected_mappings
|
||||
*/
|
||||
public function test_map_language_code($moodlelangcode, $mathjaxlangcode) {
|
||||
|
||||
@@ -55,7 +48,7 @@ class filter_mathjaxloader_filter_testcase extends advanced_testcase {
|
||||
*
|
||||
* @return array of [moodlelangcode, mathjaxcode] tuples
|
||||
*/
|
||||
public function test_map_language_code_expected_mappings() {
|
||||
public function map_language_code_expected_mappings() {
|
||||
|
||||
return [
|
||||
['cz', 'cs'], // Explicit mapping.
|
||||
|
||||
@@ -38,7 +38,7 @@ class filter_mathjaxloader_filtermath_testcase extends advanced_testcase {
|
||||
* @param string $inputtext The text given by the user.
|
||||
* @param string $expected The expected output after filtering.
|
||||
*
|
||||
* @dataProvider test_math_filtering_inputs
|
||||
* @dataProvider math_filtering_inputs
|
||||
*/
|
||||
public function test_math_filtering($inputtext, $expected) {
|
||||
$filter = new filter_mathjaxloader(context_system::instance(), []);
|
||||
@@ -50,7 +50,7 @@ class filter_mathjaxloader_filtermath_testcase extends advanced_testcase {
|
||||
*
|
||||
* @return array of [inputtext, expectedoutput] tuples.
|
||||
*/
|
||||
public function test_math_filtering_inputs() {
|
||||
public function math_filtering_inputs() {
|
||||
return [
|
||||
// One inline formula.
|
||||
['Some inline math \\( y = x^2 \\).',
|
||||
|
||||
@@ -43,24 +43,24 @@ Feature: We can change what we are viewing on the grader report
|
||||
And I click on "Hide" "link" in the "Test assignment name 2" activity
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to "View > Grader report" in the course gradebook
|
||||
And I should see "Test assignment name 1"
|
||||
And I should see "Test assignment name 2"
|
||||
And I should see "Test assignment name 1" in the "user-grades" "table"
|
||||
And I should see "Test assignment name 2" in the "user-grades" "table"
|
||||
And I should see "Manual grade"
|
||||
And I should see "Course total"
|
||||
And the following should exist in the "user-grades" table:
|
||||
| -1- | -4- | -5- | -6- | -7- |
|
||||
| Student 1 | 80 | 90 | 30 | 170 |
|
||||
And I click on "Change to aggregates only" "link"
|
||||
And I should not see "Test assignment name 1"
|
||||
And I should not see "Test assignment name 2"
|
||||
And I should not see "Test assignment name 1" in the "user-grades" "table"
|
||||
And I should not see "Test assignment name 2" in the "user-grades" "table"
|
||||
And I should not see "Manual grade"
|
||||
And I should see "Course total"
|
||||
And the following should exist in the "user-grades" table:
|
||||
| -1- | -4- |
|
||||
| Student 1 | 170 |
|
||||
And I click on "Change to grades only" "link"
|
||||
And I should see "Test assignment name 1"
|
||||
And I should see "Test assignment name 2"
|
||||
And I should see "Test assignment name 1" in the "user-grades" "table"
|
||||
And I should see "Test assignment name 2" in the "user-grades" "table"
|
||||
And I should see "Manual grade"
|
||||
And I should not see "Course total"
|
||||
And the following should exist in the "user-grades" table:
|
||||
@@ -68,7 +68,7 @@ Feature: We can change what we are viewing on the grader report
|
||||
| Student 1 | 80 | 90 | 30 |
|
||||
|
||||
@javascript @skip_chrome_zerosize
|
||||
Scenario: View and minimise the grader report containing hidden activities without the 'moodle/grade:viewhidden' capability
|
||||
Scenario: Minimise the grader report containing hidden activities without the 'moodle/grade:viewhidden' capability
|
||||
When I am on "Course 1" course homepage with editing mode on
|
||||
And I open "Test assignment name 2" actions menu
|
||||
And I click on "Hide" "link" in the "Test assignment name 2" activity
|
||||
@@ -81,24 +81,24 @@ Feature: We can change what we are viewing on the grader report
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to "View > Grader report" in the course gradebook
|
||||
And I should see "Test assignment name 1"
|
||||
And I should see "Test assignment name 2"
|
||||
And I should see "Test assignment name 1" in the "user-grades" "table"
|
||||
And I should see "Test assignment name 2" in the "user-grades" "table"
|
||||
And I should see "Manual grade"
|
||||
And I should see "Course total"
|
||||
And the following should exist in the "user-grades" table:
|
||||
| -1- | -4- | -5- | -6- | -7- |
|
||||
| Student 1 | 80 | - | 30 | 105.71 |
|
||||
And I click on "Change to aggregates only" "link"
|
||||
And I should not see "Test assignment name 1"
|
||||
And I should not see "Test assignment name 2"
|
||||
And I should not see "Test assignment name 1" in the "user-grades" "table"
|
||||
And I should not see "Test assignment name 2" in the "user-grades" "table"
|
||||
And I should not see "Manual grade"
|
||||
And I should see "Course total"
|
||||
And the following should exist in the "user-grades" table:
|
||||
| -1- | -4- |
|
||||
| Student 1 | 105.71 |
|
||||
And I click on "Change to grades only" "link"
|
||||
And I should see "Test assignment name 1"
|
||||
And I should see "Test assignment name 2"
|
||||
And I should see "Test assignment name 1" in the "user-grades" "table"
|
||||
And I should see "Test assignment name 2" in the "user-grades" "table"
|
||||
And I should see "Manual grade"
|
||||
And I should not see "Course total"
|
||||
And the following should exist in the "user-grades" table:
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace gradereport_history\output;
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
require_once($CFG->libdir . '/tablelib.php');
|
||||
require_once($CFG->dirroot . '/user/lib.php');
|
||||
|
||||
/**
|
||||
* Renderable class for gradehistory report.
|
||||
@@ -53,6 +54,11 @@ class tablelog extends \table_sql implements \renderable {
|
||||
*/
|
||||
protected $filters;
|
||||
|
||||
/**
|
||||
* @var \stdClass[] List of users included in the report (if userids are specified as filters)
|
||||
*/
|
||||
protected $users = [];
|
||||
|
||||
/**
|
||||
* @var array A list of grade items present in the course.
|
||||
*/
|
||||
@@ -99,7 +105,6 @@ class tablelog extends \table_sql implements \renderable {
|
||||
$this->courseid = $this->context->instanceid;
|
||||
$this->pagesize = $perpage;
|
||||
$this->page = $page;
|
||||
$this->filters = (object)$filters;
|
||||
$this->gradeitems = \grade_item::fetch_all(array('courseid' => $this->courseid));
|
||||
$this->cms = get_fast_modinfo($this->courseid);
|
||||
$this->useridfield = 'userid';
|
||||
@@ -108,6 +113,9 @@ class tablelog extends \table_sql implements \renderable {
|
||||
// Define columns in the table.
|
||||
$this->define_table_columns();
|
||||
|
||||
// Define filters.
|
||||
$this->define_table_filters((object) $filters);
|
||||
|
||||
// Define configs.
|
||||
$this->define_table_configs($url);
|
||||
|
||||
@@ -136,6 +144,36 @@ class tablelog extends \table_sql implements \renderable {
|
||||
$this->no_sorting('grader');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define table filters
|
||||
*
|
||||
* @param \stdClass $filters
|
||||
*/
|
||||
protected function define_table_filters(\stdClass $filters): void {
|
||||
global $DB;
|
||||
|
||||
$this->filters = $filters;
|
||||
|
||||
if (!empty($this->filters->userids)) {
|
||||
|
||||
$course = get_course($this->courseid);
|
||||
|
||||
// Retrieve userids that are part of the filters object, and ensure user can access each of them.
|
||||
[$userselect, $userparams] = $DB->get_in_or_equal(explode(',', $this->filters->userids), SQL_PARAMS_NAMED);
|
||||
[$usersort] = users_order_by_sql();
|
||||
|
||||
$this->users = array_filter(
|
||||
$DB->get_records_select('user', "id {$userselect}", $userparams, $usersort),
|
||||
static function(\stdClass $user) use ($course): bool {
|
||||
return user_can_view_profile($user, $course);
|
||||
}
|
||||
);
|
||||
|
||||
// Reset userids to the filtered array of users.
|
||||
$this->filters->userids = implode(',', array_keys($this->users));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the headers for the html table.
|
||||
*/
|
||||
@@ -507,19 +545,9 @@ class tablelog extends \table_sql implements \renderable {
|
||||
/**
|
||||
* Returns a list of selected users.
|
||||
*
|
||||
* @return array returns an array in the format $userid => $userid
|
||||
* @return \stdClass[] List of user objects
|
||||
*/
|
||||
public function get_selected_users() {
|
||||
global $DB;
|
||||
$idlist = array();
|
||||
if (!empty($this->filters->userids)) {
|
||||
|
||||
$idlist = explode(',', $this->filters->userids);
|
||||
list($where, $params) = $DB->get_in_or_equal($idlist);
|
||||
return $DB->get_records_select('user', "id $where", $params);
|
||||
|
||||
}
|
||||
return $idlist;
|
||||
public function get_selected_users(): array {
|
||||
return $this->users;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,6 +60,9 @@ class gradereport_history_report_testcase extends advanced_testcase {
|
||||
self::getDataGenerator()->enrol_user($u4->id, $c1->id, 'student');
|
||||
self::getDataGenerator()->enrol_user($u5->id, $c1->id, 'student');
|
||||
|
||||
self::getDataGenerator()->enrol_user($grader1->id, $c2->id, 'teacher');
|
||||
self::getDataGenerator()->enrol_user($u5->id, $c2->id, 'student');
|
||||
|
||||
// Modules.
|
||||
$c1m1 = $this->getDataGenerator()->create_module('assign', array('course' => $c1));
|
||||
$c1m2 = $this->getDataGenerator()->create_module('assign', array('course' => $c1));
|
||||
@@ -132,11 +135,18 @@ class gradereport_history_report_testcase extends advanced_testcase {
|
||||
$this->assertEquals(8, $this->get_tablelog_results($c1ctx, array(), true));
|
||||
$this->assertEquals(13, $this->get_tablelog_results($c2ctx, array(), true));
|
||||
|
||||
// Filtering on 1 user.
|
||||
$this->assertEquals(3, $this->get_tablelog_results($c1ctx, array('userids' => $u1->id), true));
|
||||
// Filtering on 1 user the current user cannot access should return all records.
|
||||
$this->assertEquals(8, $this->get_tablelog_results($c1ctx, array('userids' => $u1->id), true));
|
||||
|
||||
// Filtering on more users.
|
||||
$this->assertEquals(4, $this->get_tablelog_results($c1ctx, array('userids' => "$u1->id,$u3->id"), true));
|
||||
// Filtering on 2 users, only one of whom the current user can access.
|
||||
$this->assertEquals(1, $this->get_tablelog_results($c1ctx, ['userids' => "$u1->id,$u3->id"], true));
|
||||
$results = $this->get_tablelog_results($c1ctx, ['userids' => "$u1->id,$u3->id"]);
|
||||
$this->assertGradeHistoryIds([$grades['c1m1u3']->id], $results);
|
||||
|
||||
// Filtering on 2 users, both of whom the current user can access.
|
||||
$this->assertEquals(3, $this->get_tablelog_results($c1ctx, ['userids' => "$u2->id,$u3->id"], true));
|
||||
$results = $this->get_tablelog_results($c1ctx, ['userids' => "$u2->id,$u3->id"]);
|
||||
$this->assertGradeHistoryIds([$grades['c1m1u2']->id, $grades['c1m1u3']->id, $grades['c1m2u2']->id], $results);
|
||||
|
||||
// Filtering based on one grade item.
|
||||
$gi = grade_item::fetch($giparams + array('iteminstance' => $c1m1->id));
|
||||
|
||||
@@ -421,6 +421,8 @@ Feature: We can use calculated grade totals
|
||||
| Extra credit | 0 |
|
||||
| Maximum grade | 200 |
|
||||
| Rescale existing grades | No |
|
||||
# Change window size to ultra-wide to avoid 'out-of-bounds' random failures.
|
||||
And I change window size to "5120x2160"
|
||||
And I give the grade "21.00" to the user "Student 1" for the grade item "Manual item 2"
|
||||
And I press "Save changes"
|
||||
And I give the grade "20.00" to the user "Student 1" for the grade item "Manual item 2"
|
||||
@@ -432,6 +434,8 @@ Feature: We can use calculated grade totals
|
||||
| Extra credit | 0 |
|
||||
| Maximum grade | 100 |
|
||||
| Rescale existing grades | No |
|
||||
# Change window size to ultra-wide to avoid 'out-of-bounds' random failures.
|
||||
And I change window size to "5120x2160"
|
||||
And I give the grade "21.00" to the user "Student 1" for the grade item "Manual item 2"
|
||||
And I press "Save changes"
|
||||
And I give the grade "20.00" to the user "Student 1" for the grade item "Manual item 2"
|
||||
|
||||
@@ -21,13 +21,10 @@ Feature: We can set the grade to pass value
|
||||
And the following "activity" exists:
|
||||
| activity | assign |
|
||||
| course | C1 |
|
||||
| section | 1 |
|
||||
| idnumber | assign1 |
|
||||
| name | Test Assignment 1 |
|
||||
| intro | Submit your online text |
|
||||
| assignsubmission_onlinetext_enabled | 1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
|
||||
@javascript
|
||||
Scenario: Validate that switching the type of grading used correctly disables grade to pass
|
||||
@@ -115,11 +112,10 @@ Feature: We can set the grade to pass value
|
||||
|
||||
Scenario: Set a valid grade to pass for workshop activity
|
||||
Given the following "activities" exist:
|
||||
| activity | name | intro | course | section | idnumber |
|
||||
| workshop | Test Workshop 1 | Test workshop | C1 | 1 | workshop1 |
|
||||
| activity | name | course | idnumber |
|
||||
| workshop | Test Workshop 1 | C1 | workshop1 |
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I follow "Test Workshop 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test Workshop 1" "workshop activity editing" page
|
||||
And I set the following fields to these values:
|
||||
| grade | 80 |
|
||||
| Submission grade to pass | 40 |
|
||||
@@ -138,19 +134,16 @@ Feature: We can set the grade to pass value
|
||||
And the field "Grade to pass" matches value "10"
|
||||
And I set the field "Grade to pass" to "15"
|
||||
And I press "Save changes"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test Workshop 1"
|
||||
And I follow "Edit settings"
|
||||
And I am on the "Test Workshop 1" "workshop activity editing" page
|
||||
And the field "Submission grade to pass" matches value "45"
|
||||
And the field "Assessment grade to pass" matches value "15"
|
||||
|
||||
Scenario: Set an invalid grade to pass for workshop activity
|
||||
Given the following "activities" exist:
|
||||
| activity | name | intro | course | section | idnumber |
|
||||
| workshop | Test Workshop 1 | Test workshop | C1 | 1 | workshop1 |
|
||||
| activity | name | course | idnumber |
|
||||
| workshop | Test Workshop 1 | C1 | workshop1 |
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I follow "Test Workshop 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test Workshop 1" "workshop activity editing" page
|
||||
And I set the following fields to these values:
|
||||
| grade | 80 |
|
||||
| Submission grade to pass | 90 |
|
||||
@@ -184,14 +177,9 @@ Feature: We can set the grade to pass value
|
||||
|
||||
Scenario: Set a valid grade to pass for lesson activity
|
||||
Given the following "activities" exist:
|
||||
| activity | name | intro | course | section | idnumber |
|
||||
| lesson | Test Lesson 1 | Test | C1 | 1 | lesson1 |
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I follow "Test Lesson 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I set the following fields to these values:
|
||||
| Grade to pass | 90 |
|
||||
And I press "Save and return to course"
|
||||
| activity | name | course | idnumber | gradepass |
|
||||
| lesson | Test Lesson 1 | C1 | lesson1 | 90 |
|
||||
And I am on the "Test Lesson 1" "lesson activity" page
|
||||
And I navigate to "View > Grader report" in the course gradebook
|
||||
And I turn editing mode on
|
||||
And I click on "Edit lesson Test Lesson 1" "link"
|
||||
@@ -199,9 +187,7 @@ Feature: We can set the grade to pass value
|
||||
Then the field "Grade to pass" matches value "90"
|
||||
And I set the field "Grade to pass" to "80"
|
||||
And I press "Save changes"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test Lesson 1"
|
||||
And I follow "Edit settings"
|
||||
And I am on the "Test Lesson 1" "lesson activity editing" page
|
||||
And the field "Grade to pass" matches value "80"
|
||||
|
||||
Scenario: Set a valid grade to pass for database activity
|
||||
|
||||
+26
-28
@@ -1,8 +1,8 @@
|
||||
@core @core_group
|
||||
Feature: The description of a group can be viewed by students and teachers
|
||||
In order to view the description of a group
|
||||
Feature: The description and picture of a group can be viewed by students and teachers
|
||||
In order to view the description and picture of a group
|
||||
As a teacher
|
||||
I need to create groups and add descriptions to them.
|
||||
I need to create groups and add descriptions and picture to them.
|
||||
|
||||
Background:
|
||||
Given the following "courses" exist:
|
||||
@@ -19,11 +19,9 @@ Feature: The description of a group can be viewed by students and teachers
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
|
||||
@javascript
|
||||
Scenario: A student can see the group description when visible groups are set. Teachers can see group details.
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
@javascript @_file_upload
|
||||
Scenario: A student can see the group description and picture when visible groups are set. Teachers can see group details.
|
||||
Given I am on the "Course 1" "course editing" page logged in as "teacher1"
|
||||
And I set the following fields to these values:
|
||||
| Group mode | Visible groups |
|
||||
And I press "Save and display"
|
||||
@@ -32,6 +30,8 @@ Feature: The description of a group can be viewed by students and teachers
|
||||
And I set the following fields to these values:
|
||||
| Group name | Group A |
|
||||
| Group description | Description for Group A |
|
||||
# Upload group picture
|
||||
And I upload "lib/tests/fixtures/gd-logo.png" file to "New picture" filemanager
|
||||
And I press "Save changes"
|
||||
And I press "Create group"
|
||||
And I set the following fields to these values:
|
||||
@@ -39,11 +39,12 @@ Feature: The description of a group can be viewed by students and teachers
|
||||
And I press "Save changes"
|
||||
And I add "Student 1 (student1@example.com)" user to "Group A" group members
|
||||
And I add "Student 2 (student2@example.com)" user to "Group B" group members
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to course participants
|
||||
And I click on "Student 1" "link" in the "participants" "table"
|
||||
And I click on "Group A" "link"
|
||||
And I should see "Description for Group A"
|
||||
# As teacher, confirm that group picture is displayed
|
||||
And "//img[@class='grouppicture']" "xpath_element" should exist
|
||||
And ".groupinfobox" "css_element" should exist
|
||||
And I set the field "type" in the "Filter 1" "fieldset" to "Groups"
|
||||
And I set the field "Type or select..." in the "Filter 1" "fieldset" to "Group B"
|
||||
@@ -52,27 +53,23 @@ Feature: The description of a group can be viewed by students and teachers
|
||||
And I click on "Group B" "link"
|
||||
And I should see "Student 2" in the "participants" "table"
|
||||
And ".groupinfobox" "css_element" should not exist
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
When I am on the "Course 1" course page logged in as student1
|
||||
And I navigate to course participants
|
||||
And I click on "Student 1" "link" in the "participants" "table"
|
||||
And I click on "Group A" "link"
|
||||
# As student, confirm that group description and picture is displayed
|
||||
Then I should see "Description for Group A"
|
||||
And I log out
|
||||
And I log in as "student2"
|
||||
And I am on "Course 1" course homepage
|
||||
And "//img[@class='grouppicture']" "xpath_element" should exist
|
||||
And I am on the "Course 1" course page logged in as student2
|
||||
And I navigate to course participants
|
||||
And I click on "Student 2" "link" in the "participants" "table"
|
||||
And I click on "Group B" "link"
|
||||
And I should see "Student 2" in the "participants" "table"
|
||||
And ".groupinfobox" "css_element" should not exist
|
||||
|
||||
@javascript
|
||||
Scenario: A student can not see the group description when separate groups are set. Teachers can see group details.
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
@javascript @_file_upload
|
||||
Scenario: A student can not see the group description and picture when separate groups are set. Teachers can see group details.
|
||||
Given I am on the "Course 1" "course editing" page logged in as "teacher1"
|
||||
And I set the following fields to these values:
|
||||
| Group mode | Separate groups |
|
||||
And I press "Save and display"
|
||||
@@ -81,6 +78,8 @@ Feature: The description of a group can be viewed by students and teachers
|
||||
And I set the following fields to these values:
|
||||
| Group name | Group A |
|
||||
| Group description | Description for Group A |
|
||||
# Upload group picture
|
||||
And I upload "lib/tests/fixtures/gd-logo.png" file to "New picture" filemanager
|
||||
And I press "Save changes"
|
||||
And I press "Create group"
|
||||
And I set the following fields to these values:
|
||||
@@ -88,11 +87,12 @@ Feature: The description of a group can be viewed by students and teachers
|
||||
And I press "Save changes"
|
||||
And I add "Student 1 (student1@example.com)" user to "Group A" group members
|
||||
And I add "Student 2 (student2@example.com)" user to "Group B" group members
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to course participants
|
||||
And I click on "Student 1" "link" in the "participants" "table"
|
||||
And I click on "Group A" "link"
|
||||
And I should see "Description for Group A"
|
||||
# As teacher, confirm that group picture is displayed
|
||||
And "//img[@class='grouppicture']" "xpath_element" should exist
|
||||
And ".groupinfobox" "css_element" should exist
|
||||
And I set the field "type" in the "Filter 1" "fieldset" to "Groups"
|
||||
And I set the field "Type or select..." in the "Filter 1" "fieldset" to "Group B"
|
||||
@@ -100,18 +100,16 @@ Feature: The description of a group can be viewed by students and teachers
|
||||
And I click on "Student 2" "link" in the "participants" "table"
|
||||
And I click on "Group B" "link"
|
||||
And ".groupinfobox" "css_element" should not exist
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
When I am on the "Course 1" course page logged in as student1
|
||||
And I navigate to course participants
|
||||
And I click on "Student 1" "link" in the "participants" "table"
|
||||
And I click on "Group A" "link"
|
||||
And I should see "Student 1" in the "participants" "table"
|
||||
And I should not see "Description for Group A"
|
||||
# As student, confirm that group description and picture are not displayed
|
||||
Then I should not see "Description for Group A"
|
||||
And "//img[@class='grouppicture']" "xpath_element" should not exist
|
||||
And ".groupinfobox" "css_element" should not exist
|
||||
And I log out
|
||||
And I log in as "student2"
|
||||
And I am on "Course 1" course homepage
|
||||
When I am on the "Course 1" course page logged in as student2
|
||||
And I navigate to course participants
|
||||
And I click on "Student 2" "link" in the "participants" "table"
|
||||
And I click on "Group B" "link"
|
||||
@@ -14,15 +14,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Testing the H5PFrameworkInterface interface implementation.
|
||||
*
|
||||
* @package core_h5p
|
||||
* @category test
|
||||
* @copyright 2019 Mihail Geshoski <mihail@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_h5p;
|
||||
|
||||
use core_collator;
|
||||
@@ -32,6 +23,7 @@ use core_collator;
|
||||
* Test class covering the H5PFrameworkInterface interface implementation.
|
||||
*
|
||||
* @package core_h5p
|
||||
* @category test
|
||||
* @copyright 2019 Mihail Geshoski <mihail@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @runTestsInSeparateProcesses
|
||||
@@ -587,7 +579,7 @@ class framework_testcase extends \advanced_testcase {
|
||||
/**
|
||||
* Test the behaviour of isPatchedLibrary().
|
||||
*
|
||||
* @dataProvider test_isPatchedLibrary_provider
|
||||
* @dataProvider isPatchedLibrary_provider
|
||||
* @param array $libraryrecords Array containing data for the library creation
|
||||
* @param array $testlibrary Array containing the test library data
|
||||
* @param bool $expected The expectation whether the library is patched or not
|
||||
@@ -609,7 +601,7 @@ class framework_testcase extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_isPatchedLibrary_provider(): array {
|
||||
public function isPatchedLibrary_provider(): array {
|
||||
return [
|
||||
'Unpatched library. No different versioning' => [
|
||||
[
|
||||
@@ -1365,7 +1357,7 @@ class framework_testcase extends \advanced_testcase {
|
||||
/**
|
||||
* Test the behaviour of loadLibrarySemantics().
|
||||
*
|
||||
* @dataProvider test_loadLibrarySemantics_provider
|
||||
* @dataProvider loadLibrarySemantics_provider
|
||||
* @param array $libraryrecords Array containing data for the library creation
|
||||
* @param array $testlibrary Array containing the test library data
|
||||
* @param string $expected The expected semantics value
|
||||
@@ -1388,7 +1380,7 @@ class framework_testcase extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_loadLibrarySemantics_provider(): array {
|
||||
public function loadLibrarySemantics_provider(): array {
|
||||
|
||||
$semantics = json_encode(
|
||||
[
|
||||
@@ -2157,7 +2149,7 @@ class framework_testcase extends \advanced_testcase {
|
||||
/**
|
||||
* Test the behaviour of test_libraryHasUpgrade().
|
||||
*
|
||||
* @dataProvider test_libraryHasUpgrade_provider
|
||||
* @dataProvider libraryHasUpgrade_provider
|
||||
* @param array $libraryrecords Array containing data for the library creation
|
||||
* @param array $testlibrary Array containing the test library data
|
||||
* @param bool $expected The expectation whether the library is patched or not
|
||||
@@ -2179,7 +2171,7 @@ class framework_testcase extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_libraryHasUpgrade_provider(): array {
|
||||
public function libraryHasUpgrade_provider(): array {
|
||||
return [
|
||||
'Lower major version; Identical lower version' => [
|
||||
[
|
||||
|
||||
@@ -14,23 +14,12 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Test class covering the h5p data generator class.
|
||||
*
|
||||
* @package core_h5p
|
||||
* @category test
|
||||
* @copyright 2019 Mihail Geshoski <mihail@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_h5p;
|
||||
|
||||
use core_h5p\local\library\autoloader;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Generator testcase for the core_grading generator.
|
||||
* Test class covering the h5p data generator class.
|
||||
*
|
||||
* @package core_h5p
|
||||
* @category test
|
||||
@@ -165,7 +154,7 @@ class generator_testcase extends \advanced_testcase {
|
||||
* Test the behaviour of generate_h5p_data(). Test whether library files are created or not
|
||||
* on filesystem depending what the method defines.
|
||||
*
|
||||
* @dataProvider test_generate_h5p_data_files_creation_provider
|
||||
* @dataProvider generate_h5p_data_files_creation_provider
|
||||
* @param bool $createlibraryfiles Whether to create library files on the filesystem
|
||||
* @param bool $expected The expectation whether the files have been created or not
|
||||
**/
|
||||
@@ -205,7 +194,7 @@ class generator_testcase extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_generate_h5p_data_files_creation_provider(): array {
|
||||
public function generate_h5p_data_files_creation_provider(): array {
|
||||
return [
|
||||
'Do not create library related files on the filesystem' => [
|
||||
false,
|
||||
@@ -256,7 +245,7 @@ class generator_testcase extends \advanced_testcase {
|
||||
* Test the behaviour of create_h5p_record(). Test whather the h5p content data is
|
||||
* properly saved in the database.
|
||||
*
|
||||
* @dataProvider test_create_h5p_record_provider
|
||||
* @dataProvider create_h5p_record_provider
|
||||
* @param array $h5pdata The h5p content data
|
||||
* @param \stdClass $expected The expected saved data
|
||||
**/
|
||||
@@ -282,7 +271,7 @@ class generator_testcase extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_create_h5p_record_provider(): array {
|
||||
public function create_h5p_record_provider(): array {
|
||||
$createdjsoncontent = json_encode(
|
||||
array(
|
||||
'text' => '<p>Created dummy text<\/p>\n',
|
||||
@@ -379,7 +368,7 @@ class generator_testcase extends \advanced_testcase {
|
||||
* Test the behaviour of create_contents_libraries_record(). Test whether the contents libraries
|
||||
* are properly saved in the database.
|
||||
*
|
||||
* @dataProvider test_create_contents_libraries_record_provider
|
||||
* @dataProvider create_contents_libraries_record_provider
|
||||
* @param array $contentslibrariestdata The h5p contents libraries data.
|
||||
* @param \stdClass $expected The expected saved data.
|
||||
**/
|
||||
@@ -403,7 +392,7 @@ class generator_testcase extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_create_contents_libraries_record_provider(): array {
|
||||
public function create_contents_libraries_record_provider(): array {
|
||||
return [
|
||||
'Create h5p content library with set dependency type' => [
|
||||
[
|
||||
@@ -439,7 +428,7 @@ class generator_testcase extends \advanced_testcase {
|
||||
* Test the behaviour of create_library_dependency_record(). Test whether the contents libraries
|
||||
* are properly saved in the database.
|
||||
*
|
||||
* @dataProvider test_create_library_dependency_record_provider
|
||||
* @dataProvider create_library_dependency_record_provider
|
||||
* @param array $librarydependencydata The library dependency data.
|
||||
* @param \stdClass $expected The expected saved data.
|
||||
**/
|
||||
@@ -463,7 +452,7 @@ class generator_testcase extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_create_library_dependency_record_provider(): array {
|
||||
public function create_library_dependency_record_provider(): array {
|
||||
return [
|
||||
'Create h5p library dependency with set dependency type' => [
|
||||
[
|
||||
@@ -494,7 +483,7 @@ class generator_testcase extends \advanced_testcase {
|
||||
/**
|
||||
* Test the behaviour of create_content_file(). Test whether a file belonging to a content is created.
|
||||
*
|
||||
* @dataProvider test_create_content_file_provider
|
||||
* @dataProvider create_content_file_provider
|
||||
* @param array $filedata Data from the file to be created.
|
||||
* @param array $expecteddata Data expected.Data from the file to be created.
|
||||
*/
|
||||
@@ -529,7 +518,7 @@ class generator_testcase extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
**/
|
||||
public function test_create_content_file_provider(): array {
|
||||
public function create_content_file_provider(): array {
|
||||
return [
|
||||
'Create file in content with id 4' => [
|
||||
[
|
||||
|
||||
@@ -31,39 +31,49 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*/
|
||||
class core_iplookup_geoplugin_testcase extends advanced_testcase {
|
||||
|
||||
public function setUp() {
|
||||
/**
|
||||
* Load required test libraries
|
||||
*/
|
||||
public static function setUpBeforeClass(): void {
|
||||
global $CFG;
|
||||
require_once("$CFG->libdir/filelib.php");
|
||||
require_once("$CFG->dirroot/iplookup/lib.php");
|
||||
require_once("{$CFG->dirroot}/iplookup/lib.php");
|
||||
}
|
||||
|
||||
/**
|
||||
* In order to execute this test PHPUNIT_LONGTEST should be defined as true in phpunit.xml or directly in config.php
|
||||
*/
|
||||
public function setUp() {
|
||||
if (!PHPUNIT_LONGTEST) {
|
||||
// we do not want to DDOS their server, right?
|
||||
$this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
|
||||
}
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$CFG->geoipfile = '';
|
||||
}
|
||||
|
||||
public function test_ipv4() {
|
||||
/**
|
||||
* Test IPv4 address
|
||||
*
|
||||
* @covers ::iplookup_find_location
|
||||
*/
|
||||
public function test_ipv4(): void {
|
||||
$result = iplookup_find_location('50.0.184.0');
|
||||
|
||||
$this->assertEquals('array', gettype($result));
|
||||
$this->assertEquals('San Francisco', $result['city']);
|
||||
$this->assertEquals(-122.3933, $result['longitude'], 'Coordinates are out of accepted tolerance', 0.1);
|
||||
$this->assertEquals(37.7697, $result['latitude'], 'Coordinates are out of accepted tolerance', 0.1);
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertInternalType('float', $result['latitude']);
|
||||
$this->assertInternalType('float', $result['longitude']);
|
||||
$this->assertInternalType('string', $result['city']);
|
||||
$this->assertInternalType('string', $result['country']);
|
||||
$this->assertInternalType('array', $result['title']);
|
||||
$this->assertInternalType('string', $result['title'][0]);
|
||||
$this->assertInternalType('string', $result['title'][1]);
|
||||
$this->assertNull($result['error']);
|
||||
$this->assertEquals('array', gettype($result['title']));
|
||||
$this->assertEquals('San Francisco', $result['title'][0]);
|
||||
$this->assertEquals('United States', $result['title'][1]);
|
||||
}
|
||||
|
||||
public function test_geoip_ipv6() {
|
||||
/**
|
||||
* Test IPv6 address (unsupported by Geoplugin)
|
||||
*
|
||||
* @covers ::iplookup_find_location
|
||||
*/
|
||||
public function test_ipv6(): void {
|
||||
$result = iplookup_find_location('2a01:8900:2:3:8c6c:c0db:3d33:9ce6');
|
||||
|
||||
$this->assertNotNull($result['error']);
|
||||
$this->assertEquals($result['error'], get_string('invalidipformat', 'error'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-7
@@ -1180,6 +1180,7 @@ $string['sitepolicyhandlerplugin'] = '{$a->name} ({$a->component})';
|
||||
$string['sitepolicyguest'] = 'Site policy URL for guests';
|
||||
$string['sitepolicyguest_help'] = 'The URL of the site policy that all guests must see and agree to before accessing the site. Note that this setting will only have an effect if the site policy handler is set to default (core).';
|
||||
$string['sitesectionhelp'] = 'If selected, a topic section will be displayed on the site\'s front page.';
|
||||
$string['sixtyfourbitsrequired'] = 'It has been detected that your site is not using a 64-bit PHP version. You are required to upgrade your system (operating system, PHP...) before installing this Moodle version.';
|
||||
$string['sixtyfourbitswarning'] = 'It has been detected that your site is not using a 64-bit PHP version. It is recommended that you upgrade your site to ensure future compatibility.';
|
||||
$string['slasharguments'] = 'Use slash arguments';
|
||||
$string['slashargumentswarning'] = 'It is recommended that the use of slash arguments is enabled. In future it will be required. For more details, see the documentation <a href="https://docs.moodle.org/en/admin/environment/slasharguments">Using slash arguments</a>.';
|
||||
@@ -1346,12 +1347,13 @@ $string['unsupporteddbfilepertable'] = 'For full support of UTF-8 both MySQL and
|
||||
$string['unsupporteddblargeprefix'] = 'For full support of UTF-8 both MySQL and MariaDB require you to change your MySQL setting \'innodb_large_prefix\' to \'ON\'. See the documentation for further details.';
|
||||
$string['unsupporteddbstorageengine'] = 'The database storage engine being used is no longer supported.';
|
||||
$string['unsupporteddbtablerowformat'] = 'Your database has tables using Antelope as the file format. You are recommended to convert the tables to the Barracuda file format. See the documentation <a href="https://docs.moodle.org/en/cli">Administration via command line</a> for details of a tool for converting InnoDB tables to Barracuda.';
|
||||
$string['unsupportedphpversion7'] = 'PHP version 7 is not supported.';
|
||||
$string['unsupportedphpversion71'] = 'PHP version 7.1 is not supported.';
|
||||
$string['unsupportedphpversion72'] = 'PHP version 7.2 is not supported.';
|
||||
$string['unsupportedphpversion73'] = 'PHP version 7.3 is not supported.';
|
||||
$string['unsupportedphpversion74'] = 'PHP version 7.4 is not supported.';
|
||||
$string['unsupportedphpversion80'] = 'PHP version 8.0 is not supported.';
|
||||
$string['unsupportedphpversion7'] = 'PHP version 7 and higher are not supported.';
|
||||
$string['unsupportedphpversion71'] = 'PHP version 7.1 and higher are not supported.';
|
||||
$string['unsupportedphpversion72'] = 'PHP version 7.2 and higher are not supported.';
|
||||
$string['unsupportedphpversion73'] = 'PHP version 7.3 and higher are not supported.';
|
||||
$string['unsupportedphpversion74'] = 'PHP version 7.4 and higher are not supported.';
|
||||
$string['unsupportedphpversion80'] = 'PHP version 8.0 and higher are not supported.';
|
||||
$string['unsupportedphpversion81'] = 'PHP version 8.1 and higher are not supported.';
|
||||
$string['unsuspenduser'] = 'Activate user account';
|
||||
$string['updateaccounts'] = 'Update existing accounts';
|
||||
$string['updatecomponent'] = 'Update component';
|
||||
@@ -1458,7 +1460,8 @@ $string['xmlrpcmnetenabled'] = 'It has been detected that the Moodle Networking
|
||||
$string['xmlrpcwebserviceenabled'] = 'It has been detected that the XML-RPC Web Service protocol is enabled on your site. This feature relies on the PHP XML-RPC extension which is no longer maintained by PHP.';
|
||||
$string['yuicomboloading'] = 'YUI combo loading';
|
||||
$string['ziprequired'] = 'The Zip PHP extension is now required by Moodle, info-ZIP binaries or PclZip library are not used anymore.';
|
||||
|
||||
$string['modassignmentinuse'] = 'Your site is still using the Assignment 2.2 plugin. Before upgrading you must 1) backup any Assignment 2.2 activities that you want to keep and restore them as Assignment activities, and 2) delete all Assignment 2.2 data from the database.';
|
||||
$string['modassignmentsubpluginsexist'] = 'Assignment 2.2. subplugins may be present. The mod/assignment folder and all of its subplugins need to be removed before upgrading.';
|
||||
|
||||
$string['caching'] = 'Caching';
|
||||
$string['cachesettings'] = 'Cache settings';
|
||||
|
||||
@@ -180,5 +180,5 @@ $string['XOF'] = 'CFA Franc BCEAO';
|
||||
$string['XPF'] = 'CFP Franc';
|
||||
$string['YER'] = 'Yemeni Rial';
|
||||
$string['ZAR'] = 'South African Rand';
|
||||
$string['ZMK'] = 'Zambian Kwacha';
|
||||
$string['ZMW'] = 'Zambian Kwacha';
|
||||
$string['ZWL'] = 'Zimbabwe Dollar';
|
||||
|
||||
@@ -53,6 +53,7 @@ $string['cutpastemozilla'] = 'Unfortunately, you cannot currently use normal key
|
||||
$string['delete'] = 'Delete';
|
||||
$string['editors'] = 'Text editors';
|
||||
$string['editorsettings'] = 'Manage editors';
|
||||
$string['editorxformat'] = '{$a} format';
|
||||
$string['enterurlfirst'] = 'You have to enter an URL first';
|
||||
$string['filebrowser'] = 'File Browser';
|
||||
$string['findwhat'] = 'Find';
|
||||
|
||||
@@ -112,6 +112,7 @@ $string['america/cayenne'] = 'America/Cayenne';
|
||||
$string['america/cayman'] = 'America/Cayman';
|
||||
$string['america/chicago'] = 'America/Chicago';
|
||||
$string['america/chihuahua'] = 'America/Chihuahua';
|
||||
$string['america/ciudad_juarez'] = 'America/Ciudad_Juarez';
|
||||
$string['america/costa_rica'] = 'America/Costa_Rica';
|
||||
$string['america/creston'] = 'America/Creston';
|
||||
$string['america/cuiaba'] = 'America/Cuiaba';
|
||||
|
||||
@@ -333,6 +333,34 @@ class behat_config_util {
|
||||
return $this->get_component_contexts($component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the list of components contexts.
|
||||
*
|
||||
* This ensures that contexts are sorted consistently.
|
||||
* Core hooks defined in the behat_hooks class _must_ be defined first.
|
||||
*
|
||||
* @param array $contexts
|
||||
* @return array The sorted context list
|
||||
*/
|
||||
protected function sort_component_contexts(array $contexts): array {
|
||||
// Ensure that the lib_tests are first as they include the root of all tests, hooks, and more.
|
||||
uksort($contexts, function($a, $b): int {
|
||||
if ($a === 'behat_hooks') {
|
||||
return -1;
|
||||
}
|
||||
if ($b === 'behat_hooks') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($a == $b) {
|
||||
return 0;
|
||||
}
|
||||
return ($a < $b) ? -1 : 1;
|
||||
});
|
||||
|
||||
return $contexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Behat config file specifing the main context class,
|
||||
* the required Behat extensions and Moodle test wwwroot.
|
||||
@@ -638,7 +666,7 @@ class behat_config_util {
|
||||
[
|
||||
'capabilities' => [
|
||||
'extra_capabilities' => [
|
||||
'chromeOptions' => [
|
||||
'goog:chromeOptions' => [
|
||||
'args' => [
|
||||
'unlimited-storage',
|
||||
'disable-web-security',
|
||||
@@ -650,6 +678,16 @@ class behat_config_util {
|
||||
$values
|
||||
);
|
||||
|
||||
// Selenium no longer supports non-w3c browser control.
|
||||
// Rename chromeOptions to goog:chromeOptions, which is the W3C variant of this.
|
||||
if (array_key_exists('chromeOptions', $values['capabilities']['extra_capabilities'])) {
|
||||
$values['capabilities']['extra_capabilities']['goog:chromeOptions'] = array_merge_recursive(
|
||||
$values['capabilities']['extra_capabilities']['goog:chromeOptions'],
|
||||
$values['capabilities']['extra_capabilities']['chromeOptions']
|
||||
);
|
||||
unset($values['capabilities']['extra_capabilities']['chromeOptions']);
|
||||
}
|
||||
|
||||
// If the mobile app is enabled, check its version and add appropriate tags.
|
||||
if ($mobiletags = $this->get_mobile_version_tags()) {
|
||||
if (!empty($values['tags'])) {
|
||||
@@ -659,13 +697,13 @@ class behat_config_util {
|
||||
}
|
||||
}
|
||||
|
||||
$values['capabilities']['extra_capabilities']['chromeOptions']['args'] = array_map(function($arg): string {
|
||||
$values['capabilities']['extra_capabilities']['goog:chromeOptions']['args'] = array_map(function($arg): string {
|
||||
if (substr($arg, 0, 2) === '--') {
|
||||
return substr($arg, 2);
|
||||
}
|
||||
return $arg;
|
||||
}, $values['capabilities']['extra_capabilities']['chromeOptions']['args']);
|
||||
sort($values['capabilities']['extra_capabilities']['chromeOptions']['args']);
|
||||
}, $values['capabilities']['extra_capabilities']['goog:chromeOptions']['args']);
|
||||
sort($values['capabilities']['extra_capabilities']['goog:chromeOptions']['args']);
|
||||
}
|
||||
|
||||
// Fill tags information.
|
||||
@@ -1537,8 +1575,9 @@ class behat_config_util {
|
||||
}
|
||||
|
||||
// Sort the list of contexts.
|
||||
ksort($contexts);
|
||||
$contexts = $this->sort_component_contexts($contexts);
|
||||
|
||||
// Cache it for subsequent fetches.
|
||||
$this->themecontexts[$theme] = $contexts;
|
||||
|
||||
return $contexts;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
|
||||
|
||||
use Behat\Testwork\Environment\Environment;
|
||||
use Behat\Mink\Exception\DriverException;
|
||||
|
||||
/**
|
||||
* Helper to get behat contexts.
|
||||
@@ -115,6 +116,34 @@ class behat_context_helper {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all Behat contexts which match the specified context class name prefix.
|
||||
*
|
||||
* Moodle uses a consistent class naming scheme for all Behat contexts, whereby the context name is in the format:
|
||||
*
|
||||
* behat_{component}
|
||||
*
|
||||
* This method will return all contexts which match the specified prefix.
|
||||
*
|
||||
* For example, to find all editors, you would pass in 'behat_editor', and this might return:
|
||||
* - behat_editor_atto
|
||||
* - behat_editor_textarea
|
||||
*
|
||||
* @param string $prefix The prefix to search for
|
||||
* @return \Behat\Behat\Context\Context[]
|
||||
*/
|
||||
public static function get_prefixed_contexts(string $prefix): array {
|
||||
if (!is_a(self::$environment, \Behat\Behat\Context\Environment\InitializedContextEnvironment::class)) {
|
||||
throw new DriverException(
|
||||
'Cannot get prefixed contexts - the environment is not an InitializedContextEnvironment'
|
||||
);
|
||||
}
|
||||
|
||||
return array_filter(self::$environment->getContexts(), function($context) use ($prefix): bool {
|
||||
return (strpos(get_class($context), $prefix) === 0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for any theme override of the specified class name.
|
||||
*
|
||||
|
||||
@@ -165,6 +165,15 @@ abstract class behat_generator_base {
|
||||
*/
|
||||
protected abstract function get_creatable_entities(): array;
|
||||
|
||||
/**
|
||||
* Get the list of available generators for this class.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
final public function get_available_generators(): array {
|
||||
return $this->get_creatable_entities();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the work to generate an entity.
|
||||
*
|
||||
|
||||
@@ -30,6 +30,7 @@ use Behat\Mink\Exception\ExpectationException;
|
||||
use Behat\Mink\Exception\ElementNotFoundException;
|
||||
use Behat\Mink\Exception\NoSuchWindowException;
|
||||
use Behat\Mink\Session;
|
||||
use Behat\Testwork\Hook\Scope\HookScope;
|
||||
use Facebook\WebDriver\Exception\ScriptTimeoutException;
|
||||
use Facebook\WebDriver\WebDriverBy;
|
||||
use Facebook\WebDriver\WebDriverElement;
|
||||
@@ -223,6 +224,22 @@ trait behat_session_trait {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a description of the selector and locator to use in an exception message.
|
||||
*
|
||||
* @param string $selector The type of locator
|
||||
* @param mixed $locator The locator text
|
||||
* @return string
|
||||
*/
|
||||
protected function get_selector_description(string $selector, $locator): string {
|
||||
if ($selector === 'NodeElement') {
|
||||
$description = $locator->getText();
|
||||
return "'{$description}' {$selector}";
|
||||
}
|
||||
|
||||
return "'{$locator}' {$selector}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Send key presses straight to the currently active element.
|
||||
*
|
||||
@@ -423,13 +440,12 @@ trait behat_session_trait {
|
||||
if ($containerselectortype === 'NodeElement' && is_a($containerelement, NodeElement::class)) {
|
||||
// Support a NodeElement being passed in for use in step chaining.
|
||||
$containernode = $containerelement;
|
||||
$locatorexceptionmsg = $element;
|
||||
} else {
|
||||
// Gets the container, it will always be text based.
|
||||
$containernode = $this->get_text_selector_node($containerselectortype, $containerelement);
|
||||
$locatorexceptionmsg = $element . '" in the "' . $containerelement. '" "' . $containerselectortype. '"';
|
||||
}
|
||||
|
||||
$locatorexceptionmsg = $element . '" in the "' . $this->get_selector_description($containerselectortype, $containerelement);
|
||||
$exception = new ElementNotFoundException($this->getSession(), $selectortype, null, $locatorexceptionmsg);
|
||||
|
||||
return $this->find($selectortype, $element, $exception, $containernode);
|
||||
@@ -1029,6 +1045,29 @@ EOF;
|
||||
$this->look_for_exceptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a function in a specific behat context.
|
||||
*
|
||||
* For example, to call the 'set_editor_value' function for all editors, you would call:
|
||||
*
|
||||
* behat_base::execute_in_matching_contexts('editor', 'set_editor_value', ['Some value']);
|
||||
*
|
||||
* This would find all behat contexts whose class name starts with 'behat_editor_' and
|
||||
* call the 'set_editor_value' function on that context.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @param string $method
|
||||
* @param array $params
|
||||
*/
|
||||
public static function execute_in_matching_contexts(string $prefix, string $method, array $params): void {
|
||||
$contexts = behat_context_helper::get_prefixed_contexts("behat_{$prefix}_");
|
||||
foreach ($contexts as $context) {
|
||||
if (method_exists($context, $method) && is_callable([$context, $method])) {
|
||||
call_user_func_array([$context, $method], $params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actual user in the behat session (note $USER does not correspond to the behat session's user).
|
||||
* @return mixed
|
||||
@@ -1578,4 +1617,89 @@ EOF;
|
||||
|
||||
return get_fast_modinfo($course)->get_cm($result->cmid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether any of the tags availble to the current scope match using the given callable.
|
||||
*
|
||||
* This function is typically called from within a Behat Hook, such as BeforeFeature, BeforeScenario, AfterStep, etc.
|
||||
*
|
||||
* The callable is used as the second argument to `array_filter()`, and is passed a single string argument for each of the
|
||||
* tags available in the scope.
|
||||
*
|
||||
* The tags passed will include:
|
||||
* - For a FeatureScope, the Feature tags only
|
||||
* - For a ScenarioScope, the Feature and Scenario tags
|
||||
* - For a StepScope, the Feature, Scenario, and Step tags
|
||||
*
|
||||
* An example usage may be:
|
||||
*
|
||||
* // Note: phpDoc beforeStep attribution not shown.
|
||||
* public function before_step(StepScope $scope) {
|
||||
* $callback = function (string $tag): bool {
|
||||
* return $tag === 'editor_atto' || substr($tag, 0, 5) === 'atto_';
|
||||
* };
|
||||
*
|
||||
* if (!self::scope_tags_match($scope, $callback)) {
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* // Do something here.
|
||||
* }
|
||||
*
|
||||
* @param HookScope $scope The scope to check
|
||||
* @param callable $callback The callable to use to check the scope
|
||||
* @return boolean Whether any of the scope tags match
|
||||
*/
|
||||
public static function scope_tags_match(HookScope $scope, callable $callback): bool {
|
||||
$tags = [];
|
||||
|
||||
if (is_subclass_of($scope, \Behat\Behat\Hook\Scope\FeatureScope::class)) {
|
||||
$tags = $scope->getFeature()->getTags();
|
||||
}
|
||||
|
||||
if (is_subclass_of($scope, \Behat\Behat\Hook\Scope\ScenarioScope::class)) {
|
||||
$tags = array_merge(
|
||||
$scope->getFeature()->getTags(),
|
||||
$scope->getScenario()->getTags()
|
||||
);
|
||||
}
|
||||
|
||||
if (is_subclass_of($scope, \Behat\Behat\Hook\Scope\StepScope::class)) {
|
||||
$tags = array_merge(
|
||||
$scope->getFeature()->getTags(),
|
||||
$scope->getScenario()->getTags(),
|
||||
$scope->getStep()->getTags()
|
||||
);
|
||||
}
|
||||
|
||||
$matches = array_filter($tags, $callback);
|
||||
|
||||
return !empty($matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user id from an identifier.
|
||||
*
|
||||
* The user username and email fields are checked.
|
||||
*
|
||||
* @param string $identifier The user's username or email.
|
||||
* @return int|null The user id or null if not found.
|
||||
*/
|
||||
protected function get_user_id_by_identifier(string $identifier): ?int {
|
||||
global $DB;
|
||||
|
||||
$sql = <<<EOF
|
||||
SELECT id
|
||||
FROM {user}
|
||||
WHERE username = :username
|
||||
OR email = :email
|
||||
EOF;
|
||||
|
||||
$result = $DB->get_field_sql($sql, [
|
||||
'username' => $identifier,
|
||||
'email' => $identifier,
|
||||
]);
|
||||
|
||||
return $result ?: null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ class behat_partial_named_selector extends \Behat\Mink\Selector\PartialNamedSele
|
||||
*/
|
||||
protected static $allowedselectors = array(
|
||||
'activity' => 'activity',
|
||||
'actionmenu' => 'actionmenu',
|
||||
'block' => 'block',
|
||||
'button' => 'button',
|
||||
'checkbox' => 'checkbox',
|
||||
@@ -136,6 +137,17 @@ class behat_partial_named_selector extends \Behat\Mink\Selector\PartialNamedSele
|
||||
protected static $moodleselectors = array(
|
||||
'activity' => <<<XPATH
|
||||
.//li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')][descendant::*[contains(normalize-space(.), %locator%)]]
|
||||
XPATH
|
||||
, 'actionmenu' => <<<XPATH
|
||||
.//*[
|
||||
contains(concat(' ', normalize-space(@class), ' '), ' action-menu ')
|
||||
and
|
||||
descendant::*[
|
||||
contains(concat(' ', normalize-space(@class), ' '), ' dropdown-toggle ')
|
||||
and
|
||||
(contains(normalize-space(.), %locator%) or descendant::*[%titleMatch%])
|
||||
]
|
||||
]
|
||||
XPATH
|
||||
, 'block' => <<<XPATH
|
||||
.//*[@data-block][contains(concat(' ', normalize-space(@class), ' '), concat(' ', %locator%, ' ')) or
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Editor interface for setting editor values.
|
||||
*
|
||||
* @package behat
|
||||
* @category test
|
||||
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_behat;
|
||||
|
||||
interface settable_editor {
|
||||
|
||||
/**
|
||||
* Set the editor value.
|
||||
*
|
||||
* @param string $editorid The id of the editor within the page
|
||||
* @param string $value The intended content of the editor
|
||||
*/
|
||||
public function set_editor_value(string $editorid, string $value): void;
|
||||
}
|
||||
@@ -32,7 +32,6 @@ require_once(__DIR__ . '/behat_form_textarea.php');
|
||||
/**
|
||||
* Moodle editor field.
|
||||
*
|
||||
* @todo Support for multiple editors
|
||||
* @package core_form
|
||||
* @category test
|
||||
* @copyright 2012 David Monllaó
|
||||
@@ -44,31 +43,40 @@ class behat_form_editor extends behat_form_textarea {
|
||||
* Sets the value to a field.
|
||||
*
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function set_value($value) {
|
||||
|
||||
public function set_value($value): void {
|
||||
$editorid = $this->field->getAttribute('id');
|
||||
if ($this->running_javascript()) {
|
||||
$value = addslashes($value);
|
||||
// This will be transported in JSON, which doesn't allow newlines in strings, so we must escape them.
|
||||
$value = str_replace("\n", "\\n", $value);
|
||||
$js = '
|
||||
(function() {
|
||||
var editor = Y.one(document.getElementById("'.$editorid.'editable"));
|
||||
if (editor) {
|
||||
editor.setHTML("' . $value . '");
|
||||
}
|
||||
editor = Y.one(document.getElementById("'.$editorid.'"));
|
||||
editor.set("value", "' . $value . '");
|
||||
})();
|
||||
';
|
||||
behat_base::execute_script_in_session($this->session, $js);
|
||||
behat_base::execute_in_matching_contexts('editor', 'set_editor_value', [
|
||||
$editorid,
|
||||
$value,
|
||||
]);
|
||||
|
||||
} else {
|
||||
parent::set_value($value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the select element.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_value(): string {
|
||||
if ($this->running_javascript()) {
|
||||
// Give any listening editors a chance to persist the value to the textarea.
|
||||
// Some editors only do this on form submission or similar events.
|
||||
behat_base::execute_in_matching_contexts('editor', 'store_current_value', [
|
||||
$this->field->getAttribute('id'),
|
||||
]);
|
||||
}
|
||||
|
||||
return parent::get_value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Select all the text in the form field.
|
||||
*
|
||||
@@ -102,8 +110,60 @@ class behat_form_editor extends behat_form_textarea {
|
||||
* @return bool The provided value matches the field value?
|
||||
*/
|
||||
public function matches($expectedvalue) {
|
||||
// A text editor may silently wrap the content in p tags (or not). Neither is an error.
|
||||
return $this->text_matches($expectedvalue) || $this->text_matches('<p>' . $expectedvalue . '</p>');
|
||||
// Fetch the actual value to save fetching it multiple times.
|
||||
$actualvalue = $this->get_value();
|
||||
|
||||
if ($this->text_matches($expectedvalue, $actualvalue)) {
|
||||
// The text is an exact match already.
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->text_matches("<p>{$expectedvalue}</p>", $actualvalue)) {
|
||||
// A text editor may silently wrap the content in p tags.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Standardise both the expected value and the actual field value.
|
||||
// We are likely dealing with HTML content, given this is an editor.
|
||||
$expectedvalue = $this->standardise_html($expectedvalue);
|
||||
$actualvalue = $this->standardise_html($actualvalue);
|
||||
|
||||
// Note: We don't need to worry about the floats here that we care about in text_matches.
|
||||
// That condition isn't relevant to the content of an editor.
|
||||
if ($expectedvalue === $actualvalue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standardises the HTML content for comparison.
|
||||
*
|
||||
* @param string $html The HTML content to standardise
|
||||
* @return string The standardised HTML content
|
||||
*/
|
||||
protected function standardise_html(string $html): string {
|
||||
$document = new DOMDocument();
|
||||
$errorstate = libxml_use_internal_errors(true);
|
||||
|
||||
// Format the whitespace nicely.
|
||||
$document->preserveWhiteSpace = false;
|
||||
$document->formatOutput = true;
|
||||
|
||||
// Wrap the content in a DIV element so that it is not parsed weirdly.
|
||||
// Note: We must remove newlines too because DOMDocument does not do so, despite preserveWhiteSpace being false.
|
||||
// Unfortunately this is slightly limited in that it will also remove newlines from <pre> content and similar.
|
||||
$document->loadHTML(str_replace("\n", "", "<div>{$html}</div>"), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||
$document->normalizeDocument();
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors($errorstate);
|
||||
|
||||
// Save the content of the 'div' element, removing the <div> and </div> tags at the start and end.
|
||||
return trim(substr(
|
||||
$document->saveHTML($document->getElementsByTagName('div')->item(0)),
|
||||
5,
|
||||
-6
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -244,10 +244,14 @@ class behat_form_field implements behat_session_interface {
|
||||
* Checks if the provided text matches the field value.
|
||||
*
|
||||
* @param string $expectedvalue
|
||||
* @param string|null $actualvalue The actual value. If not specified, this will be fetched from $this->get_value().
|
||||
* @return bool
|
||||
*/
|
||||
protected function text_matches($expectedvalue) {
|
||||
if (trim($expectedvalue) != trim($this->get_value())) {
|
||||
protected function text_matches($expectedvalue, ?string $actualvalue = null): bool {
|
||||
$actualvalue = $actualvalue ?? $this->get_value();
|
||||
|
||||
// Non strict string comparison.
|
||||
if (trim($expectedvalue) != trim($actualvalue)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
+1
-1
@@ -138,7 +138,7 @@ function behat_error_handler($errno, $errstr, $errfile, $errline, $errcontext) {
|
||||
// set to DEVELOPER and will always include E_NOTICE,E_USER_NOTICE... as part of E_ALL, if the current
|
||||
// error_reporting() value does not include one of those levels is because it has been forced through
|
||||
// the moodle code (see fix_utf8() for example) in that cases we respect the forced error level value.
|
||||
$respect = array(E_NOTICE, E_USER_NOTICE, E_STRICT, E_WARNING, E_USER_WARNING);
|
||||
$respect = array(E_NOTICE, E_USER_NOTICE, E_STRICT, E_WARNING, E_USER_WARNING, E_DEPRECATED, E_USER_DEPRECATED);
|
||||
foreach ($respect as $respectable) {
|
||||
|
||||
// If the current value does not include this kind of errors and the reported error is
|
||||
|
||||
@@ -68,7 +68,6 @@ class mustache_pix_helper {
|
||||
if (!$component) {
|
||||
$component = '';
|
||||
}
|
||||
$component = $helper->render($component);
|
||||
$text = strtok("");
|
||||
// Allow mustache tags in the last argument.
|
||||
$text = trim($helper->render($text));
|
||||
|
||||
@@ -991,8 +991,9 @@ class core_user {
|
||||
$preferences['user_home_page_preference'] = array('type' => PARAM_INT, 'null' => NULL_ALLOWED, 'default' => HOMEPAGE_MY,
|
||||
'choices' => array(HOMEPAGE_SITE, HOMEPAGE_MY),
|
||||
'permissioncallback' => function ($user, $preferencename) {
|
||||
global $CFG;
|
||||
return (!empty($CFG->defaulthomepage) && ($CFG->defaulthomepage == HOMEPAGE_USER));
|
||||
global $CFG, $USER;
|
||||
return $user->id == $USER->id &&
|
||||
(!empty($CFG->defaulthomepage) && ($CFG->defaulthomepage == HOMEPAGE_USER));
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -242,6 +242,10 @@ function cron_run_inner_scheduled_task(\core\task\task_base $task) {
|
||||
$predbqueries = null;
|
||||
$predbqueries = $DB->perf_get_queries();
|
||||
$pretime = microtime(1);
|
||||
|
||||
// Ensure that we have a clean session with the correct cron user.
|
||||
cron_setup_user();
|
||||
|
||||
try {
|
||||
get_mailer('buffer');
|
||||
cron_prepare_core_renderer();
|
||||
@@ -325,6 +329,10 @@ function cron_run_inner_adhoc_task(\core\task\adhoc_task $task) {
|
||||
}
|
||||
|
||||
cron_setup_user($user);
|
||||
} else {
|
||||
// No user specified, ensure that we have a clean session with the correct cron user.
|
||||
cron_setup_user();
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -2258,7 +2258,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
*
|
||||
* @return array The type-value pair fixture.
|
||||
*/
|
||||
public function test_get_enc_quoted_provider() {
|
||||
public function get_enc_quoted_provider() {
|
||||
return array(
|
||||
// Reserved: some examples from SQL-92.
|
||||
[true, 'from'],
|
||||
@@ -2272,7 +2272,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
/**
|
||||
* This is a test for sql_generator::getEncQuoted().
|
||||
*
|
||||
* @dataProvider test_get_enc_quoted_provider
|
||||
* @dataProvider get_enc_quoted_provider
|
||||
* @param bool $reserved Whether the column name is reserved or not.
|
||||
* @param string $columnname The column name to be quoted, according to the value of $reserved.
|
||||
**/
|
||||
@@ -2307,7 +2307,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
*
|
||||
* @return array The type-old-new tuple fixture.
|
||||
*/
|
||||
public function test_sql_generator_get_rename_field_sql_provider() {
|
||||
public function sql_generator_get_rename_field_sql_provider() {
|
||||
return array(
|
||||
// Reserved: an example from SQL-92.
|
||||
// Both names should be reserved.
|
||||
@@ -2320,7 +2320,7 @@ class core_ddl_testcase extends database_driver_testcase {
|
||||
/**
|
||||
* This is a unit test for sql_generator::getRenameFieldSQL().
|
||||
*
|
||||
* @dataProvider test_sql_generator_get_rename_field_sql_provider
|
||||
* @dataProvider sql_generator_get_rename_field_sql_provider
|
||||
* @param bool $reserved Whether the column name is reserved or not.
|
||||
* @param string $oldcolumnname The column name to be renamed.
|
||||
* @param string $newcolumnname The new column name.
|
||||
|
||||
@@ -208,21 +208,28 @@ class sqlsrv_native_moodle_database extends moodle_database {
|
||||
|
||||
$this->store_settings($dbhost, $dbuser, $dbpass, $dbname, $prefix, $dboptions);
|
||||
|
||||
$options = [
|
||||
'UID' => $this->dbuser,
|
||||
'PWD' => $this->dbpass,
|
||||
'Database' => $this->dbname,
|
||||
'CharacterSet' => 'UTF-8',
|
||||
'MultipleActiveResultSets' => true,
|
||||
'ConnectionPooling' => !empty($this->dboptions['dbpersist']),
|
||||
'ReturnDatesAsStrings' => true,
|
||||
];
|
||||
|
||||
$dbhost = $this->dbhost;
|
||||
if (!empty($dboptions['dbport'])) {
|
||||
$dbhost .= ',' . $dboptions['dbport'];
|
||||
}
|
||||
|
||||
$this->sqlsrv = sqlsrv_connect($dbhost, array
|
||||
(
|
||||
'UID' => $this->dbuser,
|
||||
'PWD' => $this->dbpass,
|
||||
'Database' => $this->dbname,
|
||||
'CharacterSet' => 'UTF-8',
|
||||
'MultipleActiveResultSets' => true,
|
||||
'ConnectionPooling' => !empty($this->dboptions['dbpersist']),
|
||||
'ReturnDatesAsStrings' => true,
|
||||
));
|
||||
// The sqlsrv_connect() has a lot of connection options to be used.
|
||||
// Users can add any supported options with the 'extrainfo' key in the dboptions.
|
||||
if (isset($this->dboptions['extrainfo'])) {
|
||||
$options = array_merge($options, $this->dboptions['extrainfo']);
|
||||
}
|
||||
|
||||
$this->sqlsrv = sqlsrv_connect($dbhost, $options);
|
||||
|
||||
if ($this->sqlsrv === false) {
|
||||
$this->sqlsrv = null;
|
||||
|
||||
@@ -21,7 +21,7 @@ Feature: Atto Autosave
|
||||
And I click on "Save changes" "button"
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I set the field with xpath "//select[@name='summary_editor[format]']" to "1"
|
||||
And I set the field "Course summary format" to "1"
|
||||
And I click on "Save and display" "button"
|
||||
And I log out
|
||||
|
||||
|
||||
@@ -23,9 +23,12 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
|
||||
|
||||
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
|
||||
|
||||
require_once(__DIR__ . '/../../../../behat/behat_base.php');
|
||||
require_once(__DIR__ . '/../../../../behat/classes/settable_editor.php');
|
||||
|
||||
/**
|
||||
* Steps definitions to deal with the atto text editor
|
||||
@@ -35,7 +38,30 @@ require_once(__DIR__ . '/../../../../behat/behat_base.php');
|
||||
* @copyright 2014 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class behat_editor_atto extends behat_base {
|
||||
class behat_editor_atto extends behat_base implements \core_behat\settable_editor {
|
||||
|
||||
/**
|
||||
* Set the value for the editor.
|
||||
*
|
||||
* @param string $editorid
|
||||
* @param string $value
|
||||
*/
|
||||
public function set_editor_value(string $editorid, string $value): void {
|
||||
$js = <<<EOF
|
||||
(function() {
|
||||
const editableEditor = document.getElementById("${editorid}editable");
|
||||
if (editableEditor && editableEditor.classList.contains('editor_atto_content')) {
|
||||
editableEditor.innerHTML = "${value}";
|
||||
}
|
||||
const editor = document.getElementById("${editorid}");
|
||||
if (editor) {
|
||||
editor.value = "${value}";
|
||||
}
|
||||
})();
|
||||
EOF;
|
||||
|
||||
$this->execute_script($js);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the text in an Atto field.
|
||||
@@ -59,6 +85,24 @@ class behat_editor_atto extends behat_base {
|
||||
$field->select_text();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the Atto editor is used for all tests using the editor_atto, or atto_* tags.
|
||||
*
|
||||
* This ensures, whatever the default editor, that the Atto editor is used for these tests.
|
||||
*
|
||||
* @BeforeScenario
|
||||
* @param BeforeScenarioScope $scope The Behat Scope
|
||||
*/
|
||||
public function set_default_editor_flag(BeforeScenarioScope $scope): void {
|
||||
// This only applies to a scenario which matches the editor_atto, or an atto subplugin.
|
||||
$callback = function (string $tag): bool {
|
||||
return $tag === 'editor_atto' || substr($tag, 0, 5) === 'atto_';
|
||||
};
|
||||
|
||||
if (!self::scope_tags_match($scope, $callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->execute('behat_general::the_default_editor_is_set_to', ['atto']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Moodle textarea editor field.
|
||||
*
|
||||
* @package core_form
|
||||
* @category test
|
||||
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once(__DIR__ . '/../../../../behat/behat_base.php');
|
||||
require_once(__DIR__ . '/../../../../behat/classes/settable_editor.php');
|
||||
|
||||
class behat_editor_textarea extends behat_base implements \core_behat\settable_editor {
|
||||
|
||||
/**
|
||||
* Set the value for the editor.
|
||||
*
|
||||
* @param string $editorid
|
||||
* @param string $value
|
||||
*/
|
||||
public function set_editor_value(string $editorid, string $value): void {
|
||||
$js = <<<EOF
|
||||
(function() {
|
||||
const editor = document.getElementById("${editorid}");
|
||||
if (editor && editor.tagName.toLowerCase() === 'textarea') {
|
||||
editor.value = "${value}";
|
||||
}
|
||||
})();
|
||||
EOF;
|
||||
$this->execute_script($js);
|
||||
}
|
||||
}
|
||||
@@ -1657,3 +1657,14 @@ function restrict_php_version_74(&$result) {
|
||||
function restrict_php_version_80($result) {
|
||||
return restrict_php_version($result, '8.0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current PHP version is greater than or equal to
|
||||
* PHP version 8.1
|
||||
*
|
||||
* @param object $result an environment_results instance
|
||||
* @return bool result of version check
|
||||
*/
|
||||
function restrict_php_version_81($result) {
|
||||
return restrict_php_version($result, '8.1');
|
||||
}
|
||||
|
||||
+5
-1
@@ -3265,7 +3265,11 @@ class curl {
|
||||
throw new coding_exception('Curl options should be defined using strings, not constant values.');
|
||||
}
|
||||
if (stripos($name, 'CURLOPT_') === false) {
|
||||
$name = strtoupper('CURLOPT_'.$name);
|
||||
// Only prefix with CURLOPT_ if the option doesn't contain CURLINFO_,
|
||||
// which is a valid prefix for at least one option CURLINFO_HEADER_OUT.
|
||||
if (stripos($name, 'CURLINFO_') === false) {
|
||||
$name = strtoupper('CURLOPT_'.$name);
|
||||
}
|
||||
} else {
|
||||
$name = strtoupper($name);
|
||||
}
|
||||
|
||||
@@ -64,6 +64,9 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element implements templatab
|
||||
/** @var array values for editor */
|
||||
protected $_values = array('text'=>null, 'format'=>null, 'itemid'=>null);
|
||||
|
||||
/** @var bool if true label will be hidden */
|
||||
protected $_hiddenLabel = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@@ -441,6 +444,7 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element implements templatab
|
||||
$context['id'] = $id;
|
||||
$context['value'] = $text;
|
||||
$context['format'] = $format;
|
||||
$context['formatlabel'] = get_string('editorxformat', 'editor', $this->_label);
|
||||
|
||||
if (!is_null($this->getAttribute('onblur')) && !is_null($this->getAttribute('onchange'))) {
|
||||
$context['changelistener'] = true;
|
||||
@@ -496,4 +500,14 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element implements templatab
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets label to be hidden.
|
||||
*
|
||||
* @param bool $hiddenLabel Whether the label should be hidden or not.
|
||||
* @return void
|
||||
*/
|
||||
function setHiddenLabel($hiddenLabel) {
|
||||
$this->_hiddenLabel = $hiddenLabel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
</div>
|
||||
<div>
|
||||
{{#hasformats}}
|
||||
<label for="menu{{name}}format" class="sr-only">{{formatlabel}}</label>
|
||||
<select name="{{name}}[format]" id="menu{{name}}format" class="custom-select">
|
||||
{{#formats}}
|
||||
<option value="{{value}}" {{#selected}}selected{{/selected}}>{{text}}</option>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{{< core_form/element-template-inline }}
|
||||
{{$element}}
|
||||
{{{element.html}}}
|
||||
{{/element}}
|
||||
{{/ core_form/element-template-inline }}
|
||||
@@ -16,6 +16,8 @@ These changes can be reverted once the following pull requests have been integra
|
||||
This local changes can be reverted once it's checked that they are present upstream (note the
|
||||
LTI-Tool-Provider-Library-PHP repo has been archived so it doesn't accept pull requests anymore):
|
||||
* MDL-67034 php74 compliance fixes
|
||||
* MDL-71920: Migrated from curl_exec and friends to use our Moodle curl wrapper,
|
||||
so we can better handle site security settings
|
||||
|
||||
It is recommended by upstream to install depdencies via composer - but the composer installation is bundled
|
||||
with an autoloader so it's better to do it manually.
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace IMSGlobal\LTI;
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/filelib.php');
|
||||
|
||||
/**
|
||||
* Class to represent an HTTP message
|
||||
*
|
||||
@@ -110,31 +113,33 @@ class HTTPMessage
|
||||
{
|
||||
|
||||
$this->ok = false;
|
||||
// Try using curl if available
|
||||
// Try using curl if available
|
||||
if (function_exists('curl_init')) {
|
||||
$resp = '';
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $this->url);
|
||||
$chResp = '';
|
||||
|
||||
$curl = new \curl();
|
||||
$options = [
|
||||
'CURLOPT_HEADER' => true,
|
||||
'CURLINFO_HEADER_OUT' => true,
|
||||
];
|
||||
if (!empty($this->requestHeaders)) {
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->requestHeaders);
|
||||
$options['CURLOPT_HTTPHEADER'] = $this->requestHeaders;
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_HEADER, 0);
|
||||
$options['CURLOPT_HEADER'] = 0;
|
||||
}
|
||||
if ($this->method === 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $this->request);
|
||||
$chResp = $curl->post($this->url, $this->request, $options);
|
||||
} else if ($this->method !== 'GET') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->method);
|
||||
if (!is_null($this->request)) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $this->request);
|
||||
$chResp = $curl->post($this->url, $this->request, $options);
|
||||
}
|
||||
} else {
|
||||
$chResp = $curl->get($this->url, null, $options);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
$chResp = curl_exec($ch);
|
||||
$this->ok = $chResp !== false;
|
||||
if ($this->ok) {
|
||||
$info = $curl->get_info();
|
||||
|
||||
if (!$curl->get_errno() && !$curl->error) {
|
||||
$chResp = str_replace("\r\n", "\n", $chResp);
|
||||
$chRespSplit = explode("\n\n", $chResp, 2);
|
||||
if ((count($chRespSplit) > 1) && (substr($chRespSplit[1], 0, 5) === 'HTTP/')) {
|
||||
@@ -142,17 +147,20 @@ class HTTPMessage
|
||||
}
|
||||
$this->responseHeaders = $chRespSplit[0];
|
||||
$resp = $chRespSplit[1];
|
||||
$this->status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$this->status = $info['http_code'];
|
||||
$this->ok = $this->status < 400;
|
||||
if (!$this->ok) {
|
||||
$this->error = curl_error($ch);
|
||||
$this->error = $curl->error;
|
||||
}
|
||||
} else {
|
||||
$this->error = $curl->error;
|
||||
$resp = $chResp;
|
||||
}
|
||||
$this->requestHeaders = str_replace("\r\n", "\n", curl_getinfo($ch, CURLINFO_HEADER_OUT));
|
||||
curl_close($ch);
|
||||
|
||||
$this->response = $resp;
|
||||
$this->requestHeaders = !empty($info['request_header']) ? str_replace("\r\n", "\n", $info['request_header']) : '';
|
||||
} else {
|
||||
// Try using fopen if curl was not available
|
||||
// Try using fopen if curl was not available
|
||||
$opts = array('method' => $this->method,
|
||||
'content' => $this->request
|
||||
);
|
||||
|
||||
+2
-1
@@ -1063,8 +1063,9 @@ function clean_param($param, $type) {
|
||||
} else if (preg_match('/^' . preg_quote($CFG->wwwroot . '/', '/') . '/i', $param)) {
|
||||
// Absolute, and matches our wwwroot.
|
||||
} else {
|
||||
|
||||
// Relative - let's make sure there are no tricks.
|
||||
if (validateUrlSyntax('/' . $param, 's-u-P-a-p-f+q?r?')) {
|
||||
if (validateUrlSyntax('/' . $param, 's-u-P-a-p-f+q?r?') && !preg_match('/javascript:/i', $param)) {
|
||||
// Looks ok.
|
||||
} else {
|
||||
$param = '';
|
||||
|
||||
@@ -702,7 +702,7 @@ abstract class advanced_testcase extends base_testcase {
|
||||
}
|
||||
|
||||
cron_prepare_core_renderer();
|
||||
$this->setUser($user);
|
||||
cron_setup_user($user);
|
||||
|
||||
$task->execute();
|
||||
\core\task\manager::adhoc_task_complete($task);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user