Compare commits
134 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ec02c2a67 | |||
| 8a709503ac | |||
| 2c7f5745c6 | |||
| 9a18af46d7 | |||
| e09160b6db | |||
| 884a9b9706 | |||
| 753b2fb53e | |||
| 7806835569 | |||
| a937101b06 | |||
| 8596b6a299 | |||
| b5c8188961 | |||
| ed3ee9dd5c | |||
| 87aaffc3d1 | |||
| 4fae57fc97 | |||
| 9b9237fde1 | |||
| 6f3450ef1e | |||
| 089f5bb0ec | |||
| 0391a4afc7 | |||
| 0b26fce0b6 | |||
| 361070ef5d | |||
| 81cb18a67c | |||
| f4b92ca799 | |||
| e563d32ca2 | |||
| 2575add511 | |||
| 61b3c4b453 | |||
| d82d2bd6d6 | |||
| a7a8db38aa | |||
| a4e45e8159 | |||
| 814c80da13 | |||
| f7ffb41db4 | |||
| c9efe1b1ac | |||
| 82c845d8d3 | |||
| 30da75a460 | |||
| fdb7aa8035 | |||
| 4e14e2631c | |||
| 2ae147c9af | |||
| 6cacc0a4a3 | |||
| c66c1a04b5 | |||
| 06001d9c9d | |||
| 9f01ded64e | |||
| 7bd088e443 | |||
| f67020d1c2 | |||
| 2bc1e50886 | |||
| a2fe75a270 | |||
| 6b632fef09 | |||
| bc3e140d8b | |||
| b53f12d8c9 | |||
| f9c700eb9f | |||
| 0d3495e6b2 | |||
| 72eac30148 | |||
| e19f451723 | |||
| db0073291b | |||
| 984e3fc510 | |||
| 698142b1f5 | |||
| 8669b0ebb7 | |||
| 2204ea8afe | |||
| 142e12bee7 | |||
| 2bdebbf6e7 | |||
| f47ec5d0a9 | |||
| 52da2d6eaa | |||
| 78b0f6e204 | |||
| 7030fa5972 | |||
| 2bd53efc1b | |||
| 9ccb3a2af9 | |||
| 3354f46fd6 | |||
| 9fb1e3d300 | |||
| e4b11bf852 | |||
| 28cf3f9119 | |||
| 1a885a1756 | |||
| 753035e090 | |||
| cb98541563 | |||
| 2729301721 | |||
| 54a2d7df22 | |||
| 59f97a627f | |||
| 362c67f84d | |||
| cbe5ec5a51 | |||
| 201eff4d98 | |||
| 22e52ebb55 | |||
| 493d3c218e | |||
| 97ff875c83 | |||
| 8a5601722d | |||
| 68aa19ba73 | |||
| c8d12c3217 | |||
| f9d08701fb | |||
| 324e1fbb78 | |||
| b58a4e8da4 | |||
| e7c0ddb942 | |||
| e9d63ac29c | |||
| fce440eeed | |||
| 5fae2fc7a1 | |||
| 913eb31830 | |||
| 68fbcaf303 | |||
| 20403e0e43 | |||
| 2740e06920 | |||
| a84b51046a | |||
| a19092e71c | |||
| b41a748a98 | |||
| cd96cb300e | |||
| e3c031023f | |||
| c5b4da7257 | |||
| 7edc33a8ce | |||
| 61e9318339 | |||
| f254b2b88f | |||
| 264fe69743 | |||
| 80e2f8473e | |||
| b9b5f4b489 | |||
| dc871f251e | |||
| f972485484 | |||
| 1f9488ccb5 | |||
| 1d8512bc0e | |||
| b751583087 | |||
| 94b897a176 | |||
| 4c6b771c2f | |||
| b8122e1de3 | |||
| a9eae7edf4 | |||
| a7a9150b62 | |||
| c283ed04e0 | |||
| f65e706662 | |||
| 49e1731fbe | |||
| cd320b32b5 | |||
| 8dea153c6b | |||
| ae9fea0b47 | |||
| dc9258574d | |||
| 5b94aaed06 | |||
| a099c8d921 | |||
| 36a3c41b8b | |||
| d7208f8dd0 | |||
| df9c7f755d | |||
| a76583296b | |||
| b56affcfe6 | |||
| 4799f8b7c4 | |||
| 62758b8224 | |||
| cf6aeae427 | |||
| 8ea5b30a5d |
@@ -55,9 +55,16 @@ jobs:
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
php: 7.2
|
||||
# Need to disable both redis and memcached (both are optional) in order to keep igbinary installed.
|
||||
# (there are dependencies that become broken if we just switch the igbinary version).
|
||||
# Need to do this for php 7.2 because of a bug with current default igbinary
|
||||
# versions used by GHA being >= 3.2.2 and <= 3.2.4 (buggy ones).
|
||||
# TODO: MDL-72399 - Revert this patch once igbinary 3.2.5 or up is the default @ GHA.
|
||||
extensions: :redis, :memcached, igbinary-3.2.6
|
||||
db: mysqli
|
||||
- os: ubuntu-18.04
|
||||
php: 7.4
|
||||
extensions:
|
||||
db: pgsql
|
||||
|
||||
steps:
|
||||
@@ -88,6 +95,7 @@ jobs:
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: ${{ matrix.extensions }}
|
||||
coverage: none
|
||||
|
||||
- name: Checking out code from ${{ env.GITHUB_REF_SLUG }}
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ language: php
|
||||
|
||||
os: linux
|
||||
|
||||
dist: xenial
|
||||
dist: bionic
|
||||
|
||||
services:
|
||||
- mysql
|
||||
|
||||
@@ -3163,6 +3163,11 @@
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_igbinary322_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="igbinary322buggyversion" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.10" requires="3.5">
|
||||
@@ -3345,6 +3350,11 @@
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_igbinary322_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="igbinary322buggyversion" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.11" requires="3.6">
|
||||
@@ -3531,4 +3541,189 @@
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="4.0" requires="3.6">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="10.2.29" />
|
||||
<VENDOR name="mysql" version="5.7" />
|
||||
<VENDOR name="postgres" version="10" />
|
||||
<VENDOR name="mssql" version="14.0" />
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.3.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="mbstringrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
<PHP_EXTENSION name="sodium" level="optional"/>
|
||||
<PHP_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="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_max_input_vars" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
@@ -53,11 +53,8 @@ class behat_admin extends behat_base {
|
||||
}
|
||||
|
||||
foreach ($data as $label => $value) {
|
||||
$this->execute('behat_navigation::i_select_from_flat_navigation_drawer', [get_string('administrationsite')]);
|
||||
|
||||
// Search by label.
|
||||
$this->execute('behat_forms::i_set_the_field_to', [get_string('query', 'admin'), $label]);
|
||||
$this->execute("behat_forms::press_button", get_string('search', 'admin'));
|
||||
// Navigate straight to the search results fo rthis label.
|
||||
$this->execute('behat_general::i_visit', ["/admin/search.php?query=" . urlencode($label)]);
|
||||
|
||||
// Admin settings does not use the same DOM structure than other moodle forms
|
||||
// but we also need to use lib/behat/form_field/* to deal with the different moodle form elements.
|
||||
|
||||
@@ -48,7 +48,7 @@ Feature: Verify that all form fields values can be get and set
|
||||
And the "Unenrol users" select box should not contain "Baker"
|
||||
And the "Unenrol users" select box should not contain "President, Baker"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I follow "Test this one"
|
||||
And I am on the "Test this one" "wiki activity" page
|
||||
And I press "Create page"
|
||||
# Text (textarea & editor) & Select (multi-select) - Checking "I set the following fields to these values".
|
||||
When I set the following fields to these values:
|
||||
@@ -61,8 +61,7 @@ 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 should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I am on the "Test this one" "wiki activity" page
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
And the field "Force format" matches value "1"
|
||||
@@ -70,8 +69,7 @@ Feature: Verify that all form fields values can be get and set
|
||||
# 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 should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I am on the "Test this one" "wiki activity" page
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
And the field "Force format" matches value ""
|
||||
@@ -80,8 +78,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 should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I am on the "Test this one" "wiki activity" page
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
And the following fields match these values:
|
||||
@@ -92,8 +89,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 should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I am on the "Test this one" "wiki activity" page
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
And the following fields match these values:
|
||||
@@ -115,20 +111,21 @@ Feature: Verify that all form fields values can be get and set
|
||||
| Wiki name | Test this one baby |
|
||||
| Default format | HTML |
|
||||
And I press "Cancel"
|
||||
And I am on "Course 1" course homepage
|
||||
# Radio - Checking "I set the field" and "the field matches value".
|
||||
And I add a "Choice" to section "1" and I fill the form with:
|
||||
| Choice name | Test choice name |
|
||||
| Description | Test choice description |
|
||||
| Allow choice to be updated | Yes |
|
||||
| Option 1 | one |
|
||||
| Option 2 | two |
|
||||
| Option 3 | three |
|
||||
And I follow "Test choice name"
|
||||
And I set the field "one" to "1"
|
||||
And the following "activity" exists:
|
||||
| activity | choice |
|
||||
| course | C1 |
|
||||
| idnumber | choice1 |
|
||||
| intro | Test choice description |
|
||||
| name | Test choice name |
|
||||
| option | Option 1, Option 2, Option 3 |
|
||||
| section | 1 |
|
||||
| allowupdate | 1 |
|
||||
And I am on the "Test choice name" "choice activity" page
|
||||
And I set the field "Option 1" to "1"
|
||||
And I press "Save my choice"
|
||||
And the field "one" matches value "1"
|
||||
And the field "two" matches value ""
|
||||
And the field "Option 1" matches value "1"
|
||||
And the field "Option 2" matches value ""
|
||||
# Check if field xpath set/match works.
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
@@ -174,31 +171,23 @@ Feature: Verify that all form fields values can be get and set
|
||||
And I press "Cancel"
|
||||
# Advanced checkbox requires real browser to allow uncheck to work. MDL-58681. MDL-55386.
|
||||
# Advanced checkbox - Checking "I set the field" and "The field matches value" ticked.
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test this one"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test choice name" "choice activity editing" page
|
||||
And I set the field "Display description on course page" to "1"
|
||||
And I press "Save and return to course"
|
||||
And I should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test choice name" "choice activity editing" page
|
||||
And the field "Display description on course page" matches value "1"
|
||||
And the field "Display description on course page" does not match value ""
|
||||
# Advanced checkbox - Checking "I set the field" and "The field matches value" unticked.
|
||||
And I set the field "Display description on course page" to ""
|
||||
And I press "Save and return to course"
|
||||
And I should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test choice name" "choice activity editing" page
|
||||
And the field "Display description on course page" matches value ""
|
||||
And the field "Display description on course page" does not match value "1"
|
||||
# Advanced checkbox - Checking "I set the following fields to these values:" and "The following fields match these values" ticked.
|
||||
And I set the following fields to these values:
|
||||
| Display description on course page | 1 |
|
||||
And I press "Save and return to course"
|
||||
And I should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test choice name" "choice activity editing" page
|
||||
And the following fields match these values:
|
||||
| Display description on course page | 1 |
|
||||
And the following fields do not match these values:
|
||||
@@ -207,9 +196,7 @@ Feature: Verify that all form fields values can be get and set
|
||||
And I set the following fields to these values:
|
||||
| Display description on course page | |
|
||||
And I press "Save and return to course"
|
||||
And I should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test choice name" "choice activity editing" page
|
||||
And the following fields match these values:
|
||||
| Display description on course page | |
|
||||
And the following fields do not match these values:
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
@tool @tool_behat
|
||||
Feature: Use core page resolvers for the I am on the page steps
|
||||
In order to write tests correctly
|
||||
As a developer
|
||||
I need to have steps which take me straight to a page
|
||||
|
||||
Scenario Outline: When I am on an instance
|
||||
Given the following "categories" exist:
|
||||
| name | category | idnumber |
|
||||
| Cat 1 | 0 | CAT1 |
|
||||
And the following "course" exists:
|
||||
| fullname | Economics 101 |
|
||||
| shortname | ECON101 |
|
||||
| idnumber | 2021-econ101 |
|
||||
And the following "activity" exists:
|
||||
| course | ECON101 |
|
||||
| activity | forum |
|
||||
| name | Fundamentals of Economics |
|
||||
| idnumber | fundamentalsofeconomics |
|
||||
And I log in as "admin"
|
||||
When I am on the <identifier> <type> page
|
||||
Then I should see "<shouldsee>"
|
||||
|
||||
Examples:
|
||||
| description | identifier | type | shouldsee |
|
||||
| Course Category idnumber | CAT1 | category | Add a new course |
|
||||
| Course Category name | "Cat 1" | Category | Add a new course |
|
||||
| Course Full name | "Economics 101" | course | Fundamentals of Economics |
|
||||
| Course Short name | ECON101 | COURSE | Fundamentals of Economics |
|
||||
| Course idnumber | "2021-econ101" | Course | Fundamentals of Economics |
|
||||
| Forum idnumber | fundamentalsofeconomics | Activity | Add a new discussion |
|
||||
| Generic activity editing | fundamentalsofeconomics | "Activity editing" | Updating: Forum |
|
||||
| Forum name | "Fundamentals of Economics" | "Forum activity" | Add a new discussion |
|
||||
| Forum name editing | "Fundamentals of Economics" | "Forum activity editing" | Updating: Forum |
|
||||
| Forum name permissions | "Fundamentals of Economics" | "Forum activity permissions" | Permissions in Forum: Fun |
|
||||
| Forum name roles | "Fundamentals of Economics" | "Forum activity roles" | Assign roles in Forum: Fun |
|
||||
|
||||
Scenario Outline: When I am on an instance logged in as
|
||||
Given the following "categories" exist:
|
||||
| name | category | idnumber |
|
||||
| Cat 1 | 0 | CAT1 |
|
||||
And the following "course" exists:
|
||||
| fullname | Economics 101 |
|
||||
| shortname | ECON101 |
|
||||
| idnumber | 2021-econ101 |
|
||||
And the following "activity" exists:
|
||||
| course | ECON101 |
|
||||
| activity | forum |
|
||||
| name | Fundamentals of Economics |
|
||||
| idnumber | fundamentalsofeconomics |
|
||||
When I am on the <identifier> <type> page logged in as admin
|
||||
Then I should see "<shouldsee>"
|
||||
|
||||
Examples:
|
||||
| description | identifier | type | shouldsee |
|
||||
| Course Category idnumber | CAT1 | category | Add a new course |
|
||||
| Course Category name | "Cat 1" | Category | Add a new course |
|
||||
| Course Full name | "Economics 101" | course | Fundamentals of Economics |
|
||||
| Course Short name | ECON101 | COURSE | Fundamentals of Economics |
|
||||
| Course idnumber | "2021-econ101" | Course | Fundamentals of Economics |
|
||||
| Forum idnumber | fundamentalsofeconomics | Activity | Add a new discussion |
|
||||
| Generic activity editing | fundamentalsofeconomics | "Activity editing" | Updating: Forum |
|
||||
| Forum name | "Fundamentals of Economics" | "Forum activity" | Add a new discussion |
|
||||
| Forum name editing | "Fundamentals of Economics" | "Forum activity editing" | Updating: Forum |
|
||||
| Forum name permissions | "Fundamentals of Economics" | "Forum activity permissions" | Permissions in Forum: Fun |
|
||||
| Forum name roles | "Fundamentals of Economics" | "Forum activity roles" | Assign roles in Forum: Fun |
|
||||
|
||||
Scenario Outline: When I am on a named page
|
||||
Given I log in as "admin"
|
||||
When I am on the <identifier> page
|
||||
Then I should see "<shouldsee>"
|
||||
|
||||
Examples:
|
||||
| description | identifier | shouldsee |
|
||||
| Admin page | "Admin notifications" | Check for available updates |
|
||||
| Home page | Homepage | Recently accessed courses |
|
||||
|
||||
Scenario Outline: When I am on a named page logged in as
|
||||
When I am on the <identifier> page logged in as admin
|
||||
Then I should see "<shouldsee>"
|
||||
|
||||
Examples:
|
||||
| description | identifier | shouldsee |
|
||||
| Admin page | "Admin notifications" | Check for available updates |
|
||||
| Home page | Homepage | Recently accessed courses |
|
||||
@@ -9,10 +9,8 @@ Feature: Manage data requests
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | John | Doe | s1@example.com |
|
||||
| student2 | Jane | Doe | s2@example.com |
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
| contactdataprotectionofficer | 1 |
|
||||
And I log out
|
||||
And the following config values are set as admin:
|
||||
| contactdataprotectionofficer | 1 | tool_dataprivacy |
|
||||
|
||||
@javascript
|
||||
Scenario: Marking general enquiries as complete
|
||||
|
||||
@@ -50,6 +50,7 @@ switch ($action) {
|
||||
case 'disable':
|
||||
unset($enabled[$enrol]);
|
||||
set_config('enabled_stores', implode(',', array_keys($enabled)), 'tool_log');
|
||||
add_to_config_log('tool_logstore_visibility', '1', '0', $enrol);
|
||||
break;
|
||||
|
||||
case 'enable':
|
||||
@@ -59,6 +60,7 @@ switch ($action) {
|
||||
$enabled = array_keys($enabled);
|
||||
$enabled[] = $enrol;
|
||||
set_config('enabled_stores', implode(',', $enabled), 'tool_log');
|
||||
add_to_config_log('tool_logstore_visibility', '0', '1', $enrol);
|
||||
break;
|
||||
|
||||
case 'up':
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
@tool_log @report @report_configlog
|
||||
Feature: In a report, admin can see logstore visibility changes
|
||||
|
||||
# Change log stores visibility so the report contains known data.
|
||||
Background:
|
||||
Given I log in as "admin"
|
||||
And the following config values are set as admin:
|
||||
| enabled_stores | logstore_standard | tool_log |
|
||||
|
||||
@javascript
|
||||
Scenario: Display configuration changes report
|
||||
When I navigate to "Plugins > Logging > Manage log stores" in site administration
|
||||
And I click on "Disable" "icon" in the "Standard log" "table_row"
|
||||
And I click on "Enable" "icon" in the "External database log" "table_row"
|
||||
And I navigate to "Reports > Config changes" in site administration
|
||||
Then the following should exist in the "report-configlog-report-table" table:
|
||||
| User | Plugin | Setting | New value | Original value |
|
||||
| Admin User | logstore_standard | tool_logstore_visibility | 0 | 1 |
|
||||
| Admin User | logstore_database | tool_logstore_visibility | 1 | 0 |
|
||||
@@ -24,9 +24,7 @@ Feature: See the competencies for an activity on the course competencies page.
|
||||
| activity | name | intro | course | idnumber | completion | completionview |
|
||||
| page | PageName1 | PageDesc1 | C1 | PAGE1 | 1 | 1 |
|
||||
| page | PageName2 | PageDesc2 | C1 | PAGE2 | 1 | 1 |
|
||||
And I log in as "admin"
|
||||
And I am on site homepage
|
||||
And I follow "Course 1"
|
||||
And I am on the "Course 1" course page logged in as admin
|
||||
And I follow "Competencies"
|
||||
And I press "Add competencies to course"
|
||||
And "Competency picker" "dialogue" should be visible
|
||||
@@ -36,9 +34,7 @@ Feature: See the competencies for an activity on the course competencies page.
|
||||
And "Competency picker" "dialogue" should be visible
|
||||
And I select "Test-Comp2" of the competency tree
|
||||
And I click on "Add" "button" in the "Competency picker" "dialogue"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName1 "page activity editing" page
|
||||
And I follow "Expand all"
|
||||
And I set the field "Course competencies" to "Test-Comp1"
|
||||
And I press "Save and return to course"
|
||||
@@ -46,10 +42,8 @@ Feature: See the competencies for an activity on the course competencies page.
|
||||
|
||||
@javascript
|
||||
Scenario: Go to the competency course competencies page.
|
||||
When I log in as "student1"
|
||||
And I am on site homepage
|
||||
And I follow "Course 1"
|
||||
And I follow "Competencies"
|
||||
Given I am on the "Course 1" course page logged in as student1
|
||||
When I follow "Competencies"
|
||||
Then I should see "Test-Comp1"
|
||||
And I should see "Test-Comp2"
|
||||
And I set the field "Filter competencies by resource or activity" to "PageName1"
|
||||
@@ -64,12 +58,5 @@ Feature: See the competencies for an activity on the course competencies page.
|
||||
|
||||
@javascript
|
||||
Scenario: None course competencies page.
|
||||
When I log in as "student1"
|
||||
And I am on site homepage
|
||||
And I follow "Course 1"
|
||||
And I follow "PageName1"
|
||||
Then I should see "Test page content"
|
||||
And I am on site homepage
|
||||
And I follow "Course 1"
|
||||
And I follow "PageName1"
|
||||
When I am on the PageName1 "page activity" page logged in as student1
|
||||
Then I should see "Test page content"
|
||||
|
||||
@@ -42,9 +42,7 @@ Feature: Backup user data
|
||||
| Feedback for the response 'True'. | So you think it is true |
|
||||
| Feedback for the response 'False'. | So you think it is false |
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Quiz 1"
|
||||
When I am on the "Quiz 1" "quiz activity" page logged in as student1
|
||||
And I press "Attempt quiz now"
|
||||
And I click on "True" "radio" in the "First question" "question"
|
||||
And I click on "False" "radio" in the "Second question" "question"
|
||||
|
||||
@@ -68,7 +68,9 @@ if ($dataformat) {
|
||||
$downloadusers = new ArrayObject($SESSION->bulk_users);
|
||||
$iterator = $downloadusers->getIterator();
|
||||
|
||||
\core\dataformat::download_data($filename, $dataformat, $fields, $iterator, function($userid) use ($extrafields, $fields) {
|
||||
\core\dataformat::download_data($filename, $dataformat, $fields, $iterator, function($userid, $supportshtml)
|
||||
use ($extrafields, $fields) {
|
||||
|
||||
global $DB;
|
||||
|
||||
if (!$user = $DB->get_record('user', array('id' => $userid))) {
|
||||
@@ -86,6 +88,8 @@ if ($dataformat) {
|
||||
// We only take the text.
|
||||
if (is_array($user->$field)) {
|
||||
$userprofiledata[$field] = reset($user->$field);
|
||||
} else if ($supportshtml) {
|
||||
$userprofiledata[$field] = s($user->$field);
|
||||
} else {
|
||||
$userprofiledata[$field] = $user->$field;
|
||||
}
|
||||
|
||||
+3
-3
@@ -131,11 +131,11 @@ class auth_plugin_db extends auth_plugin_base {
|
||||
$authdb->Close();
|
||||
|
||||
if ($this->config->passtype === 'plaintext') {
|
||||
return ($fromdb == $extpassword);
|
||||
return ($fromdb === $extpassword);
|
||||
} else if ($this->config->passtype === 'md5') {
|
||||
return (strtolower($fromdb) == md5($extpassword));
|
||||
return (strtolower($fromdb) === md5($extpassword));
|
||||
} else if ($this->config->passtype === 'sha1') {
|
||||
return (strtolower($fromdb) == sha1($extpassword));
|
||||
return (strtolower($fromdb) === sha1($extpassword));
|
||||
} else if ($this->config->passtype === 'saltedcrypt') {
|
||||
return password_verify($extpassword, $fromdb);
|
||||
} else {
|
||||
|
||||
@@ -335,12 +335,28 @@ class auth_db_testcase extends advanced_testcase {
|
||||
$DB->update_record('auth_db_users', $user3);
|
||||
$this->assertTrue($auth->user_login('u3', 'heslo'));
|
||||
|
||||
// Test user created to see if the checking happens strictly.
|
||||
$usermd5 = (object)['name' => 'usermd5', 'pass' => '0e462097431906509019562988736854'];
|
||||
$usermd5->id = $DB->insert_record('auth_db_users', $usermd5);
|
||||
|
||||
// md5('240610708') === '0e462097431906509019562988736854'.
|
||||
$this->assertTrue($auth->user_login('usermd5', '240610708'));
|
||||
$this->assertFalse($auth->user_login('usermd5', 'QNKCDZO'));
|
||||
|
||||
set_config('passtype', 'sh1', 'auth_db');
|
||||
$auth->config->passtype = 'sha1';
|
||||
$user3->pass = sha1('heslo');
|
||||
$DB->update_record('auth_db_users', $user3);
|
||||
$this->assertTrue($auth->user_login('u3', 'heslo'));
|
||||
|
||||
// Test user created to see if the checking happens strictly.
|
||||
$usersha1 = (object)['name' => 'usersha1', 'pass' => '0e66507019969427134894567494305185566735'];
|
||||
$usersha1->id = $DB->insert_record('auth_db_users', $usersha1);
|
||||
|
||||
// sha1('aaroZmOk') === '0e66507019969427134894567494305185566735'.
|
||||
$this->assertTrue($auth->user_login('usersha1', 'aaroZmOk'));
|
||||
$this->assertFalse($auth->user_login('usersha1', 'aaK1STfY'));
|
||||
|
||||
set_config('passtype', 'saltedcrypt', 'auth_db');
|
||||
$auth->config->passtype = 'saltedcrypt';
|
||||
$user3->pass = password_hash('heslo', PASSWORD_BCRYPT);
|
||||
|
||||
@@ -93,13 +93,12 @@ class helper {
|
||||
|
||||
foreach ($sessions as $session) {
|
||||
// Get user session from DB.
|
||||
if (session_decode(base64_decode($session->sessdata))) {
|
||||
if (isset($_SESSION['SESSION']) && isset($_SESSION['SESSION']->shibboleth_session_id)) {
|
||||
// If there is a match, kill the session.
|
||||
if ($_SESSION['SESSION']->shibboleth_session_id == trim($spsessionid)) {
|
||||
// Delete this user's sessions.
|
||||
\core\session\manager::kill_user_sessions($session->userid);
|
||||
}
|
||||
$usersession = self::unserializesession(base64_decode($session->sessdata));
|
||||
if (isset($usersession['SESSION']) && isset($usersession['SESSION']->shibboleth_session_id)) {
|
||||
// If there is a match, kill the session.
|
||||
if ($usersession['SESSION']->shibboleth_session_id == trim($spsessionid)) {
|
||||
// Delete this user's sessions.
|
||||
\core\session\manager::kill_user_sessions($session->userid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,11 +112,22 @@ class helper {
|
||||
*/
|
||||
private static function unserializesession($serializedstring) {
|
||||
$variables = array();
|
||||
$a = preg_split("/(\w+)\|/", $serializedstring, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
|
||||
$counta = count($a);
|
||||
for ($i = 0; $i < $counta; $i = $i + 2) {
|
||||
$variables[$a[$i]] = unserialize($a[$i + 1]);
|
||||
|
||||
$index = 0;
|
||||
|
||||
// Find next delimiter after current index. It's key being the characters between those points.
|
||||
while ($delimiterpos = strpos($serializedstring, '|', $index)) {
|
||||
$key = substr($serializedstring, $index, $delimiterpos - $index);
|
||||
|
||||
// Start unserializing immediately after the delimiter. PHP will read as much valid data as possible.
|
||||
$value = unserialize(substr($serializedstring, $delimiterpos + 1),
|
||||
['allowed_classes' => ['stdClass']]);
|
||||
$variables[$key] = $value;
|
||||
|
||||
// Advance index beyond the length of the previously captured serialized value.
|
||||
$index = $delimiterpos + 1 + strlen(serialize($value));
|
||||
}
|
||||
|
||||
return $variables;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,10 +41,8 @@ Feature: Authentication
|
||||
Then I should see "You are not logged in" in the "page-footer" "region"
|
||||
|
||||
Scenario Outline: Checking the display of the Remember username checkbox
|
||||
Given I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
Given the following config values are set as admin:
|
||||
| rememberusername | <settingvalue> |
|
||||
And I log out
|
||||
And I am on homepage
|
||||
When I click on "Log in" "link" in the ".logininfo" "css_element"
|
||||
Then I should <expect> "Remember username"
|
||||
|
||||
@@ -48,10 +48,10 @@ Feature: Confirm that conditions on completion no longer cause a bug
|
||||
And I click on "Activity completion" "button" in the "Add restriction..." "dialogue"
|
||||
And I set the field with xpath "//div[contains(concat(' ', normalize-space(@class), ' '), ' availability-item ')][preceding-sibling::div]//select[@name='cm']" to "Page2"
|
||||
And I press "Save and return to course"
|
||||
And I should see "Not available unless:" in the ".activity.glossary" "css_element"
|
||||
Then I should see "Not available unless:" in the ".activity.glossary" "css_element"
|
||||
And I should see "The activity Page1 is marked complete" in the ".activity.glossary" "css_element"
|
||||
And I should see "The activity Page2 is marked complete" in the ".activity.glossary" "css_element"
|
||||
And I follow "TestGlossary"
|
||||
|
||||
# Behat will automatically check there is no error on this page.
|
||||
Then I should see "TestGlossary"
|
||||
And I am on the TestGlossary "glossary activity" page
|
||||
And I should see "TestGlossary"
|
||||
|
||||
@@ -86,13 +86,12 @@ Feature: availability_grade
|
||||
And I set the field "Minimum grade percentage (inclusive)" to "10"
|
||||
And I press "Save and return to course"
|
||||
|
||||
And I log out
|
||||
|
||||
# Log in as student without a grade yet.
|
||||
When I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
When I am on the "A1" "assign activity" page logged in as student1
|
||||
|
||||
# Do the assignment.
|
||||
And I follow "A1"
|
||||
And I click on "Add submission" "button"
|
||||
And I set the field "Online text" to "Q"
|
||||
And I click on "Save changes" "button"
|
||||
@@ -103,14 +102,12 @@ Feature: availability_grade
|
||||
And I should not see "P3" in the "region-main" "region"
|
||||
And I should not see "P4" in the "region-main" "region"
|
||||
And I should see "A1" in the "region-main" "region"
|
||||
And I log out
|
||||
|
||||
# Log back in as teacher.
|
||||
When I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
When I am on the "A1" "assign activity" page logged in as teacher1
|
||||
|
||||
# Give the assignment 40%.
|
||||
And I follow "A1"
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
# Pick the grade link in the row that has s@example.com in it.
|
||||
And I click on "Grade" "link" in the "s@example.com" "table_row"
|
||||
@@ -118,11 +115,10 @@ Feature: availability_grade
|
||||
And I click on "Save changes" "button"
|
||||
And I press "OK"
|
||||
And I click on "Edit settings" "link"
|
||||
And I log out
|
||||
|
||||
# Log back in as student.
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I am on the "Course 1" course page logged in as student1
|
||||
|
||||
# Check pages are visible.
|
||||
Then I should see "P2" in the "region-main" "region"
|
||||
|
||||
@@ -95,8 +95,7 @@ Feature: availability_profile
|
||||
And I click on "Save and return to course" "button"
|
||||
|
||||
# Edit it again and check the setting still works.
|
||||
When I follow "P1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
When I am on the P1 "page activity editing" page
|
||||
And I expand all fieldsets
|
||||
Then the field "User profile field" matches value "Super field"
|
||||
And the field "Value to compare against" matches value "Bananaman"
|
||||
|
||||
@@ -358,6 +358,15 @@ class backup_controller extends base_controller {
|
||||
return $this->plan;
|
||||
}
|
||||
|
||||
/**
|
||||
* For debug only. Get a simple test display of all the settings.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function debug_display_all_settings_values(): string {
|
||||
return $this->get_plan()->debug_display_all_settings_values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user roles that should be kept in the destination course
|
||||
* for a course copy operation.
|
||||
|
||||
@@ -350,6 +350,15 @@ class restore_controller extends base_controller {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For debug only. Get a simple test display of all the settings.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function debug_display_all_settings_values(): string {
|
||||
return $this->get_plan()->debug_display_all_settings_values();
|
||||
}
|
||||
|
||||
public function get_info() {
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
@@ -2110,7 +2110,6 @@ class restore_ras_and_caps_structure_step extends restore_structure_step {
|
||||
|
||||
public function process_override($data) {
|
||||
$data = (object)$data;
|
||||
|
||||
// Check roleid is one of the mapped ones
|
||||
$newrole = $this->get_mapping('role', $data->roleid);
|
||||
$newroleid = $newrole->newitemid ?? false;
|
||||
@@ -2128,8 +2127,8 @@ class restore_ras_and_caps_structure_step extends restore_structure_step {
|
||||
// Check if the new role is an overrideable role AND if the user performing the restore has the
|
||||
// capability to assign the capability.
|
||||
if (in_array($newrole->info['shortname'], $overrideableroles) &&
|
||||
($safecapability && has_capability('moodle/role:safeoverride', $context, $userid) ||
|
||||
!$safecapability && has_capability('moodle/role:override', $context, $userid))
|
||||
(has_capability('moodle/role:override', $context, $userid) ||
|
||||
($safecapability && has_capability('moodle/role:safeoverride', $context, $userid)))
|
||||
) {
|
||||
assign_capability($data->capability, $data->permission, $newroleid, $this->task->get_contextid());
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
// Include all the needed stuff.
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
||||
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for how backup and restore handles role-related things.
|
||||
*
|
||||
* @package core_backup
|
||||
* @copyright 2021 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class roles_backup_restore_test extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Create a course where the (non-editing) Teacher role is overridden
|
||||
* to have 'moodle/user:loginas' and 'moodle/site:accessallgroups'.
|
||||
*
|
||||
* @return stdClass the new course.
|
||||
*/
|
||||
protected function create_course_with_role_overrides(): stdClass {
|
||||
$generator = $this->getDataGenerator();
|
||||
$course = $generator->create_course();
|
||||
$teacher = $generator->create_user();
|
||||
|
||||
$context = context_course::instance($course->id);
|
||||
$generator->enrol_user($teacher->id, $course->id, 'teacher');
|
||||
|
||||
$editingteacherrole = $this->get_role('teacher');
|
||||
role_change_permission($editingteacherrole->id, $context, 'moodle/user:loginas', CAP_ALLOW);
|
||||
role_change_permission($editingteacherrole->id, $context, 'moodle/site:accessallgroups', CAP_ALLOW);
|
||||
|
||||
return $course;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the role id from a shortname.
|
||||
*
|
||||
* @param string $shortname the role shortname.
|
||||
* @return stdClass the role from the DB.
|
||||
*/
|
||||
protected function get_role(string $shortname): stdClass {
|
||||
global $DB;
|
||||
return $DB->get_record('role', ['shortname' => $shortname]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array capability => CAP_... constant for all the orverrides set for a given role on a given context.
|
||||
*
|
||||
* @param string $shortname role shortname.
|
||||
* @param context $context context.
|
||||
* @return array the overrides set here.
|
||||
*/
|
||||
protected function get_overrides_for_role_on_context(string $shortname, context $context): array {
|
||||
$overridedata = get_capabilities_from_role_on_context($this->get_role($shortname), $context);
|
||||
$overrides = [];
|
||||
foreach ($overridedata as $override) {
|
||||
$overrides[$override->capability] = $override->permission;
|
||||
}
|
||||
return $overrides;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a backup of the course.
|
||||
*
|
||||
* @param stdClass $course The course object.
|
||||
* @return string Unique identifier for this backup.
|
||||
*/
|
||||
protected function backup_course(\stdClass $course): string {
|
||||
global $CFG, $USER;
|
||||
|
||||
// Turn off file logging, otherwise it can't delete the file (Windows).
|
||||
$CFG->backup_file_logger_level = backup::LOG_NONE;
|
||||
|
||||
// Do backup with default settings. MODE_IMPORT means it will just
|
||||
// create the directory and not zip it.
|
||||
$bc = new \backup_controller(backup::TYPE_1COURSE, $course->id,
|
||||
backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT,
|
||||
$USER->id);
|
||||
$backupid = $bc->get_backupid();
|
||||
$bc->execute_plan();
|
||||
$bc->destroy();
|
||||
|
||||
return $backupid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores a backup that has been made earlier.
|
||||
*
|
||||
* @param string $backupid The unique identifier of the backup.
|
||||
* @param string $asroleshortname Which role in the new cousre the restorer should have.
|
||||
* @return int The new course id.
|
||||
*/
|
||||
protected function restore_adding_to_course(string $backupid, string $asroleshortname): int {
|
||||
global $CFG, $USER;
|
||||
|
||||
// Create course to restore into, and a user to do the restore.
|
||||
$generator = $this->getDataGenerator();
|
||||
$course = $generator->create_course();
|
||||
$restorer = $generator->create_user();
|
||||
|
||||
$generator->enrol_user($restorer->id, $course->id, $asroleshortname);
|
||||
$this->setUser($restorer);
|
||||
|
||||
// Turn off file logging, otherwise it can't delete the file (Windows).
|
||||
$CFG->backup_file_logger_level = backup::LOG_NONE;
|
||||
|
||||
// Do restore to new course with default settings.
|
||||
$rc = new \restore_controller($backupid, $course->id,
|
||||
backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id,
|
||||
backup::TARGET_CURRENT_ADDING);
|
||||
|
||||
$precheck = $rc->execute_precheck();
|
||||
$this->assertTrue($precheck);
|
||||
$rc->get_plan()->get_setting('role_assignments')->set_value(true);
|
||||
$rc->execute_plan();
|
||||
$rc->destroy();
|
||||
|
||||
return $course->id;
|
||||
}
|
||||
|
||||
public function test_restore_role_overrides_as_manager(): void {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a course and back it up.
|
||||
$course = $this->create_course_with_role_overrides();
|
||||
$backupid = $this->backup_course($course);
|
||||
|
||||
// When manager restores, both role overrides should be restored.
|
||||
$newcourseid = $this->restore_adding_to_course($backupid, 'manager');
|
||||
|
||||
// Verify.
|
||||
$overrides = $this->get_overrides_for_role_on_context('teacher',
|
||||
context_course::instance($newcourseid));
|
||||
$this->assertArrayHasKey('moodle/user:loginas', $overrides);
|
||||
$this->assertEquals(CAP_ALLOW, $overrides['moodle/user:loginas']);
|
||||
$this->assertArrayHasKey('moodle/site:accessallgroups', $overrides);
|
||||
$this->assertEquals(CAP_ALLOW, $overrides['moodle/site:accessallgroups']);
|
||||
}
|
||||
|
||||
public function test_restore_role_overrides_as_teacher(): void {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a course and back it up.
|
||||
$course = $this->create_course_with_role_overrides();
|
||||
$backupid = $this->backup_course($course);
|
||||
|
||||
// When teacher restores, only the safe override should be restored.
|
||||
$newcourseid = $this->restore_adding_to_course($backupid, 'editingteacher');
|
||||
|
||||
// Verify.
|
||||
$overrides = $this->get_overrides_for_role_on_context('teacher',
|
||||
context_course::instance($newcourseid));
|
||||
$this->assertArrayNotHasKey('moodle/user:loginas', $overrides);
|
||||
$this->assertArrayHasKey('moodle/site:accessallgroups', $overrides);
|
||||
$this->assertEquals(CAP_ALLOW, $overrides['moodle/site:accessallgroups']);
|
||||
}
|
||||
}
|
||||
@@ -126,6 +126,19 @@ abstract class base_plan implements checksumable, executable {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* For debug only. Get a simple test display of all the settings.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function debug_display_all_settings_values(): string {
|
||||
$result = '';
|
||||
foreach ($this->settings as $name => $setting) {
|
||||
$result .= $name . ': ' . $setting->get_value() . "\n";
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper over @get_setting() that returns if the requested setting exists or no
|
||||
*/
|
||||
|
||||
@@ -14,10 +14,8 @@ Feature: Duplicate activities
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
| backup_import_activities | 0 |
|
||||
And I log out
|
||||
And the following config values are set as admin:
|
||||
| backup_import_activities | 0 | backup |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Database" to section "1" and I fill the form with:
|
||||
|
||||
@@ -51,18 +51,18 @@ Feature: Award badges based on activity completion
|
||||
And I log out
|
||||
|
||||
Scenario: Student earns a badge using activity completion, but does not get passing grade
|
||||
When I log in as "student1"
|
||||
Given I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And the "Test quiz name" "quiz" activity with "auto" completion should be marked as not complete
|
||||
And I follow "Test quiz name"
|
||||
When I am on the "Test quiz name" "quiz activity" page
|
||||
And I press "Re-attempt quiz"
|
||||
And I set the field "False" to "1"
|
||||
And I press "Finish attempt ..."
|
||||
And I press "Submit all and finish"
|
||||
And I click on "Submit all and finish" "button" in the "Confirmation" "dialogue"
|
||||
And I log out
|
||||
Then 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
|
||||
And I navigate to "Badges > Manage badges" in current page administration
|
||||
And I follow "Course Badge"
|
||||
And I should see "Recipients (1)"
|
||||
Then I should see "Recipients (1)"
|
||||
|
||||
@@ -64,6 +64,7 @@ Feature: Award badges based on competency completion
|
||||
And I set the following fields to these values:
|
||||
| Rating | C |
|
||||
And I click on "Rate" "button" in the "Rate" "dialogue"
|
||||
And I should see "The competency rating was manually set in the course"
|
||||
And I log out
|
||||
# See if we got the badge
|
||||
Then I log in as "user1"
|
||||
@@ -114,8 +115,8 @@ Feature: Award badges based on competency completion
|
||||
And I press "Rate"
|
||||
And I set the following fields to these values:
|
||||
| Rating | C |
|
||||
And I wait until the page is ready
|
||||
And I click on "Rate" "button" in the "Rate" "dialogue"
|
||||
And I should see "The competency rating was manually set in the course"
|
||||
And I log out
|
||||
# See if we got the badge
|
||||
Then I log in as "user1"
|
||||
@@ -168,8 +169,8 @@ Feature: Award badges based on competency completion
|
||||
And I press "Rate"
|
||||
And I set the following fields to these values:
|
||||
| Rating | C |
|
||||
And I wait until the page is ready
|
||||
And I click on "Rate" "button" in the "Rate" "dialogue"
|
||||
And I should see "The competency rating was manually set in the course"
|
||||
And I log out
|
||||
# We should not get the badge yet.
|
||||
Then I log in as "user1"
|
||||
@@ -184,8 +185,8 @@ Feature: Award badges based on competency completion
|
||||
And I press "Rate"
|
||||
And I set the following fields to these values:
|
||||
| Rating | C |
|
||||
And I wait until the page is ready
|
||||
And I click on "Rate" "button" in the "Rate" "dialogue"
|
||||
And I should see "The competency rating was manually set in the course"
|
||||
And I log out
|
||||
# See if we got the badge now.
|
||||
Then I log in as "user1"
|
||||
|
||||
@@ -147,7 +147,6 @@ Feature: My overview block pagination
|
||||
| student1 | C13 | student |
|
||||
When I log in as "student1"
|
||||
And I click on "[data-control='next']" "css_element" in the "Course overview" "block"
|
||||
And I wait until ".block_myoverview [data-control='next']" "css_element" exists
|
||||
Then the "class" attribute of ".block_myoverview [data-control='next']" "css_element" should contain "disabled"
|
||||
And I log out
|
||||
|
||||
@@ -181,7 +180,6 @@ Feature: My overview block pagination
|
||||
| student1 | C25 | student |
|
||||
When I log in as "student1"
|
||||
And I click on "[data-control='next']" "css_element" in the "Course overview" "block"
|
||||
And I wait until ".block_myoverview [data-control='next']" "css_element" exists
|
||||
Then the "class" attribute of ".block_myoverview [data-control='next']" "css_element" should not contain "disabled"
|
||||
And the "class" attribute of ".block_myoverview [data-control='previous']" "css_element" should not contain "disabled"
|
||||
And I should see "Course 13" in the "Course overview" "block"
|
||||
|
||||
@@ -27,9 +27,7 @@ Feature: Course overview block show users their progress on courses
|
||||
Then I should not see "0%" in the "Course overview" "block"
|
||||
|
||||
Scenario: User complete activity and verify his progress
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I follow "Test choice 1"
|
||||
Given I am on the "Test choice 1" "choice activity" page logged in as teacher1
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I set the following fields to these values:
|
||||
| Completion tracking | Show activity as complete when conditions are met |
|
||||
@@ -40,8 +38,8 @@ Feature: Course overview block show users their progress on courses
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
Then I should see "Course 1" in the "Course overview" "block"
|
||||
And I should see "0%" in the "Course overview" "block"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test choice 1"
|
||||
|
||||
And I am on the "Test choice 1" "choice activity" page
|
||||
And I follow "Dashboard" in the user menu
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
And I should see "100%" in the "Course overview" "block"
|
||||
|
||||
@@ -19,7 +19,7 @@ Feature: The private files block allows users to store files privately in moodle
|
||||
| page | C1 | page1 | Test page name | Test page description |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I follow "Test page name"
|
||||
And I am on the "Test page name" "page activity" page
|
||||
And I add the "Private files" block
|
||||
And I should see "No files available" in the "Private files" "block"
|
||||
When I follow "Manage private files..."
|
||||
|
||||
+8
-12
@@ -16,24 +16,20 @@ Feature: The recently accessed items block allows users to easily access their m
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| student1 | C2 | student |
|
||||
And I log in as "admin"
|
||||
And I am on "Course 1" course homepage
|
||||
And I turn editing mode on
|
||||
And I add a "Forum" to section "1" and I fill the form with:
|
||||
| Forum name | Test forum name |
|
||||
| Forum type | Standard forum for general use |
|
||||
| Description | Test forum description |
|
||||
And I log out
|
||||
And the following "activity" exists:
|
||||
| course | C1 |
|
||||
| activity | forum |
|
||||
| idnumber | Test forum name |
|
||||
| name | Test forum name |
|
||||
And I log in as "student1"
|
||||
When I press "Customise this page"
|
||||
And I press "Customise this page"
|
||||
And I add the "Recently accessed items" block
|
||||
|
||||
Scenario: User has not accessed any item
|
||||
Then I should see "No recent items" in the "Recently accessed items" "block"
|
||||
|
||||
Scenario: User has accessed some items
|
||||
Given I am on "Course 1" course homepage
|
||||
When I follow "Test forum name"
|
||||
Given I change window size to "large"
|
||||
When I am on the "Test forum name" "forum activity" page
|
||||
And I follow "Dashboard" in the user menu
|
||||
And I change window size to "large"
|
||||
Then I should see "Test forum name" in the "Recently accessed items" "block"
|
||||
|
||||
@@ -24,9 +24,9 @@ Feature: Edit activities in main menu block
|
||||
|
||||
@javascript
|
||||
Scenario: Activities in main menu block can be made available but not visible on a course page
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
Given the following config values are set as admin:
|
||||
| allowstealth | 1 |
|
||||
And I log in as "admin"
|
||||
And I am on site homepage
|
||||
And I navigate to "Turn editing on" in current page administration
|
||||
And I add the "Main menu" block
|
||||
|
||||
@@ -33,10 +33,8 @@ Feature: Edit activities in social activities block
|
||||
|
||||
@javascript
|
||||
Scenario: Activities in social activities block can be made available but not visible on a course page
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
Given the following config values are set as admin:
|
||||
| allowstealth | 1 |
|
||||
And I log out
|
||||
And I log in as "user1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add the "Recent activity" block
|
||||
|
||||
+2
-1
@@ -3204,6 +3204,7 @@ function calendar_update_subscription($subscription) {
|
||||
* @return bool true if current user can edit the subscription else false
|
||||
*/
|
||||
function calendar_can_edit_subscription($subscriptionorid) {
|
||||
global $USER;
|
||||
if (is_array($subscriptionorid)) {
|
||||
$subscription = (object)$subscriptionorid;
|
||||
} else if (is_object($subscriptionorid)) {
|
||||
@@ -3224,7 +3225,7 @@ function calendar_can_edit_subscription($subscriptionorid) {
|
||||
calendar_get_allowed_types($allowed, $courseid, null, $category);
|
||||
switch ($subscription->eventtype) {
|
||||
case 'user':
|
||||
return $allowed->user;
|
||||
return ($USER->id == $subscription->userid && $allowed->user);
|
||||
case 'course':
|
||||
if (isset($allowed->courses[$courseid])) {
|
||||
return $allowed->courses[$courseid];
|
||||
|
||||
@@ -2154,11 +2154,7 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
// Change our event's date to the 20th Monday of the current year.
|
||||
$twentiethmonday = new DateTime(date('Y-01-01'));
|
||||
$twentiethmonday->modify('+20 Monday');
|
||||
$startdatetime = $this->change_event_startdate($twentiethmonday->format('Ymd\T090000'), 'US/Eastern');
|
||||
|
||||
$startdate = new DateTime($startdatetime->format('Y-m-d'));
|
||||
|
||||
$offset = $startdatetime->diff($startdate, true);
|
||||
$startdatetime = $this->change_event_startdate($twentiethmonday->format('Ymd\T000000'), 'US/Eastern');
|
||||
|
||||
$interval = new DateInterval('P1Y');
|
||||
|
||||
@@ -2183,7 +2179,6 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$expecteddate->modify('January 1');
|
||||
$expecteddate->add($interval);
|
||||
$expecteddate->modify("+20 Monday");
|
||||
$expecteddate->add($offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,12 +36,10 @@ Feature: Restrict activity availability through date conditions
|
||||
| x[year] | 2037 |
|
||||
And I press "Save and return to course"
|
||||
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
|
||||
Then I should see "Available from 31 December 2037"
|
||||
And "Test assignment 1" "link" should not exist in the "page" "region"
|
||||
And "Test assignment 1" activity should be dimmed
|
||||
And "Test assignment 1" "link" should not exist
|
||||
And I log out
|
||||
|
||||
@javascript
|
||||
Scenario: Show activity hidden to students when available until date is in past
|
||||
@@ -60,6 +58,5 @@ Feature: Restrict activity availability through date conditions
|
||||
And I click on ".availability-item .availability-eye img" "css_element"
|
||||
And I press "Save and return to course"
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
Then I should not see "Test assignment 2"
|
||||
When I am on the "Course 1" course page logged in as student1
|
||||
Then I should not see "Test assignment 2" in the "page" "region"
|
||||
|
||||
@@ -17,13 +17,16 @@ Feature: Restrict activity availability through grade conditions
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And I log in as "teacher1"
|
||||
And the following "activity" exists:
|
||||
| course | C1 |
|
||||
| activity | assign |
|
||||
| idnumber | Grade assignment |
|
||||
| name | Grade assignment |
|
||||
| assignsubmission_onlinetext_enabled | 1 |
|
||||
| assignsubmission_file_enabled | 0 |
|
||||
| submissiondrafts | 0 |
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Assignment" to section "1" and I fill the form with:
|
||||
| Assignment name | Grade assignment |
|
||||
| Description | Grade this assignment to revoke restriction on restricted assignment |
|
||||
| assignsubmission_onlinetext_enabled | 1 |
|
||||
| assignsubmission_file_enabled | 0 |
|
||||
# Adding the page like this because id_availableform_enabled needs to be clicked to trigger the action.
|
||||
And I add a "Page" to section "2"
|
||||
And I expand all fieldsets
|
||||
@@ -31,15 +34,15 @@ Feature: Restrict activity availability through grade conditions
|
||||
And I click on "Grade" "button" in the "Add restriction..." "dialogue"
|
||||
And I click on "min" "checkbox"
|
||||
And I set the following fields to these values:
|
||||
| Name | Test page name |
|
||||
| Description | Restricted page, till grades in Grade assignment is at least 20% |
|
||||
| Page content | Test page contents |
|
||||
| id | Grade assignment |
|
||||
| minval | 20 |
|
||||
| Name | Test page name |
|
||||
| Description | Restricted page, till grades in Grade assignment is at least 20% |
|
||||
| Page content | Test page contents |
|
||||
| id | Grade assignment |
|
||||
| minval | 20 |
|
||||
And I press "Save and return to course"
|
||||
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
|
||||
Then I should see "Not available unless: You achieve a required score in Grade assignment"
|
||||
And "Test page name" activity should be dimmed
|
||||
And "Test page name" "link" should not exist
|
||||
@@ -50,9 +53,8 @@ Feature: Restrict activity availability through grade conditions
|
||||
And I press "Save changes"
|
||||
And I should see "Submitted for grading"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Grade assignment"
|
||||
|
||||
And I am on the "Grade assignment" "assign activity" page logged in as teacher1
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
And I click on "Grade" "link" in the "Student First" "table_row"
|
||||
And I set the following fields to these values:
|
||||
@@ -61,7 +63,7 @@ Feature: Restrict activity availability through grade conditions
|
||||
And I press "OK"
|
||||
And I follow "Edit settings"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
|
||||
And I am on the "Course 1" course page logged in as student1
|
||||
And "Test page name" activity should be visible
|
||||
And I should not see "Not available unless: You achieve a required score in Grade assignment"
|
||||
|
||||
@@ -76,16 +76,14 @@ Feature: Restrict sections availability through completion or grade conditions
|
||||
And I am on "Course 1" course homepage
|
||||
Then I should see "Not available unless: You achieve a required score in Grade assignment"
|
||||
And "Test page name" activity should be hidden
|
||||
And I follow "Grade assignment"
|
||||
And I am on the "Grade assignment" "assign activity" page
|
||||
And I press "Add submission"
|
||||
And I set the following fields to these values:
|
||||
| Online text | I'm the student submission |
|
||||
And I press "Save changes"
|
||||
And I should see "Submitted for grading"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Grade assignment"
|
||||
And I am on the "Grade assignment" "assign activity" page logged in as teacher1
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
And I click on "Grade" "link" in the "Student First" "table_row"
|
||||
And I set the following fields to these values:
|
||||
@@ -94,7 +92,6 @@ Feature: Restrict sections availability through completion or grade conditions
|
||||
And I press "OK"
|
||||
And I follow "Edit settings"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I am on the "Course 1" Course page logged in as student1
|
||||
And "Test page name" activity should be visible
|
||||
And I should not see "Not available unless: You achieve a required score in Grade assignment"
|
||||
|
||||
+24
-6
@@ -4720,21 +4720,39 @@ function course_get_recent_courses(int $userid = null, int $limit = 0, int $offs
|
||||
$basefields = array('id', 'idnumber', 'summary', 'summaryformat', 'startdate', 'enddate', 'category',
|
||||
'shortname', 'fullname', 'timeaccess', 'component', 'visible');
|
||||
|
||||
$sort = trim($sort);
|
||||
if (empty($sort)) {
|
||||
$sort = 'timeaccess DESC';
|
||||
} else {
|
||||
// The SQL string for sorting can define sorting by multiple columns.
|
||||
$rawsorts = explode(',', $sort);
|
||||
$sorts = array();
|
||||
// Validate and trim the sort parameters in the SQL string for sorting.
|
||||
foreach ($rawsorts as $rawsort) {
|
||||
$rawsort = trim($rawsort);
|
||||
$sorts[] = trim($rawsort);
|
||||
$sort = trim($rawsort);
|
||||
$sortparams = explode(' ', $sort);
|
||||
// A valid sort statement can not have more than 2 params (ex. 'summary desc' or 'timeaccess').
|
||||
if (count($sortparams) > 2) {
|
||||
throw new invalid_parameter_exception(
|
||||
'Invalid structure of the sort parameter, allowed structure: fieldname [ASC|DESC].');
|
||||
}
|
||||
$sortfield = trim($sortparams[0]);
|
||||
// Validate the value which defines the field to sort by.
|
||||
if (!in_array($sortfield, $basefields)) {
|
||||
throw new invalid_parameter_exception('Invalid field in the sort parameter, allowed fields: ' .
|
||||
implode(', ', $basefields) . '.');
|
||||
}
|
||||
$sortdirection = isset($sortparams[1]) ? trim($sortparams[1]) : '';
|
||||
// Validate the value which defines the sort direction (if present).
|
||||
$allowedsortdirections = ['asc', 'desc'];
|
||||
if (!empty($sortdirection) && !in_array(strtolower($sortdirection), $allowedsortdirections)) {
|
||||
throw new invalid_parameter_exception('Invalid sort direction in the sort parameter, allowed values: ' .
|
||||
implode(', ', $allowedsortdirections) . '.');
|
||||
}
|
||||
$sorts[] = $sort;
|
||||
}
|
||||
$sort = implode(',', $sorts);
|
||||
}
|
||||
|
||||
$orderby = "ORDER BY $sort";
|
||||
|
||||
$ctxfields = context_helper::get_preload_record_columns_sql('ctx');
|
||||
|
||||
$coursefields = 'c.' .join(',', $basefields);
|
||||
@@ -4767,7 +4785,7 @@ function course_get_recent_courses(int $userid = null, int $limit = 0, int $offs
|
||||
AND ue.timestart < :now1
|
||||
AND (ue.timeend = 0 OR ue.timeend > :now2)
|
||||
))
|
||||
$orderby";
|
||||
ORDER BY $sort";
|
||||
|
||||
$now = round(time(), -2); // Improves db caching.
|
||||
$params = ['userid' => $userid, 'contextlevel' => CONTEXT_COURSE, 'visible' => 1, 'status' => ENROL_USER_ACTIVE,
|
||||
|
||||
@@ -8,32 +8,26 @@ Feature: Edit completion settings of an activity
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | enablecompletion |
|
||||
| Course 1 | C1 | 1 |
|
||||
And I log in as "admin"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Page" to section "1" and I fill the form with:
|
||||
| Name | TestPage |
|
||||
| Description | x |
|
||||
| Page content | x |
|
||||
| Completion tracking | 2 |
|
||||
| Require view | 1 |
|
||||
And I am on "Course 1" course homepage
|
||||
And the following "activities" exist:
|
||||
| activity | course | idnumber | intro | name | completion | completionview |
|
||||
| page | C1 | p1 | x | TestPage | 2 | 1 |
|
||||
|
||||
Scenario: Completion is not locked when the activity has not yet been viewed
|
||||
Given I click on "Edit settings" "link" in the "TestPage" activity
|
||||
Given I am on the TestPage "Page Activity editing" page logged in as admin
|
||||
When I expand all fieldsets
|
||||
Then I should see "Completion tracking"
|
||||
And I should not see "Completion options locked"
|
||||
|
||||
Scenario: Completion is locked after the activity has been viewed
|
||||
Given I follow "TestPage"
|
||||
When I follow "Edit settings"
|
||||
Given I am on the TestPage "Page Activity" page logged in as admin
|
||||
When I am on the TestPage "Page Activity editing" page
|
||||
And I expand all fieldsets
|
||||
Then I should see "Completion options locked"
|
||||
|
||||
@javascript
|
||||
Scenario: Pressing the unlock button allows the user to edit completion settings
|
||||
Given I follow "TestPage"
|
||||
When I navigate to "Edit settings" in current page administration
|
||||
Given I am on the TestPage "Page Activity" page logged in as admin
|
||||
When I am on the TestPage "Page Activity editing" page
|
||||
And I expand all fieldsets
|
||||
And I press "Unlock completion options"
|
||||
Then I should see "Completion options unlocked"
|
||||
@@ -45,8 +39,8 @@ Feature: Edit completion settings of an activity
|
||||
|
||||
@javascript
|
||||
Scenario: Even when completion is locked, the user can still set the date
|
||||
Given I follow "TestPage"
|
||||
When I navigate to "Edit settings" in current page administration
|
||||
Given I am on the TestPage "Page Activity" page logged in as admin
|
||||
And I am on the TestPage "Page Activity editing" page
|
||||
And I expand all fieldsets
|
||||
When I click on "id_completionexpected_enabled" "checkbox"
|
||||
And I set the field "id_completionexpected_year" to "2013"
|
||||
|
||||
@@ -131,10 +131,8 @@ Feature: Toggle activities visibility from the course page
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
And the following config values are set as admin:
|
||||
| allowstealth | 1 |
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add the "Recent activity" block
|
||||
|
||||
@@ -42,9 +42,9 @@ Feature: Activity navigation
|
||||
| wiki | Wiki 1 | Test wiki description | C1 | wiki1 | 6 |
|
||||
| workshop | Workshop 1 | Test workshop description | C1 | workshop1 | 6 |
|
||||
| assign | Assignment 1 | Test assignment description | C2 | assign21 | 0 |
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
And the following config values are set as admin:
|
||||
| allowstealth | 1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
# Stealth activity.
|
||||
And I click on "Hide" "link" in the "Forum 1" activity
|
||||
|
||||
@@ -45,9 +45,7 @@ Feature: Activity navigation involving activities with access restrictions
|
||||
|
||||
@javascript
|
||||
Scenario: Activity navigation involving activities with access restrictions
|
||||
Given I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
When I follow "Page 1"
|
||||
Given I am on the "Page 1" "page activity" page logged in as student1
|
||||
Then I should see "Page 2" in the "#next-activity-link" "css_element"
|
||||
# Activity that has access restriction should not show up in the dropdown.
|
||||
And the "Jump to..." select box should not contain "Page 3"
|
||||
@@ -56,7 +54,7 @@ Feature: Activity navigation involving activities with access restrictions
|
||||
And I should see "Page 2" in the "#prev-activity-link" "css_element"
|
||||
And the "Jump to..." select box should not contain "Page 3"
|
||||
# Navigate to Page 2.
|
||||
And I follow "Page 2"
|
||||
And I click on "Page 2" "link" in the "page-content" "region"
|
||||
# Since Page 2 has now been viewed and deemed completed, Page 3 can now be accessed.
|
||||
And I should see "Page 3" in the "#next-activity-link" "css_element"
|
||||
And the "Jump to..." select box should contain "Page 3"
|
||||
|
||||
@@ -11,16 +11,16 @@ Feature: Add activities to courses
|
||||
| student2 | Student | 2 | student2@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | format |
|
||||
| Course 1 | C1 | topics |
|
||||
| Course 1 | Course 1 | topics |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
And I log in as "admin"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
| student1 | Course 1 | student |
|
||||
| student2 | Course 1 | student |
|
||||
|
||||
@javascript
|
||||
Scenario: Add an activity to a course
|
||||
Given I am on the "Course 1" Course page logged in as admin
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
When I add a "Database" to section "3" and I fill the form with:
|
||||
| Name | Test name |
|
||||
| Description | Test database description |
|
||||
@@ -38,15 +38,18 @@ Feature: Add activities to courses
|
||||
|
||||
@javascript
|
||||
Scenario: Add an activity supplying only the name
|
||||
Given I am on the "Course 1" Course page logged in as admin
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
When I add a "Database" to section "3" and I fill the form with:
|
||||
| Name | Test name |
|
||||
Then I should see "Test name"
|
||||
|
||||
@javascript
|
||||
Scenario: Set activity description to required then add an activity supplying only the name
|
||||
Given I set the following administration settings values:
|
||||
| requiremodintro | Yes |
|
||||
When I am on "Course 1" course homepage
|
||||
Given the following config values are set as admin:
|
||||
| requiremodintro | 1 |
|
||||
And I am on the "Course 1" Course page logged in as admin
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Database" to section "3" and I fill the form with:
|
||||
| Name | Test name |
|
||||
Then I should see "Required"
|
||||
|
||||
@@ -1346,44 +1346,6 @@ class behat_course extends behat_base {
|
||||
return $this->getSession()->getPage()->findButton(get_string('turneditingoff')) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the category with the given idnumber.
|
||||
*
|
||||
* Please note that this function requires the category to exist. If it does not exist an ExpectationException is thrown.
|
||||
*
|
||||
* @param string $idnumber
|
||||
* @return string
|
||||
* @throws ExpectationException
|
||||
*/
|
||||
protected function get_category_id($idnumber) {
|
||||
global $DB;
|
||||
try {
|
||||
return $DB->get_field('course_categories', 'id', array('idnumber' => $idnumber), MUST_EXIST);
|
||||
} catch (dml_missing_record_exception $ex) {
|
||||
throw new ExpectationException(sprintf("There is no category in the database with the idnumber '%s'", $idnumber),
|
||||
$this->getSession());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the course with the given idnumber.
|
||||
*
|
||||
* Please note that this function requires the category to exist. If it does not exist an ExpectationException is thrown.
|
||||
*
|
||||
* @param string $idnumber
|
||||
* @return string
|
||||
* @throws ExpectationException
|
||||
*/
|
||||
protected function get_course_id($idnumber) {
|
||||
global $DB;
|
||||
try {
|
||||
return $DB->get_field('course', 'id', array('idnumber' => $idnumber), MUST_EXIST);
|
||||
} catch (dml_missing_record_exception $ex) {
|
||||
throw new ExpectationException(sprintf("There is no course in the database with the idnumber '%s'", $idnumber),
|
||||
$this->getSession());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the category node from within the listing on the management page.
|
||||
*
|
||||
|
||||
@@ -20,15 +20,12 @@ Feature: Managers can create courses
|
||||
When I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add the "Latest announcements" block
|
||||
Then "Latest announcements" "block" should exist
|
||||
And I follow "Announcements"
|
||||
And I am on the Announcements "forum activity" page
|
||||
And "Add a new topic" "link" should exist
|
||||
And "Subscription mode > Forced subscription" "link" should not exist in current page administration
|
||||
And "Subscription mode > Forced subscription" "text" should exist in current page administration
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Announcements"
|
||||
And I am on the Announcements "forum activity" page logged in as student1
|
||||
And "Add a new topic" "link" should not exist
|
||||
And "Forced subscription" "text" should exist in current page administration
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ Feature: Courses can be set up to display dates relative to the user's enrolment
|
||||
|
||||
@javascript
|
||||
Scenario: Create a course with relative dates feature disabled
|
||||
Given I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
Given the following config values are set as admin:
|
||||
| enablecourserelativedates | 0 |
|
||||
And I log in as "admin"
|
||||
And I am on site homepage
|
||||
And I turn editing mode on
|
||||
When I press "Add a new course"
|
||||
@@ -18,20 +18,19 @@ Feature: Courses can be set up to display dates relative to the user's enrolment
|
||||
|
||||
@javascript
|
||||
Scenario: Create a course with relative dates feature enabled
|
||||
Given I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
Given the following config values are set as admin:
|
||||
| enablecourserelativedates | 1 |
|
||||
And I log in as "admin"
|
||||
And I am on site homepage
|
||||
And I turn editing mode on
|
||||
When I press "Add a new course"
|
||||
And I wait until the page is ready
|
||||
Then I should see "Relative dates mode"
|
||||
And I should see "Relative dates mode cannot be changed once the course has been created."
|
||||
|
||||
Scenario: Edit courses with relative dates feature enabled
|
||||
Given I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
Given the following config values are set as admin:
|
||||
| enablecourserelativedates | 1 |
|
||||
And I log in as "admin"
|
||||
And I create a course with:
|
||||
| Course full name | Course 1 |
|
||||
| Course short name | C1 |
|
||||
|
||||
@@ -13,11 +13,11 @@ Feature: Users can request and approve courses
|
||||
|
||||
Scenario: Simple course request workflow
|
||||
Given the following "system role assigns" exist:
|
||||
| user | course | role |
|
||||
| user | course | role |
|
||||
| user2 | Acceptance test site | manager |
|
||||
Given I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
And the following config values are set as admin:
|
||||
| lockrequestcategory | 1 |
|
||||
Given I log in as "admin"
|
||||
And I set the following system permissions of "Authenticated user" role:
|
||||
| capability | permission |
|
||||
| moodle/course:request | Allow |
|
||||
|
||||
@@ -20,8 +20,7 @@ Feature: View subfolders in a course in-line
|
||||
| Name | Test folder |
|
||||
| Display folder contents | On a separate page |
|
||||
| Show subfolders expanded | |
|
||||
And I should see "Test folder"
|
||||
And I follow "Test folder"
|
||||
And I am on the "Test folder" "folder activity" page
|
||||
And I press "Edit"
|
||||
And I press "Create folder"
|
||||
And I set the field "New folder name" to "Test subfolder 1"
|
||||
@@ -32,7 +31,7 @@ Feature: View subfolders in a course in-line
|
||||
Scenario: Add a folder with two subfolders - view on separate page
|
||||
Given I am on "Course 1" course homepage
|
||||
And I should not see "Test subfolder 1"
|
||||
And I follow "Test folder"
|
||||
And I am on the "Test folder" "folder activity" page
|
||||
And I should see "Test subfolder 1"
|
||||
And I press "Edit"
|
||||
And I press "Create folder"
|
||||
@@ -41,13 +40,13 @@ Feature: View subfolders in a course in-line
|
||||
And I press "Save changes"
|
||||
When I am on "Course 1" course homepage
|
||||
Then I should not see "Test subfolder 2"
|
||||
And I follow "Test folder"
|
||||
And I am on the "Test folder" "folder activity" page
|
||||
And I should see "Test subfolder 2"
|
||||
Given I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test folder" "folder activity editing" page
|
||||
And I set the field "Show subfolders expanded" to "1"
|
||||
When I am on "Course 1" course homepage
|
||||
Then I should not see "Test subfolder 2"
|
||||
And I follow "Test folder"
|
||||
And I am on the "Test folder" "folder activity" page
|
||||
And I should see "Test subfolder 2"
|
||||
|
||||
@javascript
|
||||
@@ -63,8 +62,7 @@ Feature: View subfolders in a course in-line
|
||||
And I press "Save and return to course"
|
||||
Then I should see "Test subfolder 1"
|
||||
And I should not see "Test sub subfolder"
|
||||
Given I open "Test folder" actions menu
|
||||
When I click on "Edit settings" "link" in the "Test folder" activity
|
||||
And I am on the "Test folder" "folder activity editing" page
|
||||
And I set the field "Show subfolders expanded" to "1"
|
||||
And I press "Save and return to course"
|
||||
Then I should see "Test subfolder 1"
|
||||
|
||||
@@ -4550,7 +4550,7 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
'totalcourses' => 0,
|
||||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'expecteddbqueries' => 1,
|
||||
'expecteddbqueries' => 4,
|
||||
'expectedresult' => $buildexpectedresult(0, 0)
|
||||
],
|
||||
'less than query limit' => [
|
||||
@@ -4558,7 +4558,7 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
'totalcourses' => 2,
|
||||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'expecteddbqueries' => 1,
|
||||
'expecteddbqueries' => 2,
|
||||
'expectedresult' => $buildexpectedresult(2, 0)
|
||||
],
|
||||
'more than query limit' => [
|
||||
@@ -4566,7 +4566,7 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
'totalcourses' => 7,
|
||||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'expecteddbqueries' => 3,
|
||||
'expecteddbqueries' => 4,
|
||||
'expectedresult' => $buildexpectedresult(7, 0)
|
||||
],
|
||||
'limit less than query limit' => [
|
||||
@@ -4574,7 +4574,7 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
'totalcourses' => 7,
|
||||
'limit' => 2,
|
||||
'offset' => 0,
|
||||
'expecteddbqueries' => 1,
|
||||
'expecteddbqueries' => 2,
|
||||
'expectedresult' => $buildexpectedresult(2, 0)
|
||||
],
|
||||
'limit less than query limit with offset' => [
|
||||
@@ -4582,7 +4582,7 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
'totalcourses' => 7,
|
||||
'limit' => 2,
|
||||
'offset' => 2,
|
||||
'expecteddbqueries' => 1,
|
||||
'expecteddbqueries' => 2,
|
||||
'expectedresult' => $buildexpectedresult(2, 2)
|
||||
],
|
||||
'limit less than total' => [
|
||||
@@ -4590,7 +4590,7 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
'totalcourses' => 9,
|
||||
'limit' => 6,
|
||||
'offset' => 0,
|
||||
'expecteddbqueries' => 2,
|
||||
'expecteddbqueries' => 3,
|
||||
'expectedresult' => $buildexpectedresult(6, 0)
|
||||
],
|
||||
'less results than limit' => [
|
||||
@@ -4598,7 +4598,7 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
'totalcourses' => 9,
|
||||
'limit' => 20,
|
||||
'offset' => 0,
|
||||
'expecteddbqueries' => 3,
|
||||
'expecteddbqueries' => 4,
|
||||
'expectedresult' => $buildexpectedresult(9, 0)
|
||||
],
|
||||
'less results than limit exact divisible' => [
|
||||
@@ -4606,7 +4606,7 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
'totalcourses' => 9,
|
||||
'limit' => 20,
|
||||
'offset' => 0,
|
||||
'expecteddbqueries' => 4,
|
||||
'expecteddbqueries' => 5,
|
||||
'expectedresult' => $buildexpectedresult(9, 0)
|
||||
],
|
||||
'less results than limit with offset' => [
|
||||
@@ -4614,7 +4614,7 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
'totalcourses' => 9,
|
||||
'limit' => 10,
|
||||
'offset' => 5,
|
||||
'expecteddbqueries' => 2,
|
||||
'expecteddbqueries' => 3,
|
||||
'expectedresult' => $buildexpectedresult(4, 5)
|
||||
],
|
||||
];
|
||||
@@ -5442,7 +5442,16 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
// Every course accessed, order by shortname DESC. The last create course ($course[2]) should have the greater shortname.
|
||||
$result = course_get_recent_courses($student->id, 0, 0, 'shortname DESC');
|
||||
$this->assertCount(3, $result);
|
||||
$this->assertEquals($courses[2]->id, array_shift($result)->id);
|
||||
$this->assertEquals($courses[2]->id, array_values($result)[0]->id);
|
||||
$this->assertEquals($courses[1]->id, array_values($result)[1]->id);
|
||||
$this->assertEquals($courses[0]->id, array_values($result)[2]->id);
|
||||
|
||||
// Every course accessed, order by shortname ASC.
|
||||
$result = course_get_recent_courses($student->id, 0, 0, 'shortname ASC');
|
||||
$this->assertCount(3, $result);
|
||||
$this->assertEquals($courses[0]->id, array_values($result)[0]->id);
|
||||
$this->assertEquals($courses[1]->id, array_values($result)[1]->id);
|
||||
$this->assertEquals($courses[2]->id, array_values($result)[2]->id);
|
||||
|
||||
$guestcourse = $generator->create_course(
|
||||
(object)array('shortname' => 'guestcourse',
|
||||
@@ -5464,6 +5473,56 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
$this->assertArrayNotHasKey($courses[0]->id, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the validation of the sort value in course_get_recent_courses().
|
||||
*
|
||||
* @dataProvider course_get_recent_courses_sort_validation_provider
|
||||
* @param string $sort The sort value
|
||||
* @param string $expectedexceptionmsg The expected exception message
|
||||
*/
|
||||
public function test_course_get_recent_courses_sort_validation(string $sort, string $expectedexceptionmsg) {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
if (!empty($expectedexceptionmsg)) {
|
||||
$this->expectException('invalid_parameter_exception');
|
||||
$this->expectExceptionMessage($expectedexceptionmsg);
|
||||
}
|
||||
course_get_recent_courses($user->id, 0, 0, $sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for test_course_get_recent_courses_sort_validation().
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function course_get_recent_courses_sort_validation_provider() {
|
||||
return [
|
||||
'Invalid sort format (SQL injection attempt)' =>
|
||||
[
|
||||
'shortname DESC LIMIT 1--',
|
||||
'Invalid structure of the sort parameter, allowed structure: fieldname [ASC|DESC].',
|
||||
],
|
||||
'Sort uses \'sort by\' field that does not exist' =>
|
||||
[
|
||||
'shortname DESC, xyz ASC',
|
||||
'Invalid field in the sort parameter, allowed fields: id, idnumber, summary, summaryformat, ' .
|
||||
'startdate, enddate, category, shortname, fullname, timeaccess, component, visible.',
|
||||
],
|
||||
'Sort uses invalid value for the sorting direction' =>
|
||||
[
|
||||
'shortname xyz, lastaccess',
|
||||
'Invalid sort direction in the sort parameter, allowed values: asc, desc.',
|
||||
],
|
||||
'Valid sort format' =>
|
||||
[
|
||||
'shortname asc, timeaccess',
|
||||
''
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the course_get_recent_courses function.
|
||||
*/
|
||||
|
||||
@@ -2765,7 +2765,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
/**
|
||||
* Test cases for the get_enrolled_courses_by_timeline_classification test.
|
||||
*/
|
||||
public function get_get_enrolled_courses_by_timeline_classification_test_cases() {
|
||||
public function get_get_enrolled_courses_by_timeline_classification_test_cases():array {
|
||||
$now = time();
|
||||
$day = 86400;
|
||||
|
||||
@@ -2864,6 +2864,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
'classification' => 'future',
|
||||
'limit' => 2,
|
||||
'offset' => 0,
|
||||
'sort' => 'shortname ASC',
|
||||
'expectedcourses' => [],
|
||||
'expectednextoffset' => 0
|
||||
],
|
||||
@@ -2873,6 +2874,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
'classification' => 'future',
|
||||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'sort' => 'shortname ASC',
|
||||
'expectedcourses' => ['afuture', 'bfuture', 'cfuture', 'dfuture', 'efuture'],
|
||||
'expectednextoffset' => 15
|
||||
],
|
||||
@@ -2881,6 +2883,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
'classification' => 'future',
|
||||
'limit' => 2,
|
||||
'offset' => 0,
|
||||
'sort' => 'shortname ASC',
|
||||
'expectedcourses' => ['afuture', 'bfuture'],
|
||||
'expectednextoffset' => 4
|
||||
],
|
||||
@@ -2889,6 +2892,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
'classification' => 'future',
|
||||
'limit' => 2,
|
||||
'offset' => 2,
|
||||
'sort' => 'shortname ASC',
|
||||
'expectedcourses' => ['bfuture', 'cfuture'],
|
||||
'expectednextoffset' => 7
|
||||
],
|
||||
@@ -2897,6 +2901,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
'classification' => 'future',
|
||||
'limit' => 5,
|
||||
'offset' => 0,
|
||||
'sort' => 'shortname ASC',
|
||||
'expectedcourses' => ['afuture', 'bfuture', 'cfuture', 'dfuture', 'efuture'],
|
||||
'expectednextoffset' => 13
|
||||
],
|
||||
@@ -2905,6 +2910,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
'classification' => 'future',
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'sort' => 'shortname ASC',
|
||||
'expectedcourses' => ['afuture', 'bfuture', 'cfuture', 'dfuture', 'efuture'],
|
||||
'expectednextoffset' => 15
|
||||
],
|
||||
@@ -2913,6 +2919,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
'classification' => 'future',
|
||||
'limit' => 10,
|
||||
'offset' => 5,
|
||||
'sort' => 'shortname ASC',
|
||||
'expectedcourses' => ['cfuture', 'dfuture', 'efuture'],
|
||||
'expectednextoffset' => 15
|
||||
],
|
||||
@@ -2921,6 +2928,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
'classification' => 'all',
|
||||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'sort' => 'shortname ASC',
|
||||
'expectedcourses' => [
|
||||
'afuture',
|
||||
'ainprogress',
|
||||
@@ -2945,6 +2953,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 0,
|
||||
'sort' => 'shortname ASC',
|
||||
'expectedcourses' => [
|
||||
'afuture',
|
||||
'ainprogress',
|
||||
@@ -2959,6 +2968,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 5,
|
||||
'sort' => 'shortname ASC',
|
||||
'expectedcourses' => [
|
||||
'bpast',
|
||||
'cfuture',
|
||||
@@ -2973,9 +2983,161 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 50,
|
||||
'sort' => 'shortname ASC',
|
||||
'expectedcourses' => [],
|
||||
'expectednextoffset' => 50
|
||||
],
|
||||
'all limit and offset with sort ul.timeaccess desc' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'inprogress',
|
||||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'sort' => 'ul.timeaccess desc',
|
||||
'expectedcourses' => [
|
||||
'ainprogress',
|
||||
'binprogress',
|
||||
'cinprogress',
|
||||
'dinprogress',
|
||||
'einprogress'
|
||||
],
|
||||
'expectednextoffset' => 15
|
||||
],
|
||||
'all limit and offset with sort sql injection for sort or 1==1' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 5,
|
||||
'sort' => 'ul.timeaccess desc or 1==1',
|
||||
'expectedcourses' => [],
|
||||
'expectednextoffset' => 0,
|
||||
'expectedexception' => 'Invalid $sort parameter in enrol_get_my_courses()'
|
||||
],
|
||||
'all limit and offset with sql injection of sort a custom one' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 5,
|
||||
'sort' => "ul.timeaccess LIMIT 1--",
|
||||
'expectedcourses' => [],
|
||||
'expectednextoffset' => 0,
|
||||
'expectedexception' => 'Invalid $sort parameter in enrol_get_my_courses()'
|
||||
],
|
||||
'all limit and offset with wrong sort direction' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 5,
|
||||
'sort' => "ul.timeaccess abcdasc",
|
||||
'expectedcourses' => [],
|
||||
'expectednextoffset' => 0,
|
||||
'expectedexception' => 'Invalid sort direction in $sort parameter in enrol_get_my_courses()'
|
||||
],
|
||||
'all limit and offset with wrong sort direction' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 5,
|
||||
'sort' => "ul.timeaccess.foo ascd",
|
||||
'expectedcourses' => [],
|
||||
'expectednextoffset' => 0,
|
||||
'expectedexception' => 'Invalid sort direction in $sort parameter in enrol_get_my_courses()'
|
||||
],
|
||||
'all limit and offset with wrong sort param' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 5,
|
||||
'sort' => "foobar",
|
||||
'expectedcourses' => [],
|
||||
'expectednextoffset' => 0,
|
||||
'expectedexception' => 'Invalid $sort parameter in enrol_get_my_courses()'
|
||||
],
|
||||
'all limit and offset with wrong field name' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 5,
|
||||
'sort' => "ul.foobar",
|
||||
'expectedcourses' => [],
|
||||
'expectednextoffset' => 0,
|
||||
'expectedexception' => 'Invalid $sort parameter in enrol_get_my_courses()'
|
||||
],
|
||||
'all limit and offset with wrong field separator' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 5,
|
||||
'sort' => "ul.timeaccess.foo",
|
||||
'expectedcourses' => [],
|
||||
'expectednextoffset' => 0,
|
||||
'expectedexception' => 'Invalid $sort parameter in enrol_get_my_courses()'
|
||||
],
|
||||
'all limit and offset with wrong field separator #' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 5,
|
||||
'sort' => "ul#timeaccess",
|
||||
'expectedcourses' => [],
|
||||
'expectednextoffset' => 0,
|
||||
'expectedexception' => 'Invalid $sort parameter in enrol_get_my_courses()'
|
||||
],
|
||||
'all limit and offset with wrong field separator $' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 5,
|
||||
'sort' => 'ul$timeaccess',
|
||||
'expectedcourses' => [],
|
||||
'expectednextoffset' => 0,
|
||||
'expectedexception' => 'Invalid $sort parameter in enrol_get_my_courses()'
|
||||
],
|
||||
'all limit and offset with wrong field name' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 5,
|
||||
'sort' => 'timeaccess123',
|
||||
'expectedcourses' => [],
|
||||
'expectednextoffset' => 0,
|
||||
'expectedexception' => 'Invalid $sort parameter in enrol_get_my_courses()'
|
||||
],
|
||||
'all limit and offset with no sort direction for ul' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'inprogress',
|
||||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'sort' => "ul.timeaccess",
|
||||
'expectedcourses' => ['ainprogress', 'binprogress', 'cinprogress', 'dinprogress', 'einprogress'],
|
||||
'expectednextoffset' => 15,
|
||||
],
|
||||
'all limit and offset with valid field name and no prefix, test for ul' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'inprogress',
|
||||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'sort' => "timeaccess",
|
||||
'expectedcourses' => ['ainprogress', 'binprogress', 'cinprogress', 'dinprogress', 'einprogress'],
|
||||
'expectednextoffset' => 15,
|
||||
],
|
||||
'all limit and offset with valid field name and no prefix' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 5,
|
||||
'sort' => "fullname",
|
||||
'expectedcourses' => ['bpast', 'cpast', 'dfuture', 'dpast', 'efuture'],
|
||||
'expectednextoffset' => 10,
|
||||
],
|
||||
'all limit and offset with valid field name and no prefix and with sort direction' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'all',
|
||||
'limit' => 5,
|
||||
'offset' => 5,
|
||||
'sort' => "fullname desc",
|
||||
'expectedcourses' => ['bpast', 'cpast', 'dfuture', 'dpast', 'efuture'],
|
||||
'expectednextoffset' => 10,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -2987,16 +3149,20 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
* @param string $classification Timeline classification
|
||||
* @param int $limit Maximum number of results
|
||||
* @param int $offset Offset the unfiltered courses result set by this amount
|
||||
* @param string $sort sort the courses
|
||||
* @param array $expectedcourses Expected courses in result
|
||||
* @param int $expectednextoffset Expected next offset value in result
|
||||
* @param string|null $expectedexception Expected exception string
|
||||
*/
|
||||
public function test_get_enrolled_courses_by_timeline_classification(
|
||||
$coursedata,
|
||||
$classification,
|
||||
$limit,
|
||||
$offset,
|
||||
$sort,
|
||||
$expectedcourses,
|
||||
$expectednextoffset
|
||||
$expectednextoffset,
|
||||
$expectedexception = null
|
||||
) {
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
@@ -3013,6 +3179,11 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
|
||||
$this->setUser($student);
|
||||
|
||||
if (isset($expectedexception)) {
|
||||
$this->expectException('coding_exception');
|
||||
$this->expectExceptionMessage($expectedexception);
|
||||
}
|
||||
|
||||
// NOTE: The offset applies to the unfiltered full set of courses before the classification
|
||||
// filtering is done.
|
||||
// E.g. In our example if an offset of 2 is given then it would mean the first
|
||||
@@ -3021,7 +3192,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
$classification,
|
||||
$limit,
|
||||
$offset,
|
||||
'shortname ASC'
|
||||
$sort
|
||||
);
|
||||
$result = external_api::clean_returnvalue(
|
||||
core_course_external::get_enrolled_courses_by_timeline_classification_returns(),
|
||||
@@ -3032,7 +3203,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
return $course['shortname'];
|
||||
}, $result['courses']);
|
||||
|
||||
$this->assertEquals($expectedcourses, $actual);
|
||||
$this->assertEqualsCanonicalizing($expectedcourses, $actual);
|
||||
$this->assertEquals($expectednextoffset, $result['nextoffset']);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
This files describes API changes in /dataformat/ download system,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 3.9.8 ===
|
||||
* Optional $callback for the following \core\dataformat methods now receive a second argument to define
|
||||
whether the current format writer supports HTML:
|
||||
- download_data()
|
||||
- write_data()
|
||||
- write_data_to_filearea()
|
||||
|
||||
=== 3.9 ===
|
||||
* The following methods have been added to the base dataformat class to allow instances to export to a local
|
||||
file. They can be overridden in extending classes to define how files should be created:
|
||||
|
||||
@@ -24,10 +24,7 @@ Feature: Render H5P content using filters
|
||||
|
||||
@javascript @external
|
||||
Scenario: Render an external H5P content URL.
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
Given I am on the PageName1 "page activity editing" page logged in as teacher1
|
||||
And I set the field "Page content" to "<div>Go for it</div>https://moodle.h5p.com/content/1290772960722742119/embed"
|
||||
When I click on "Save and display" "button"
|
||||
And I wait until the page is ready
|
||||
@@ -36,10 +33,7 @@ Feature: Render H5P content using filters
|
||||
|
||||
@javascript
|
||||
Scenario: Add an external H5P content URL in a link with the URL. Should be rendered.
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
Given I am on the PageName1 "page activity editing" page logged in as teacher1
|
||||
# This content won't be displayed, so this scenario shouldn't be labeled as external.
|
||||
And I set the field "Page content" to "<a href='https://moodle.h5p.com/content/1290772960722742119/embed'>https://moodle.h5p.com/content/1290772960722742119/embed</a>"
|
||||
When I click on "Save and display" "button"
|
||||
@@ -48,10 +42,7 @@ Feature: Render H5P content using filters
|
||||
Then I should see "Lorum ipsum"
|
||||
|
||||
Scenario: Add an external H5P content URL in a link with text. Shouldn't be rendered.
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
Given I am on the PageName1 "page activity editing" page logged in as teacher1
|
||||
# This content won't be displayed, so this scenario shouldn't be labeled as external.
|
||||
And I set the field "Page content" to "<a href='https://moodle.h5p.com/content/1290772960722742119/embed'>Here you are the content</a>"
|
||||
When I click on "Save and display" "button"
|
||||
@@ -67,8 +58,7 @@ Feature: Render H5P content using filters
|
||||
| Name | ipsumFile |
|
||||
And I upload "h5p/tests/fixtures/ipsums.h5p" file to "Select files" filemanager
|
||||
And I press "Save and return to course"
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName1 "page activity editing" page
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I click on "Browse repositories..." "button" in the "Insert H5P" "dialogue"
|
||||
And I click on "Server files" "link" in the ".fp-repo-area" "css_element"
|
||||
@@ -85,9 +75,7 @@ Feature: Render H5P content using filters
|
||||
Then I should see "Lorum ipsum"
|
||||
And I switch to the main frame
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I am on the PageName1 "page activity" page logged in as student1
|
||||
# Switch to iframe created by filter
|
||||
And I switch to "h5p-iframe" class iframe
|
||||
# Switch to iframe created by embed.php page
|
||||
@@ -104,8 +92,7 @@ Feature: Render H5P content using filters
|
||||
| Name | ipsumFile |
|
||||
And I upload "h5p/tests/fixtures/ipsums.h5p" file to "Select files" filemanager
|
||||
And I press "Save and return to course"
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName1 "page activity editing" page
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I click on "Browse repositories..." "button" in the "Insert H5P" "dialogue"
|
||||
And I click on "Server files" "link" in the ".fp-repo-area" "css_element"
|
||||
@@ -129,8 +116,7 @@ Feature: Render H5P content using filters
|
||||
| Name | ipsumFileTeacher |
|
||||
And I upload "h5p/tests/fixtures/ipsums.h5p" file to "Select files" filemanager
|
||||
And I press "Save and return to course"
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName1 "page activity editing" page
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I click on "Browse repositories..." "button" in the "Insert H5P" "dialogue"
|
||||
And I click on "Server files" "link" in the ".fp-repo-area" "css_element"
|
||||
@@ -153,8 +139,7 @@ Feature: Render H5P content using filters
|
||||
| Name | ipsumFile |
|
||||
And I upload "h5p/tests/fixtures/ipsums.h5p" file to "Select files" filemanager
|
||||
And I press "Save and return to course"
|
||||
And I follow "PageName2"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName2 "page activity editing" page
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I click on "Browse repositories..." "button" in the "Insert H5P" "dialogue"
|
||||
And I click on "Server files" "link" in the ".fp-repo-area" "css_element"
|
||||
@@ -172,9 +157,7 @@ Feature: Render H5P content using filters
|
||||
And I should see "Lorum ipsum"
|
||||
And I switch to the main frame
|
||||
And I log out
|
||||
When I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I am on the PageName1 "page activity" page logged in as teacher1
|
||||
# Switch to iframe created by filter
|
||||
And I switch to "h5p-iframe" class iframe
|
||||
# Switch to iframe created by embed.php page
|
||||
|
||||
+13
-10
@@ -47,23 +47,23 @@
|
||||
* @param int $fontsize the font size
|
||||
* @return string the latex document
|
||||
*/
|
||||
function construct_latex_document( $formula, $fontsize=12 ) {
|
||||
global $CFG;
|
||||
|
||||
$formula = filter_tex_sanitize_formula($formula);
|
||||
|
||||
function construct_latex_document($formula, $fontsize = 12) {
|
||||
// $fontsize don't affects to formula's size. $density can change size
|
||||
$doc = "\\documentclass[{$fontsize}pt]{article}\n";
|
||||
$doc = "\\documentclass[{$fontsize}pt]{article}\n";
|
||||
$doc .= get_config('filter_tex', 'latexpreamble');
|
||||
$doc .= "\\pagestyle{empty}\n";
|
||||
$doc .= "\\begin{document}\n";
|
||||
//dlnsk $doc .= "$ {$formula} $\n";
|
||||
if (preg_match("/^[[:space:]]*\\\\begin\\{(gather|align|alignat|multline).?\\}/i",$formula)) {
|
||||
if (preg_match("/^[[:space:]]*\\\\begin\\{(gather|align|alignat|multline).?\\}/i", $formula)) {
|
||||
$doc .= "$formula\n";
|
||||
} else {
|
||||
$doc .= "$ {$formula} $\n";
|
||||
}
|
||||
$doc .= "\\end{document}\n";
|
||||
|
||||
// Sanitize the whole document (rather than just the formula) to make sure no one can bypass sanitization
|
||||
// by using \newcommand in preamble to give an alias to a blocked command.
|
||||
$doc = filter_tex_sanitize_formula($doc);
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
@@ -114,11 +114,14 @@
|
||||
$convertformat = 'png';
|
||||
}
|
||||
$filename = str_replace(".{$convertformat}", '', $filename);
|
||||
$tex = "{$this->temp_dir}/$filename.tex";
|
||||
$tex = "$filename.tex"; // Absolute paths won't work with openin_any = p setting.
|
||||
$dvi = "{$this->temp_dir}/$filename.dvi";
|
||||
$ps = "{$this->temp_dir}/$filename.ps";
|
||||
$img = "{$this->temp_dir}/$filename.{$convertformat}";
|
||||
|
||||
// Change directory to temp dir so that we can work with relative paths.
|
||||
chdir($this->temp_dir);
|
||||
|
||||
// turn the latex doc into a .tex file in the temp area
|
||||
$fh = fopen( $tex, 'w' );
|
||||
fputs( $fh, $doc );
|
||||
@@ -126,7 +129,7 @@
|
||||
|
||||
// run latex on document
|
||||
$command = "$pathlatex --interaction=nonstopmode --halt-on-error $tex";
|
||||
chdir( $this->temp_dir );
|
||||
|
||||
if ($this->execute($command, $log)) { // It allways False on Windows
|
||||
// return false;
|
||||
}
|
||||
|
||||
+30
-2
@@ -61,7 +61,7 @@ function filter_tex_get_executable($debug=false) {
|
||||
|
||||
function filter_tex_sanitize_formula($texexp) {
|
||||
/// Check $texexp against blacklist (whitelisting could be more complete but also harder to maintain)
|
||||
$tex_blacklist = array(
|
||||
$denylist = [
|
||||
'include','command','loop','repeat','open','toks','output',
|
||||
'input','catcode','name','^^',
|
||||
'\def','\edef','\gdef','\xdef',
|
||||
@@ -71,9 +71,37 @@ function filter_tex_sanitize_formula($texexp) {
|
||||
'\afterassignment','\expandafter','\noexpand','\special',
|
||||
'\let', '\futurelet','\else','\fi','\chardef','\makeatletter','\afterground',
|
||||
'\noexpand','\line','\mathcode','\item','\section','\mbox','\declarerobustcommand'
|
||||
];
|
||||
|
||||
$allowlist = ['inputenc'];
|
||||
|
||||
// Prepare the denylist for regular expression.
|
||||
$denylist = array_map(function($value){
|
||||
return '/' . preg_quote($value, '/') . '/i';
|
||||
}, $denylist);
|
||||
|
||||
// Prepare the allowlist for regular expression.
|
||||
$allowlist = array_map(function($value){
|
||||
return '/\bforbiddenkeyword_(' . preg_quote($value, '/') . ')\b/i';
|
||||
}, $allowlist);
|
||||
|
||||
// First, mangle all denied words.
|
||||
$texexp = preg_replace_callback($denylist,
|
||||
function($matches) {
|
||||
return 'forbiddenkeyword_' . $matches[0];
|
||||
},
|
||||
$texexp
|
||||
);
|
||||
|
||||
return str_ireplace($tex_blacklist, 'forbiddenkeyword', $texexp);
|
||||
// Then, change back the allowed words.
|
||||
$texexp = preg_replace_callback($allowlist,
|
||||
function($matches) {
|
||||
return $matches[1];
|
||||
},
|
||||
$texexp
|
||||
);
|
||||
|
||||
return $texexp;
|
||||
}
|
||||
|
||||
function filter_tex_get_cmd($pathname, $texexp) {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Tex filter library functions tests
|
||||
*
|
||||
* @package filter_tex
|
||||
* @category test
|
||||
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace filter_tex;
|
||||
|
||||
use advanced_testcase;
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/filter/tex/lib.php');
|
||||
|
||||
/**
|
||||
* Tex filter library functions tests
|
||||
*
|
||||
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class lib_test extends advanced_testcase {
|
||||
/**
|
||||
* Data provider for test_filter_tex_sanitize_formula.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filter_tex_sanitize_formula_provider() : array {
|
||||
return [
|
||||
['x\ =\ \frac{\sqrt{144}}{2}\ \times\ (y\ +\ 12)', 'x\ =\ \frac{\sqrt{144}}{2}\ \times\ (y\ +\ 12)'],
|
||||
['\usepackage[latin1]{inputenc}', '\usepackage[latin1]{inputenc}'],
|
||||
['\newcommand{\A}{\verbatiminput}', '\newforbiddenkeyword_command{\A}{\verbatimforbiddenkeyword_input}'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for filter_tex_sanitize_formula() function.
|
||||
*
|
||||
* @dataProvider filter_tex_sanitize_formula_provider
|
||||
* @param $formula The formula to test
|
||||
* @param $expected The sanitized version of the formula we expect to get
|
||||
*/
|
||||
public function test_filter_tex_sanitize_formula(string $formula, string $expected) {
|
||||
$this->assertEquals($expected, filter_tex_sanitize_formula($formula));
|
||||
}
|
||||
}
|
||||
@@ -238,12 +238,15 @@
|
||||
$output .= "<p>base filename for expression is '$md5'</p>\n";
|
||||
|
||||
// temporary paths
|
||||
$tex = "$latex->temp_dir/$md5.tex";
|
||||
$tex = "$md5.tex"; // Absolute paths won't work with openin_any = p setting.
|
||||
$dvi = "$latex->temp_dir/$md5.dvi";
|
||||
$ps = "$latex->temp_dir/$md5.ps";
|
||||
$convertformat = get_config('filter_tex', 'convertformat');
|
||||
$img = "$latex->temp_dir/$md5.{$convertformat}";
|
||||
|
||||
// Change directory to temp dir so that we can work with relative paths.
|
||||
chdir($latex->temp_dir);
|
||||
|
||||
// put the expression as a file into the temp area
|
||||
$expression = html_entity_decode($expression);
|
||||
$output .= "<p>Processing TeX expression:</p><pre>$expression</pre>\n";
|
||||
@@ -252,9 +255,6 @@
|
||||
fputs($fh, $doc);
|
||||
fclose($fh);
|
||||
|
||||
// cd to temp dir
|
||||
chdir($latex->temp_dir);
|
||||
|
||||
// step 1: latex command
|
||||
$pathlatex = escapeshellarg($pathlatex);
|
||||
$cmd = "$pathlatex --interaction=nonstopmode --halt-on-error $tex";
|
||||
|
||||
@@ -50,7 +50,8 @@ Feature: Marking guides can be created and edited
|
||||
@javascript
|
||||
Scenario: Deleting criterion and comment
|
||||
# Deleting criterion
|
||||
When I go to "Test assignment 1 name" advanced grading definition page
|
||||
When I am on "Course 1" course homepage
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And I click on "Delete criterion" "button" in the "Guide criterion B" "table_row"
|
||||
And I press "Yes"
|
||||
And I press "Save"
|
||||
@@ -59,7 +60,8 @@ Feature: Marking guides can be created and edited
|
||||
And I should see "WARNING: Your marking guide has a maximum grade of 70 points"
|
||||
But I should not see "Guide criterion B"
|
||||
# Deleting a frequently used comment
|
||||
When I go to "Test assignment 1 name" advanced grading definition page
|
||||
When I am on "Course 1" course homepage
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And I click on "Delete comment" "button" in the "Comment 3" "table_row"
|
||||
And I press "Yes"
|
||||
And I press "Save"
|
||||
@@ -91,9 +93,7 @@ Feature: Marking guides can be created and edited
|
||||
Then I should see "80" in the "Student 1" "table_row"
|
||||
And I log out
|
||||
# Viewing it as a student.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I am on the "Test assignment 1 name" "assign activity" page logged in as student1
|
||||
And I should see "80" in the ".feedback" "css_element"
|
||||
And I should see "Marking guide test description" in the ".feedback" "css_element"
|
||||
And I should see "Very good"
|
||||
|
||||
@@ -70,9 +70,7 @@ Feature: Rubrics can be created and edited
|
||||
And I should see "35" in the "Student 1" "table_row"
|
||||
And I log out
|
||||
# Viewing it as a student.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I am on the "Test assignment 1 name" "assign activity" page logged in as student1
|
||||
And I should see "35" in the ".feedback" "css_element"
|
||||
And I should see "Rubric test description" in the ".feedback" "css_element"
|
||||
And I should see "In general... work harder..."
|
||||
@@ -80,8 +78,7 @@ Feature: Rubrics can be created and edited
|
||||
And the level with "20" points is selected for the rubric criterion "Criterion 1"
|
||||
And the level with "5" points is selected for the rubric criterion "Criterion 3"
|
||||
And I log out
|
||||
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
|
||||
# Editing a rubric definition without regrading students.
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And "Save as draft" "button" should not exist
|
||||
@@ -93,9 +90,7 @@ Feature: Rubrics can be created and edited
|
||||
And I press "Continue"
|
||||
And I log out
|
||||
# Check that the student still sees the grade.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I am on the "Test assignment 1 name" "assign activity" page logged in as student1
|
||||
And I should see "35" in the ".feedback" "css_element"
|
||||
And the level with "20" points is selected for the rubric criterion "Criterion 1"
|
||||
And I log out
|
||||
@@ -110,30 +105,23 @@ Feature: Rubrics can be created and edited
|
||||
And I press "Continue"
|
||||
And I log out
|
||||
# Check that the student doesn't see the grade.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I am on the "Test assignment 1 name" "assign activity" page logged in as student1
|
||||
And I should see "35" in the ".feedback" "css_element"
|
||||
And the level with "20" points is not selected for the rubric criterion "Criterion 1"
|
||||
And I log out
|
||||
# Regrade student.
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I am on the "Test assignment 1 name" "assign activity" page logged in as teacher1
|
||||
And I go to "Student 1" "Test assignment 1 name" activity advanced grading page
|
||||
And I should see "The rubric definition was changed after this student had been graded. The student can not see this rubric until you check the rubric and update the grade."
|
||||
And I save the advanced grading form
|
||||
And I log out
|
||||
# Check that the student sees the grade again.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I am on the "Test assignment 1 name" "assign activity" page logged in as student1
|
||||
And I should see "31.82" in the ".feedback" "css_element"
|
||||
And the level with "20" points is not selected for the rubric criterion "Criterion 1"
|
||||
# Hide all rubric info for students
|
||||
And I log out
|
||||
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
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And I set the field "Allow users to preview rubric (otherwise it will only be displayed after grading)" to ""
|
||||
And I set the field "Display rubric description during evaluation" to ""
|
||||
@@ -145,9 +133,7 @@ Feature: Rubrics can be created and edited
|
||||
And I press "Continue"
|
||||
And I log out
|
||||
# Students should not see anything.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I am on the "Test assignment 1 name" "assign activity" page logged in as student1
|
||||
And I should not see "Criterion 1" in the ".submissionstatustable" "css_element"
|
||||
And I should not see "Criterion 2" in the ".submissionstatustable" "css_element"
|
||||
And I should not see "Criterion 3" in the ".submissionstatustable" "css_element"
|
||||
|
||||
@@ -14,12 +14,14 @@ Feature: Reuse my rubrics in other activities
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Assignment" to section "1" and I fill the form with:
|
||||
| Assignment name | Test assignment 1 name |
|
||||
| Description | Test assignment 1 description |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | section | idnumber |
|
||||
| assign | Test assignment 1 name | Test assignment 1 description | C1 | 1 | assign1 |
|
||||
| assign | Test assignment 2 name | Test assignment 2 description | C1 | 1 | assign1 |
|
||||
And I am on the "Test assignment 1 name" "assign activity editing" page logged in as teacher1
|
||||
And I set the following fields to these values:
|
||||
| Grading method | Rubric |
|
||||
And I press "Save and return to course"
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And I set the following fields to these values:
|
||||
| Name | Assignment 1 rubric |
|
||||
@@ -29,11 +31,10 @@ Feature: Reuse my rubrics in other activities
|
||||
| Criterion 2 | Level 21 | 21 | Level 22 | 22 | Level 3 | 23 |
|
||||
| Criterion 3 | Level 31 | 31 | Level 32 | 32 | | |
|
||||
And I press "Save rubric and make it ready"
|
||||
And I am on "Course 1" course homepage
|
||||
When I add a "Assignment" to section "1" and I fill the form with:
|
||||
| Assignment name | Test assignment 2 name |
|
||||
| Description | Test assignment 2 description |
|
||||
And I am on the "Test assignment 2 name" "assign activity editing" page
|
||||
And I set the following fields to these values:
|
||||
| Grading method | Rubric |
|
||||
And I press "Save and return to course"
|
||||
And I set "Test assignment 2 name" activity to use "Assignment 1 rubric" grading form
|
||||
Then I should see "Ready for use"
|
||||
And I should see "Criterion 1"
|
||||
@@ -44,7 +45,6 @@ Feature: Reuse my rubrics in other activities
|
||||
And I should see "Criterion 1"
|
||||
And I should see "Criterion 2"
|
||||
And I should see "Criterion 3"
|
||||
And I press "Cancel"
|
||||
|
||||
@javascript
|
||||
Scenario: A teacher can reuse one of his/her previously created rubrics, with Javascript enabled
|
||||
|
||||
@@ -28,16 +28,13 @@ Feature: We can change what we are viewing on the grader report
|
||||
| Description | Submit your online text |
|
||||
| assignsubmission_onlinetext_enabled | 1 |
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment name 1"
|
||||
And I am on the "Test assignment name 1" "assign activity" page logged in as student1
|
||||
When I press "Add submission"
|
||||
And I set the following fields to these values:
|
||||
| Online text | This is a submission for assignment 1 |
|
||||
And I press "Save changes"
|
||||
Then I should see "Submitted for grading"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment name 2"
|
||||
And I am on the "Test assignment name 2" "assign activity" page
|
||||
When I press "Add submission"
|
||||
And I set the following fields to these values:
|
||||
| Online text | This is a submission for assignment 2 |
|
||||
@@ -55,7 +52,7 @@ Feature: We can change what we are viewing on the grader report
|
||||
|
||||
@javascript
|
||||
Scenario: View and minimise the grader report containing hidden activities
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
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
|
||||
And I am on "Course 1" course homepage
|
||||
@@ -83,7 +80,7 @@ Feature: We can change what we are viewing on the grader report
|
||||
|
||||
@javascript
|
||||
Scenario: View and minimise the grader report containing hidden activities without the 'moodle/grade:viewhidden' capability
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
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
|
||||
And I log out
|
||||
|
||||
@@ -30,9 +30,7 @@ Feature: We can bulk insert grades for students in a course
|
||||
| assign | C1 | a4 | Test assignment four | Submit nothing! |
|
||||
|
||||
Scenario: I can bulk insert grades and check their override flags for grade view.
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment one"
|
||||
Given I am on the "Test assignment one" "assign activity" page logged in as teacher1
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the following fields to these values:
|
||||
@@ -71,9 +69,7 @@ Feature: We can bulk insert grades for students in a course
|
||||
And the field "Override for Student 4" matches value "1"
|
||||
|
||||
Scenario: I can bulk insert grades and check their override flags for user view.
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment two"
|
||||
Given I am on the "Test assignment two" "assign activity" page logged in as teacher1
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the following fields to these values:
|
||||
@@ -103,8 +99,7 @@ Feature: We can bulk insert grades for students in a course
|
||||
And the field "Override for Test assignment four" matches value "1"
|
||||
|
||||
Scenario: I can not update grades if the value is out of bounds.
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
Given I am on the "Course 1" course page logged in as teacher1
|
||||
And I navigate to "View > Grader report" in the course gradebook
|
||||
And I follow "Single view for Test assignment one"
|
||||
And I set the field "Perform bulk insert" to "1"
|
||||
|
||||
@@ -11,8 +11,8 @@ Feature: We can change the grading type and maximum grade point values
|
||||
| fullname | shortname | category | format |
|
||||
| Course 1 | C1 | 0 | topics |
|
||||
Given the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro |
|
||||
| assign | C1 | assign1| Test Assignment 1 | Test Assignment 1 |
|
||||
| activity | course | idnumber | name | intro |
|
||||
| assign | C1 | assign1 | Test Assignment 1 | Test Assignment 1 |
|
||||
And I log in as "admin"
|
||||
And I navigate to "Grades > General settings" in site administration
|
||||
And I set the following fields to these values:
|
||||
@@ -23,9 +23,8 @@ Feature: We can change the grading type and maximum grade point values
|
||||
|
||||
@javascript
|
||||
Scenario: Validate that switching the type of grading used correctly disables input form elements
|
||||
When I follow "Test Assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
Given I am on the "Test Assignment 1" "assign activity editing" page
|
||||
When I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "Point"
|
||||
Then the "Scale" "select" should be disabled
|
||||
And the "Maximum grade" "field" should be enabled
|
||||
@@ -39,46 +38,42 @@ Feature: We can change the grading type and maximum grade point values
|
||||
|
||||
@javascript
|
||||
Scenario: Create an activity with a maximum grade point value less than the system maximum
|
||||
When I follow "Test Assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
Given I am on the "Test Assignment 1" "assign activity editing" page
|
||||
When I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "point"
|
||||
And I set the field "grade[modgrade_point]" to "600"
|
||||
And I press "Save and display"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test Assignment 1" "assign activity editing" page
|
||||
Then the field "grade[modgrade_point]" matches value "600"
|
||||
And the "Scale" "select" should be disabled
|
||||
And I press "Save and return to course"
|
||||
|
||||
@javascript
|
||||
Scenario: Create an activity with a scale as the grade type
|
||||
When I follow "Test Assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
Given I am on the "Test Assignment 1" "assign activity editing" page
|
||||
When I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "Scale"
|
||||
And I set the field "grade[modgrade_scale]" to "Separate and Connected ways of knowing"
|
||||
And I press "Save and display"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test Assignment 1" "assign activity editing" page
|
||||
Then the field "grade[modgrade_scale]" matches value "Separate and Connected ways of knowing"
|
||||
And the "Maximum grade" "field" should be disabled
|
||||
And I press "Save and return to course"
|
||||
|
||||
@javascript
|
||||
Scenario: Create an activity with no grade as the grade type
|
||||
When I follow "Test Assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
Given I am on the "Test Assignment 1" "assign activity editing" page
|
||||
When I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "None"
|
||||
And I press "Save and display"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test Assignment 1" "assign activity editing" page
|
||||
And the "Scale" "select" should be disabled
|
||||
And the "Maximum grade" "field" should be disabled
|
||||
And I press "Save and return to course"
|
||||
|
||||
Scenario: Create an activity with a maximum grade point value higher than the system maximum
|
||||
When I follow "Test Assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
Given I am on the "Test Assignment 1" "assign activity editing" page
|
||||
When I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "Point"
|
||||
And I set the field "grade[modgrade_point]" to "20000"
|
||||
And I press "Save and display"
|
||||
@@ -86,9 +81,8 @@ Feature: We can change the grading type and maximum grade point values
|
||||
And I press "Cancel"
|
||||
|
||||
Scenario: Create an activity with a valid maximum grade point and then change the system maximum to be lower
|
||||
When I follow "Test Assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
Given I am on the "Test Assignment 1" "assign activity editing" page
|
||||
When I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "point"
|
||||
And I set the field "grade[modgrade_point]" to "600"
|
||||
And I press "Save and display"
|
||||
@@ -96,9 +90,7 @@ Feature: We can change the grading type and maximum grade point values
|
||||
And I set the following fields to these values:
|
||||
| Grade point maximum | 100 |
|
||||
And I press "Save changes"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test Assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test Assignment 1" "assign activity editing" page
|
||||
And I press "Save and display"
|
||||
Then I should see "Invalid grade value. This must be an integer between 1 and 100"
|
||||
And I press "Cancel"
|
||||
|
||||
@@ -23,8 +23,7 @@ Feature: Regrading grades does not unnecessarily mark some as overriden
|
||||
And I navigate to "Grades > Grade category settings" in site administration
|
||||
And I set the field "Available aggregation types" to "Weighted mean of grades"
|
||||
And I press "Save changes"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Assignment 1"
|
||||
And I am on the "Assignment 1" "assign activity" page
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Grade out of 100" to "80"
|
||||
@@ -59,8 +58,7 @@ Feature: Regrading grades does not unnecessarily mark some as overriden
|
||||
|
||||
@javascript
|
||||
Scenario: Confirm overridden course total does not get regraded when activity grade is changed
|
||||
Given I am on "Course 1" course homepage
|
||||
And I follow "Assignment 1"
|
||||
Given I am on the "Assignment 1" "assign activity" page
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Grade out of 100" to "90"
|
||||
|
||||
@@ -43,10 +43,7 @@ Feature: View gradebook when scales are used
|
||||
And the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro | gradecategory |
|
||||
| assign | C1 | a1 | Test assignment one | Submit something! | Sub category 1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment one"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test assignment one" "assign activity editing" page logged in as teacher1
|
||||
And I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "Scale"
|
||||
And I set the field "grade[modgrade_scale]" to "EN Letterscale"
|
||||
|
||||
@@ -39,10 +39,7 @@ Feature: View gradebook when single item scales are used
|
||||
| assign | C1 | a1 | Test assignment one | Submit something! | <span lang="en" class="multilang">EN</span><span lang="fr" class="multilang">FR</span> Sub category 1 |
|
||||
And the "multilang" filter is "on"
|
||||
And the "multilang" filter applies to "content and headings"
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment one"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test assignment one" "assign activity editing" page logged in as teacher1
|
||||
And I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "Scale"
|
||||
And I set the field "grade[modgrade_scale]" to "EN Singleitem"
|
||||
|
||||
@@ -29,5 +29,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['parentlanguage'] = '';
|
||||
$string['thisdirection'] = 'ltr';
|
||||
$string['thislanguage'] = 'Afrikaans';
|
||||
|
||||
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['clianswerno'] = 'لا';
|
||||
$string['cliansweryes'] = 'نعم';
|
||||
$string['cliincorrectvalueerror'] = 'خطأ، القيمة "{$a->value}" غير صحيحة من أجل "{$a->option}"';
|
||||
$string['cliincorrectvalueerror'] = 'خطأ، القيمة "{$a->value}" غير صحيحة لـ "{$a->option}"';
|
||||
$string['cliincorrectvalueretry'] = 'قيمة غير صحيحة، حاول مرة أخرى';
|
||||
$string['clitypevalue'] = 'اكتب قيمة';
|
||||
$string['clitypevaluedefault'] = 'اكتب قيمة أو اضغط زر الإدخال لاستخدام القيم الافتراضية ({$a})';
|
||||
|
||||
@@ -45,6 +45,6 @@ $string['invalidmd5'] = 'La variable de comprobación era incorrecta - inténtel
|
||||
$string['missingrequiredfield'] = 'Falta algún campo necesario';
|
||||
$string['remotedownloaderror'] = '<p>Falló la descarga del componente a su servidor. Se recomienda verificar los ajustes del proxy, extensión PHP cURL.</p>
|
||||
<p>Debe descargar el <a href="{$a->url}">{$a->url}</a> archivo manualmente, copiarlo en "{$a->dest}" en su servidor y descomprimirlo allí.</p>';
|
||||
$string['wrongdestpath'] = 'Ruta de destino errónea.';
|
||||
$string['wrongsourcebase'] = 'Base de fuente de URL errónea.';
|
||||
$string['wrongdestpath'] = 'Ruta de destino errónea';
|
||||
$string['wrongsourcebase'] = 'Base de URL de origen errónea';
|
||||
$string['wrongzipfilename'] = 'Nombre de fichero ZIP incorrecto';
|
||||
|
||||
@@ -85,20 +85,16 @@ $string['pathsunsecuredataroot'] = 'La ubicación de dataroot no es segura';
|
||||
$string['pathswrongadmindir'] = 'El directorio admin no existe';
|
||||
$string['phpextension'] = 'Extensión PHP {$a}';
|
||||
$string['phpversion'] = 'Versión PHP';
|
||||
$string['phpversionhelp'] = '<p>Moodle requiere al menos una versión de PHP 4.3.0 o 5.1.0 ((5.0.x tiene una serie de problemas conocidos).</p>
|
||||
<p>En este momento está ejecutando la versión {$a}</p>
|
||||
<p>¡Debe actualizar PHP o trasladarse a otro servidor con una versión más reciente de PHP!<br />
|
||||
(En caso de 5.0.x podría también revertir a la versión 4.4.x)</p>';
|
||||
$string['phpversionhelp'] = '<p>Moodle requiere una versión de PHP que sea al menos la 5.6.5 o 7.1 (7.0.x tiene algunas limitaciones del motor).</p>
|
||||
<p>En este momento está ejecutando la versión {$a}.</p>
|
||||
<p>Debe actualizar PHP o trasladarse a otro servidor con una versión más reciente de PHP.</p>';
|
||||
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
|
||||
$string['welcomep20'] = 'Si está viendo esta página es porque ha podido ejecutar el paquete <strong>{$a->packname} {$a->packversion}</strong> satisfactoriamente en su ordenador. ¡Enhorabuena!';
|
||||
$string['welcomep30'] = 'Esta versión de <strong>{$a->installername}</strong> incluye las
|
||||
aplicaciones necesarias para que <strong>Moodle</strong> funcione en su ordenador,
|
||||
principalmente:';
|
||||
$string['welcomep40'] = 'El paquete también incluye <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
|
||||
$string['welcomep50'] = 'El uso de todas las aplicaciones del paquete está gobernado por sus respectivas
|
||||
licencias. El programa <strong>{$a->installername}</strong> es
|
||||
<a href="http://www.opensource.org/docs/definition_plain.html">código abierto</a> y se distribuye
|
||||
bajo licencia <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>.';
|
||||
$string['welcomep50'] = 'El uso de todas las aplicaciones del paquete está supeditado a sus respectivas licencias. El programa <strong>{$a->installername}</strong> es <a href="http://www.opensource.org/docs/definition_plain.html">de código abierto</a> y se distribuye bajo licencia <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>.';
|
||||
$string['welcomep60'] = 'Las siguientes páginas le guiarán a través de algunos sencillos pasos para configurar y ajustar <strong>Moodle</strong> en su ordenador. Puede utilizar los valores por defecto sugeridos o, de forma opcional, modificarlos para que se ajusten a sus necesidades.';
|
||||
$string['welcomep70'] = 'Pulse en el botón "Siguiente" para continuar con la configuración de <strong>Moodle</strong>.';
|
||||
$string['wwwroot'] = 'Dirección Web';
|
||||
|
||||
@@ -39,3 +39,4 @@ $string['cliunknowoption'] = 'Tunnistamaton valinta: {$a} Ole hyvä ja käytä -
|
||||
$string['cliyesnoprompt'] = 'syötä k (kyllä) tai e (ei)';
|
||||
$string['environmentrequireinstall'] = 'pitää olla asennettuna ja käytössä';
|
||||
$string['environmentrequireversion'] = 'versio {$a->needed} vaaditaan ja käytössä on versio {$a->current}';
|
||||
$string['upgradekeyset'] = 'Päivitysavain (jätä tyhjäksi, jos et halua asettaa sitä)';
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
*
|
||||
* Do not edit this file manually! It contains just a subset of strings
|
||||
* needed during the very first steps of installation. This file was
|
||||
* generated automatically by export-installer.php (which is part of AMOS
|
||||
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
|
||||
* list of strings defined in /install/stringnames.txt.
|
||||
*
|
||||
* @package installer
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['thislanguage'] = 'Hornjoserbsski';
|
||||
@@ -38,7 +38,7 @@ $string['cannotfindcomponent'] = 'Nincs meg az összetevő.';
|
||||
$string['cannotsavemd5file'] = 'Az md5 állományt nem lehet elmenteni.';
|
||||
$string['cannotsavezipfile'] = 'A tömörített állományt nem lehet elmenteni.';
|
||||
$string['cannotunzipfile'] = 'A tömörített állományt nem lehet kicsomagolni.';
|
||||
$string['componentisuptodate'] = 'Az összetevő a legújabb.';
|
||||
$string['componentisuptodate'] = 'Az összetevő a legfrissebb.';
|
||||
$string['dmlexceptiononinstall'] = '<p>Adatbázishiba történt [{$a->errorcode}].<br />{$a->debuginfo}</p>';
|
||||
$string['downloadedfilecheckfailed'] = 'A letöltött állomány ellenőrzése nem sikerült.';
|
||||
$string['invalidmd5'] = 'Az ellenőrző változó hibás volt – próbálkozzék ismét';
|
||||
|
||||
@@ -33,7 +33,7 @@ $string['admindirname'] = 'Rendszergazda-könyvtár';
|
||||
$string['availablelangs'] = 'Elérhető nyelvek listája';
|
||||
$string['chooselanguagehead'] = 'Nyelv kiválasztása';
|
||||
$string['chooselanguagesub'] = 'Válasszon nyelvet a telepítéshez! Ez lesz a portál alapbeállítás szerinti nyelve, de később módosíthatja.';
|
||||
$string['clialreadyconfigured'] = 'A config.php már létezik, a portál telepítéséhez használja az admin/cli/install_database.php állományt.';
|
||||
$string['clialreadyconfigured'] = 'A config.php már létezik, a Moodle portálra telepítéséhez használja az admin/cli/install_database.php állományt.';
|
||||
$string['clialreadyinstalled'] = 'A config.php már létezik, a portál frissítéséhez használja az admin/cli/upgrade.php állományt.';
|
||||
$string['cliinstallheader'] = 'A Moodle {$a} parancssori telepítő programja';
|
||||
$string['databasehost'] = 'Az adatbázis gazdagépe';
|
||||
@@ -56,7 +56,9 @@ $string['pathshead'] = 'Útvonalak megerősítése';
|
||||
$string['pathsrodataroot'] = 'Az adatok gyökérkönyvtára nem írható.';
|
||||
$string['pathsroparentdataroot'] = 'A felettes könyvtár ({$a->parent}) nem írható. A telepítő nem tudja létrehozni az adatkönyvtárat ({$a->dataroot}).';
|
||||
$string['pathssubadmindir'] = 'Egy pár webes gazdagép esetén az /admin speciális URL pl. a vezérlőpanel eléréséhez. Ez ütközik a Moodle admin oldalainak standard helyével. Javítás: a telepítésben nevezze át a rendszergazda könyvtárát, az új nevet pedig írja be ide. Például: <em>moodleadmin</em>. Ezzel helyrehozhatók a Moodle rendszergazdai hivatkozásai.';
|
||||
$string['pathssubdataroot'] = 'Szüksége van egy helyre, ahol a Moodle mentheti a feltöltött állományokat. Ez a könyvtár a webszerver felhasználója (általában \'nobody\' vagy \'apache\') számára legyen mind olvasható, MIND ÍRHATÓ. Ha nem létezik, a telepítő megpróbálja létrehozni.';
|
||||
$string['pathssubdataroot'] = 'Szüksége van egy helyre, ahol a Moodle mentheti a feltöltött állományokat. Ez a könyvtár a webszerver felhasználója (általában \'www-data\', \'nobody\' vagy \'apache\') számára legyen mind olvasható, mind írható.
|
||||
A könyvtár a webről közvetlenül nem lehet elérhető.
|
||||
Ha nem létezik, a telepítő megpróbálja létrehozni.';
|
||||
$string['pathssubdirroot'] = 'Teljes útvonal a Moodle telepítéséhez. ';
|
||||
$string['pathssubwwwroot'] = 'A Moodle elérésére használandó teljes webcím. A Moodle egyszerre több címről nem érhető el. Ha portálja több címet használ, a jelen cím kivételével az összeshez állandó
|
||||
átirányítást kell beállítania. Ha portálja mind intranetről, mind az internetről elérhető, a nyilvános címet itt adja meg, a DNS-t pedig úgy állítsa be, hogy az intranetről a nyilvános cím is elérhető legyen. Ha a cím hibás, módosítsa böngészőjében a webcímet, hogy a telepítés egy másik értékkel induljon újra.';
|
||||
|
||||
@@ -29,14 +29,16 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['clianswerno'] = 'T';
|
||||
$string['cliansweryes'] = 'Y';
|
||||
$string['cliincorrectvalueerror'] = 'Error, nilai yang salah "{$a->value}" untuk opsi "{$a->option}"';
|
||||
$string['cliincorrectvalueretry'] = 'Nilai input salah, silahkan coba lagi';
|
||||
$string['clianswerno'] = 't';
|
||||
$string['cliansweryes'] = 'y';
|
||||
$string['cliincorrectvalueerror'] = 'GALAT, nilai yang salah "{$a->value}" untuk opsi "{$a->option}"';
|
||||
$string['cliincorrectvalueretry'] = 'Nilai masukan salah, silakan coba lagi';
|
||||
$string['clitypevalue'] = 'tipe nilai';
|
||||
$string['clitypevaluedefault'] = 'tipe nilai, tekan enter untuk menggunakan nilai default ({$a})';
|
||||
$string['clitypevaluedefault'] = 'tipe nilai, tekan ENTER untuk menggunakan nilai bawaan ({$a})';
|
||||
$string['cliunknowoption'] = 'Opsi tidak dikenali :
|
||||
{$a}
|
||||
Silahkan gunakan opsi --help';
|
||||
Silakan gunakan opsi --help';
|
||||
$string['cliyesnoprompt'] = 'ketik y (Ya) atau t (Tidak)';
|
||||
$string['environmentrequireinstall'] = 'harus dipasang dan diaktifkan';
|
||||
$string['environmentrequireversion'] = 'versi {$a->needed} diperlukan dan Anda menjalankan {$a->current}';
|
||||
$string['upgradekeyset'] = 'Tingkatkan kunci (biarkan kosong untuk tidak mengaturnya)';
|
||||
|
||||
@@ -65,12 +65,12 @@ $string['pathsunsecuredataroot'] = 'Lokasi dataroot tidak aman';
|
||||
$string['pathswrongadmindir'] = 'Direktori admin tidak ada';
|
||||
$string['phpextension'] = 'Ekstensi PHP {$a}';
|
||||
$string['phpversion'] = 'Versi PHP';
|
||||
$string['phpversionhelp'] = '<p> Moodle membutuhkan versi PHP setidaknya 5.6.5 atau 7.1 (7.0.x memiliki beberapa keterbatasan mesin). </p> <p> Anda saat ini menjalankan versi {$a}. </p> <p> Anda harus meningkatkan versi PHP atau pindah ke host dengan versi PHP yang lebih baru. </p>';
|
||||
$string['phpversionhelp'] = '<p> Moodle membutuhkan versi PHP setidaknya 5.6.5 atau 7.1 (7.0.x memiliki beberapa keterbatasan mesin). </p> <p> Anda saat ini menjalankan versi {$a}. </p> <p> Anda harus meningkatkan versi PHP atau pindah ke hos dengan versi PHP yang lebih baru. </p>';
|
||||
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
|
||||
$string['welcomep20'] = 'Anda melihat halaman ini karena Anda telah berhasil memasang dan meluncurkan paket <strong> {$a->packname} {$a->packversion}</strong> di komputer Anda. Selamat!';
|
||||
$string['welcomep30'] = 'Rilis <strong> {$a->installername}</strong> ini mencakup aplikasi untuk menciptakan lingkungan tempat <strong> Moodle </strong> yang akan digunakan, yaitu:';
|
||||
$string['welcomep40'] = 'Paket juga termasuk <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
|
||||
$string['welcomep50'] = 'Penggunaan semua aplikasi dalam paket ini diatur oleh lisensi masing-masing. Paket <strong>{$a->installername}</strong> lengkap adalah <a href="http://www.opensource.org/docs/definition_plain.html"> sumber terbuka </a> dan didistribusikan di bawah lisensi <a href="http://www.gnu.org/copyleft/gpl.html"> GPL </a>.';
|
||||
$string['welcomep50'] = 'Penggunaan semua aplikasi dalam paket ini diatur oleh lisensi masing-masing. Paket lengkap <strong>{$a->installername}</strong> adalah <a href="https://www.opensource.org/docs/definition_plain.html"> open source </a> dan didistribusikan di bawah lisensi <a href="https://www.gnu.org/copyleft/gpl.html">GPL</a>.';
|
||||
$string['welcomep60'] = 'Halaman berikut akan menuntun Anda melalui beberapa langkah yang mudah diikuti untuk mengonfigurasi dan menyiapkan <strong> Moodle </strong> di komputer Anda. Anda dapat menerima pengaturan bawaan atau, secara opsional, mengubahnya sesuai dengan kebutuhan Anda.';
|
||||
$string['welcomep70'] = 'Klik tombol "Selanjutnya" di bawah untuk melanjutkan instalasi <strong> Moodle </strong>.';
|
||||
$string['wwwroot'] = 'Alamat web';
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
*
|
||||
* Do not edit this file manually! It contains just a subset of strings
|
||||
* needed during the very first steps of installation. This file was
|
||||
* generated automatically by export-installer.php (which is part of AMOS
|
||||
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
|
||||
* list of strings defined in /install/stringnames.txt.
|
||||
*
|
||||
* @package installer
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['clitypevalue'] = 'တန်ဖိုး ထည့်သွင်းပါ';
|
||||
$string['clitypevaluedefault'] = 'တန်ဖိုး ထည့်သွင်းပါ၊ ပုံသေတန်ဖိုး ({$a}) ကိုအသုံးပြုလိုပါက ENTER ကိုနှိပ်ပါ';
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
*
|
||||
* Do not edit this file manually! It contains just a subset of strings
|
||||
* needed during the very first steps of installation. This file was
|
||||
* generated automatically by export-installer.php (which is part of AMOS
|
||||
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
|
||||
* list of strings defined in /install/stringnames.txt.
|
||||
*
|
||||
* @package installer
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['cannotcreatetempdir'] = 'temp directory ကို မတည်ဆောက်နိုင်ပါ၊၊';
|
||||
$string['cannotdownloadcomponents'] = 'အစိတ်အပိုင်းများကို ဒေါင်းလုတ်ဆွဲမရပါ။';
|
||||
$string['cannotdownloadzipfile'] = 'ZIP ဖိုင်ကိုဒေါင်းလုတ်ဆွဲမရပါ။';
|
||||
$string['cannotfindcomponent'] = 'အစိတ်အပိုင်းကို ရှာဖွေမတွေ့ရှိပါ။';
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
*
|
||||
* Do not edit this file manually! It contains just a subset of strings
|
||||
* needed during the very first steps of installation. This file was
|
||||
* generated automatically by export-installer.php (which is part of AMOS
|
||||
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
|
||||
* list of strings defined in /install/stringnames.txt.
|
||||
*
|
||||
* @package installer
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['language'] = 'ဘာသာစကား';
|
||||
$string['moodlelogo'] = 'Moodle Logo';
|
||||
$string['next'] = 'နောက်သို့';
|
||||
@@ -37,6 +37,6 @@ $string['clitypevalue'] = 'introduza valor';
|
||||
$string['clitypevaluedefault'] = 'introduza valor, pressione a tecla \'Enter\' para usar o valor predefinido ({$a})';
|
||||
$string['cliunknowoption'] = 'Opções desconhecidas: {$a}. Por favor, use a opção --help';
|
||||
$string['cliyesnoprompt'] = 'digite s (para sim) ou n (para não)';
|
||||
$string['environmentrequireinstall'] = 'deve estar instalada e ativa';
|
||||
$string['environmentrequireversion'] = 'é requerida a versão {$a->needed} e está a correr a versão {$a->current}';
|
||||
$string['upgradekeyset'] = 'Chave de atualização (deixe em branco caso não pretenda definir uma)';
|
||||
$string['environmentrequireinstall'] = 'tem de estar instalada e ativa';
|
||||
$string['environmentrequireversion'] = 'é requerida a versão {$a->needed} e está a usar a versão {$a->current}';
|
||||
$string['upgradekeyset'] = 'Chave de atualização (deixe em branco caso não pretenda definir)';
|
||||
|
||||
@@ -54,7 +54,7 @@ $string['pathserrcreatedataroot'] = 'O programa de instalação não conseguiu c
|
||||
$string['pathshead'] = 'Confirmar caminhos';
|
||||
$string['pathsrodataroot'] = 'A pasta de dados não tem permissões de escrita.';
|
||||
$string['pathsroparentdataroot'] = 'A pasta ascendente <b>{$a->parent}</b> não tem permissões de escrita. O programa de instalação não conseguiu criar a pasta <b>{$a->dataroot}</b>.';
|
||||
$string['pathssubadmindir'] = 'Alguns servidores Web utilizam a pasta <strong>admin</strong> em URLs especiais de acesso a funcionalidades especiais, como é o caso de painéis de controlo. Algumas situações podem criar conflitos com a localização normal das páginas de administração do Moodle. Estes problemas podem ser resolvidos renomeando a pasta <strong>admin</strong> na instalação do Moodle e indicando aqui o novo nome a utilizar. Por exemplo:<br /><br /><b>moodleadmin</b><br /><br />Esta ação resolverá os problemas de acesso das hiperligações para as funcionalidades de administração do Moodle.';
|
||||
$string['pathssubadmindir'] = 'Alguns servidores Web utilizam a pasta <strong>admin</strong> em URLs especiais de acesso a funcionalidades especiais, como é o caso de painéis de controlo. Algumas situações podem criar conflitos com a localização normal das páginas de administração do Moodle. Estes problemas podem ser resolvidos renomeando a pasta <strong>admin</strong> na instalação do Moodle e indicando aqui o novo nome a utilizar. Exemplo:<br /><br /><b>moodleadmin</b><br /><br />Esta ação resolverá os problemas de acesso das hiperligações para as funcionalidades de administração do Moodle.';
|
||||
$string['pathssubdataroot'] = '<p>Pasta onde o Moodle irá armazenar todo o conteúdo de ficheiros enviados pelos utilizadores.</p>
|
||||
<p>Esta pasta deve ser legível e gravável pelo utilizador do servidor web (geralmente \'www-data\', \'nobody\', ou \'apache\').</p>
|
||||
<p>Não deve ser acessível diretamente através da web.</p>
|
||||
|
||||
@@ -33,6 +33,7 @@ $string['admindirname'] = 'Skrbniška mapa';
|
||||
$string['availablelangs'] = 'Jezikovni paketi na voljo';
|
||||
$string['chooselanguagehead'] = 'Izberite jezik';
|
||||
$string['chooselanguagesub'] = 'Izberite jezik za namestitev. Pozneje boste lahko izbrali tudi jezike strani in uporabniške jezike.';
|
||||
$string['clialreadyconfigured'] = 'Konfiguracijska datoteka config.php že obstaja. Uporabite admin/cli/install_database.php, če želite namestiti Moodle na to spletno mesto.';
|
||||
$string['clialreadyinstalled'] = 'Datoteka config.php že obstaja, prosimo uporabite admin/cli/install_database.php, če želite posodobiti vašo stran.';
|
||||
$string['cliinstallheader'] = 'Moodle {$a} namestitveni program z ukazno vrstico';
|
||||
$string['databasehost'] = 'Gostitelj podatkovne baze';
|
||||
|
||||
@@ -48,4 +48,4 @@ $string['missingrequiredfield'] = 'Det saknas några obligatoriska fält';
|
||||
$string['remotedownloaderror'] = 'Nedladdningen av en komponent till din server misslyckades. Vänligen verifiera inställningar för proxy. <br /><br />Du måste ladda ner <a href="{$a->url}">{$a->url}</a> filen manuellt, kopiera den till "{$a->dest}" på din server och packa upp den där.';
|
||||
$string['wrongdestpath'] = 'Fel målsökväg';
|
||||
$string['wrongsourcebase'] = 'Felaktig käll- URL rot';
|
||||
$string['wrongzipfilename'] = 'Fel namn på ZIP-filen';
|
||||
$string['wrongzipfilename'] = 'Felaktigt namn på ZIP-fil';
|
||||
|
||||
@@ -34,7 +34,7 @@ $string['availablelangs'] = 'Tillgängliga språkpaket';
|
||||
$string['chooselanguagehead'] = 'Välj ett språk';
|
||||
$string['chooselanguagesub'] = 'Vänligen välj ett språk för installationen. Du kommer att ha möjlighet att välja språk för webbplatsen och användarna på en senare skärm.';
|
||||
$string['clialreadyconfigured'] = 'Filen <em>config.php</em> finns redan. Använd <code>admin/cli/install_database.php</code> för att installera Moodle på denna server.';
|
||||
$string['clialreadyinstalled'] = 'Filen config.php finns redan. Vänligen använd admin/cli/upgrade.php om Du vill uppgradera Din webbplats.';
|
||||
$string['clialreadyinstalled'] = 'Filen <code>config.php</code> finns redan. Vänligen använd <code>admin/cli/upgrade.php</code> om du vill uppgradera Moodle på den här webbplatsen.';
|
||||
$string['cliinstallheader'] = 'Kommandoradsbaserat installationsprogram för Moodle {$a}';
|
||||
$string['databasehost'] = 'Databasserver';
|
||||
$string['databasename'] = 'Namn på databas';
|
||||
|
||||
@@ -30,3 +30,6 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['language'] = 'زبان';
|
||||
$string['next'] = 'اگلے';
|
||||
$string['previous'] = 'پچھلا';
|
||||
$string['reload'] = 'دوبارہ لوڈ کریں';
|
||||
|
||||
@@ -659,6 +659,7 @@ $string['checkboxyes'] = 'Yes';
|
||||
$string['choosefiletoedit'] = 'Choose file to edit';
|
||||
$string['h5pgetcontenttypestask'] = 'Download available H5P content types from h5p.org';
|
||||
$string['iconvrequired'] = 'Installing ICONV extension is required.';
|
||||
$string['igbinary322buggyversion'] = 'The php-igbinary extension installed on the site can lead to problems when running with PHP 7.2. You are recommended to either upgrade to php-igbinary 3.2.5 or later, or alternatively to upgrade to PHP 7.3 or later.';
|
||||
$string['ignore'] = 'Ignore';
|
||||
$string['includemoduleuserdata'] = 'Include module user data';
|
||||
$string['incompatibleblocks'] = 'Incompatible blocks';
|
||||
|
||||
+1
-1
@@ -604,7 +604,7 @@ $string['emailalreadysent'] = 'A password reset email has already been sent. Ple
|
||||
$string['emailactive'] = 'Email activated';
|
||||
$string['emailagain'] = 'Email (again)';
|
||||
$string['emailconfirm'] = 'Confirm your account';
|
||||
$string['emailconfirmation'] = 'Hi {$a->firstname},
|
||||
$string['emailconfirmation'] = 'Hi,
|
||||
|
||||
A new account has been requested at \'{$a->sitename}\'
|
||||
using your email address.
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
define ("core/paged_content_pages",["jquery","core/templates","core/notification","core/pubsub","core/paged_content_events"],function(a,b,c,d,e){var f={ROOT:"[data-region=\"page-container\"]",PAGE_REGION:"[data-region=\"paged-content-page\"]",ACTIVE_PAGE_REGION:"[data-region=\"paged-content-page\"].active"},g={PAGING_CONTENT_ITEM:"core/paged_content_page",LOADING:"core/overlay_loading"},h=function(a,b){return a.find("[data-page=\""+b+"\"]")},i=function(d){var e=a.Deferred();d.attr("aria-busy",!0);b.render(g.LOADING,{visible:!0}).then(function(b){var c=a(b),f=setTimeout(function(){d.css("position","relative");c.appendTo(d)},300);e.always(function(){clearTimeout(f);c.remove();d.css("position","");d.removeAttr("aria-busy")})}).fail(c.exception);return e},j=function(d,e,f){var i=a.Deferred();e.then(function(a,e){e=e||"";b.render(g.PAGING_CONTENT_ITEM,{page:f,content:a}).then(function(a){b.appendNodeContents(d,a,e);var c=h(d,f);i.resolve(c)}).fail(function(a){i.reject(a)}).fail(c.exception)}).fail(function(a){i.reject(a)}).fail(c.exception);return i.promise()},k=function(b,g,k,l){var m=[],n=[],o=a.Deferred();g.forEach(function(a){var c=a.pageNumber,d=h(b,c);if(d.length){m.push(d)}else{n.push(a)}});if(n.length&&"function"==typeof l){var p=l(n,{allItemsLoaded:function allItemsLoaded(a){d.publish(k+e.ALL_ITEMS_LOADED,a)}}),q=p.map(function(a,c){return j(b,a,n[c].pageNumber)});a.when.apply(a,q).then(function(){var a=Array.prototype.slice.call(arguments);o.resolve(a)}).fail(function(a){o.reject(a)}).fail(c.exception)}else{o.resolve([])}var r=i(b);o.then(function(a){var c=m.concat(a);b.find(f.PAGE_REGION).addClass("hidden");c.forEach(function(a){if(!0){a.removeClass("hidden")}})}).then(function(){d.publish(k+e.PAGES_SHOWN,g)}).fail(c.exception).always(function(){r.resolve()})};return{init:function init(b,c,f){b=a(b);d.subscribe(c+e.SHOW_PAGES,function(a){k(b,a,c,f)});d.subscribe(c+e.SET_ITEMS_PER_PAGE_LIMIT,function(){b.empty()})},rootSelector:f.ROOT}});
|
||||
define ("core/paged_content_pages",["jquery","core/templates","core/notification","core/pubsub","core/paged_content_events","core/pending"],function(a,b,c,d,e,f){var g={ROOT:"[data-region=\"page-container\"]",PAGE_REGION:"[data-region=\"paged-content-page\"]",ACTIVE_PAGE_REGION:"[data-region=\"paged-content-page\"].active"},h={PAGING_CONTENT_ITEM:"core/paged_content_page",LOADING:"core/overlay_loading"},i=function(a,b){return a.find("[data-page=\""+b+"\"]")},j=function(d){var e=a.Deferred();d.attr("aria-busy",!0);var g=new f("core/paged_content_pages:startLoading");b.render(h.LOADING,{visible:!0}).then(function(b){var c=a(b),f=setTimeout(function(){d.css("position","relative");c.appendTo(d)},300);e.always(function(){clearTimeout(f);c.remove();d.css("position","");d.removeAttr("aria-busy");g.resolve()})}).fail(c.exception);return e},k=function(d,e,f){var g=a.Deferred();e.then(function(a,e){e=e||"";b.render(h.PAGING_CONTENT_ITEM,{page:f,content:a}).then(function(a){b.appendNodeContents(d,a,e);var c=i(d,f);g.resolve(c)}).fail(function(a){g.reject(a)}).fail(c.exception)}).fail(function(a){g.reject(a)}).fail(c.exception);return g.promise()},l=function(b,h,l,m){var n=new f("core/paged_content_pages:showPages"),o=[],p=[],q=a.Deferred();h.forEach(function(a){var c=a.pageNumber,d=i(b,c);if(d.length){o.push(d)}else{p.push(a)}});if(p.length&&"function"==typeof m){var r=m(p,{allItemsLoaded:function allItemsLoaded(a){d.publish(l+e.ALL_ITEMS_LOADED,a)}}),s=r.map(function(a,c){return k(b,a,p[c].pageNumber)});a.when.apply(a,s).then(function(){var a=Array.prototype.slice.call(arguments);q.resolve(a)}).fail(function(a){q.reject(a)}).fail(c.exception)}else{q.resolve([])}var t=j(b);q.then(function(a){var c=o.concat(a);b.find(g.PAGE_REGION).addClass("hidden");c.forEach(function(a){if(!0){a.removeClass("hidden")}})}).then(function(){d.publish(l+e.PAGES_SHOWN,h)}).fail(c.exception).always(function(){t.resolve();n.resolve()}).catch()};return{init:function init(b,c,f){b=a(b);d.subscribe(c+e.SHOW_PAGES,function(a){l(b,a,c,f)});d.subscribe(c+e.SET_ITEMS_PER_PAGE_LIMIT,function(){b.empty()})},rootSelector:g.ROOT}});
|
||||
//# sourceMappingURL=paged_content_pages.min.js.map
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,2 +1,2 @@
|
||||
define ("core/paged_content_paging_bar",["jquery","core/custom_interaction_events","core/paged_content_events","core/str","core/pubsub"],function(a,b,c,d,e){var f={ROOT:"[data-region=\"paging-bar\"]",PAGE:"[data-page]",PAGE_ITEM:"[data-region=\"page-item\"]",PAGE_LINK:"[data-region=\"page-link\"]",FIRST_BUTTON:"[data-control=\"first\"]",LAST_BUTTON:"[data-control=\"last\"]",NEXT_BUTTON:"[data-control=\"next\"]",PREVIOUS_BUTTON:"[data-control=\"previous\"]",DOTS_BUTTONS:"[data-dots]",BEGINNING_DOTS_BUTTON:"[data-dots=\"beginning\"]",ENDING_DOTS_BUTTON:"[data-dots=\"ending\"]"},g=function(a,b){return a.find(f.PAGE_ITEM+"[data-page-number=\""+b+"\"]")},h=function(a){return a.find(f.NEXT_BUTTON)},i=function(a,b){a.attr("data-last-page-number",b)},j=function(a){return parseInt(a.attr("data-last-page-number"),10)},k=function(a){return parseInt(a.attr("data-active-page-number"),10)},l=function(a,b){a.attr("data-active-page-number",b)},m=function(a){var b=k(a);return!isNaN(b)&&0!=b},n=function(a,b){if(b.attr("data-page")!=void 0){return parseInt(b.attr("data-page-number"),10)}var c=1,d=null;switch(b.attr("data-control")){case"first":c=1;break;case"last":c=j(a);break;case"next":d=k(a);var e=j(a);if(!e){c=d+1}else if(d&&d<e){c=d+1}else{c=e}break;case"previous":d=k(a);if(d&&1<d){c=d-1}else{c=1}break;default:c=1;break;}return parseInt(c,10)},o=function(a){return parseInt(a.attr("data-items-per-page"),10)},p=function(a,b){a.attr("data-items-per-page",b)},q=function(a){a.removeClass("hidden")},r=function(a){a.addClass("hidden")},s=function(a){var b=a.find(f.NEXT_BUTTON),c=a.find(f.LAST_BUTTON);b.addClass("disabled");b.attr("aria-disabled",!0);c.addClass("disabled");c.attr("aria-disabled",!0)},t=function(a){var b=a.find(f.NEXT_BUTTON),c=a.find(f.LAST_BUTTON);b.removeClass("disabled");b.removeAttr("aria-disabled");c.removeClass("disabled");c.removeAttr("aria-disabled")},u=function(a){var b=a.find(f.PREVIOUS_BUTTON),c=a.find(f.FIRST_BUTTON);b.addClass("disabled");b.attr("aria-disabled",!0);c.addClass("disabled");c.attr("aria-disabled",!0)},v=function(b){var c=k(b),d=j(b),e=b.find(f.DOTS_BUTTONS),g=b.find(f.BEGINNING_DOTS_BUTTON),h=b.find(f.ENDING_DOTS_BUTTON),i=b.find(f.PAGE),l=parseInt(b.attr("data-bar-size"),10);if(l&&d>l){var m=Math.max(c-Math.round(l/2),1),n=m+l-1;if(n>=d){n=d;m=n-l+1}if(1<m){q(g);m++}else{r(g)}if(n<d){q(h);n--}else{r(h)}e.addClass("disabled");e.attr("aria-disabled",!0);r(i);i.each(function(b,c){c=a(c);if(b+1>=m&&b+1<=n){q(c)}})}else{r(e)}},w=function(a){var b=a.find(f.PREVIOUS_BUTTON),c=a.find(f.FIRST_BUTTON);b.removeClass("disabled");b.removeAttr("aria-disabled");c.removeClass("disabled");c.removeAttr("aria-disabled")},x=function(a){var b=a.attr("data-aria-label-components-pagination-item"),c=b.split(",").map(function(a){return a.trim()});return c},y=function(a){var b=a.attr("data-aria-label-components-pagination-active-item"),c=b.split(",").map(function(a){return a.trim()});return c},z=function(b,c){var d=0;l(b,0);c.each(function(c,e){var f=c+1;e=a(e);e.attr("data-page-number",f);d++;if(e.hasClass("active")){l(b,f)}});i(b,d)},A=function(b){var c=x(b),e=y(b),g=k(b),h=b.find(f.PAGE_ITEM),i=h.map(function(d,f){f=a(f);var h=n(b,f);if(h===g){return{key:e[0],component:e[1],param:h}}else{return{key:c[0],component:c[1],param:h}}});d.get_strings(i).then(function(b){h.each(function(c,d){d=a(d);var e=b[c];d.attr("aria-label",e);d.find(f.PAGE_LINK).attr("aria-label",e)});return b}).catch(function(){})},B=function(a,b,d){var h=j(a),i=b==k(a),m=o(a);if(!i){a.find(f.PAGE_ITEM).removeClass("active").removeAttr("aria-current");var n=g(a,b);n.addClass("active");n.attr("aria-current",!0);l(a,b);v(a)}if(h&&b>=h){s(a)}else{t(a)}if(1<b){w(a)}else{u(a)}A(a);e.publish(d+c.SHOW_PAGES,[{pageNumber:b,limit:m,offset:(b-1)*m}])},C=function(d,g){var h=d.attr("data-ignore-control-while-loading"),k=!1;if(""==h){h=!0}b.define(d,[b.events.activate]);d.on(b.events.activate,f.PAGE_ITEM,function(b,c){c.originalEvent.preventDefault();c.originalEvent.stopPropagation();if(h&&k){return}var e=a(b.target).closest(f.PAGE_ITEM);if(!e.hasClass("disabled")){var i=n(d,e);B(d,i,g);k=!0}});e.subscribe(g+c.ALL_ITEMS_LOADED,function(a){k=!1;var b=j(d);if(!b||a<b){i(d,a)}if(1===a&&d.attr("data-hide-control-on-single-page")){r(d);s(d);u(d)}else{q(d);s(d)}});e.subscribe(g+c.PAGES_SHOWN,function(){k=!1});e.subscribe(g+c.SET_ITEMS_PER_PAGE_LIMIT,function(a){p(d,a);i(d,0);l(d,0);q(d);B(d,1,g)})};return{init:function init(b,c){b=a(b);var d=b.find(f.PAGE);z(b,d);C(b,c);if(m(b)){var e=k(b);g(b,e).click();if(1==e){u(b)}}else{h(b).click()}v(b)},disableNextControlButtons:s,enableNextControlButtons:t,disablePreviousControlButtons:u,enablePreviousControlButtons:w,showPage:B,rootSelector:f.ROOT}});
|
||||
define ("core/paged_content_paging_bar",["jquery","core/custom_interaction_events","core/paged_content_events","core/str","core/pubsub","core/pending"],function(a,b,c,d,e,f){var g={ROOT:"[data-region=\"paging-bar\"]",PAGE:"[data-page]",PAGE_ITEM:"[data-region=\"page-item\"]",PAGE_LINK:"[data-region=\"page-link\"]",FIRST_BUTTON:"[data-control=\"first\"]",LAST_BUTTON:"[data-control=\"last\"]",NEXT_BUTTON:"[data-control=\"next\"]",PREVIOUS_BUTTON:"[data-control=\"previous\"]",DOTS_BUTTONS:"[data-dots]",BEGINNING_DOTS_BUTTON:"[data-dots=\"beginning\"]",ENDING_DOTS_BUTTON:"[data-dots=\"ending\"]"},h=function(a,b){return a.find(g.PAGE_ITEM+"[data-page-number=\""+b+"\"]")},i=function(a){return a.find(g.NEXT_BUTTON)},j=function(a,b){a.attr("data-last-page-number",b)},k=function(a){return parseInt(a.attr("data-last-page-number"),10)},l=function(a){return parseInt(a.attr("data-active-page-number"),10)},m=function(a,b){a.attr("data-active-page-number",b)},n=function(a){var b=l(a);return!isNaN(b)&&0!=b},o=function(a,b){if(b.attr("data-page")!=void 0){return parseInt(b.attr("data-page-number"),10)}var c=1,d=null;switch(b.attr("data-control")){case"first":c=1;break;case"last":c=k(a);break;case"next":d=l(a);var e=k(a);if(!e){c=d+1}else if(d&&d<e){c=d+1}else{c=e}break;case"previous":d=l(a);if(d&&1<d){c=d-1}else{c=1}break;default:c=1;break;}return parseInt(c,10)},p=function(a){return parseInt(a.attr("data-items-per-page"),10)},q=function(a,b){a.attr("data-items-per-page",b)},r=function(a){a.removeClass("hidden")},s=function(a){a.addClass("hidden")},t=function(a){var b=a.find(g.NEXT_BUTTON),c=a.find(g.LAST_BUTTON);b.addClass("disabled");b.attr("aria-disabled",!0);c.addClass("disabled");c.attr("aria-disabled",!0)},u=function(a){var b=a.find(g.NEXT_BUTTON),c=a.find(g.LAST_BUTTON);b.removeClass("disabled");b.removeAttr("aria-disabled");c.removeClass("disabled");c.removeAttr("aria-disabled")},v=function(a){var b=a.find(g.PREVIOUS_BUTTON),c=a.find(g.FIRST_BUTTON);b.addClass("disabled");b.attr("aria-disabled",!0);c.addClass("disabled");c.attr("aria-disabled",!0)},w=function(b){var c=l(b),d=k(b),e=b.find(g.DOTS_BUTTONS),f=b.find(g.BEGINNING_DOTS_BUTTON),h=b.find(g.ENDING_DOTS_BUTTON),i=b.find(g.PAGE),j=parseInt(b.attr("data-bar-size"),10);if(j&&d>j){var m=Math.max(c-Math.round(j/2),1),n=m+j-1;if(n>=d){n=d;m=n-j+1}if(1<m){r(f);m++}else{s(f)}if(n<d){r(h);n--}else{s(h)}e.addClass("disabled");e.attr("aria-disabled",!0);s(i);i.each(function(b,c){c=a(c);if(b+1>=m&&b+1<=n){r(c)}})}else{s(e)}},x=function(a){var b=a.find(g.PREVIOUS_BUTTON),c=a.find(g.FIRST_BUTTON);b.removeClass("disabled");b.removeAttr("aria-disabled");c.removeClass("disabled");c.removeAttr("aria-disabled")},y=function(a){var b=a.attr("data-aria-label-components-pagination-item"),c=b.split(",").map(function(a){return a.trim()});return c},z=function(a){var b=a.attr("data-aria-label-components-pagination-active-item"),c=b.split(",").map(function(a){return a.trim()});return c},A=function(b,c){var d=0;m(b,0);c.each(function(c,e){var f=c+1;e=a(e);e.attr("data-page-number",f);d++;if(e.hasClass("active")){m(b,f)}});j(b,d)},B=function(b){var c=y(b),e=z(b),f=l(b),h=b.find(g.PAGE_ITEM),i=h.map(function(d,g){g=a(g);var h=o(b,g);if(h===f){return{key:e[0],component:e[1],param:h}}else{return{key:c[0],component:c[1],param:h}}});d.get_strings(i).then(function(b){h.each(function(c,d){d=a(d);var e=b[c];d.attr("aria-label",e);d.find(g.PAGE_LINK).attr("aria-label",e)});return b}).catch(function(){})},C=function(a,b,d){var i=new f("core/paged_content_paging_bar:showPage"),j=k(a),n=b==l(a),o=p(a);if(!n){a.find(g.PAGE_ITEM).removeClass("active").removeAttr("aria-current");var q=h(a,b);q.addClass("active");q.attr("aria-current",!0);m(a,b);w(a)}if(j&&b>=j){t(a)}else{u(a)}if(1<b){x(a)}else{v(a)}B(a);e.publish(d+c.SHOW_PAGES,[{pageNumber:b,limit:o,offset:(b-1)*o}]);i.resolve()},D=function(d,f){var h=d.attr("data-ignore-control-while-loading"),i=!1;if(""==h){h=!0}b.define(d,[b.events.activate]);d.on(b.events.activate,g.PAGE_ITEM,function(b,c){c.originalEvent.preventDefault();c.originalEvent.stopPropagation();if(h&&i){return}var e=a(b.target).closest(g.PAGE_ITEM);if(!e.hasClass("disabled")){var j=o(d,e);C(d,j,f);i=!0}});e.subscribe(f+c.ALL_ITEMS_LOADED,function(a){i=!1;var b=k(d);if(!b||a<b){j(d,a)}if(1===a&&d.attr("data-hide-control-on-single-page")){s(d);t(d);v(d)}else{r(d);t(d)}});e.subscribe(f+c.PAGES_SHOWN,function(){i=!1});e.subscribe(f+c.SET_ITEMS_PER_PAGE_LIMIT,function(a){q(d,a);j(d,0);m(d,0);r(d);C(d,1,f)})};return{init:function init(b,c){b=a(b);var d=b.find(g.PAGE);A(b,d);D(b,c);if(n(b)){var e=l(b);h(b,e).click();if(1==e){v(b)}}else{i(b).click()}w(b)},disableNextControlButtons:t,enableNextControlButtons:u,disablePreviousControlButtons:v,enablePreviousControlButtons:x,showPage:C,rootSelector:g.ROOT}});
|
||||
//# sourceMappingURL=paged_content_paging_bar.min.js.map
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -26,14 +26,16 @@ define(
|
||||
'core/templates',
|
||||
'core/notification',
|
||||
'core/pubsub',
|
||||
'core/paged_content_events'
|
||||
'core/paged_content_events',
|
||||
'core/pending',
|
||||
],
|
||||
function(
|
||||
$,
|
||||
Templates,
|
||||
Notification,
|
||||
PubSub,
|
||||
PagedContentEvents
|
||||
PagedContentEvents,
|
||||
Pending
|
||||
) {
|
||||
|
||||
var SELECTORS = {
|
||||
@@ -74,6 +76,8 @@ define(
|
||||
var deferred = $.Deferred();
|
||||
root.attr('aria-busy', true);
|
||||
|
||||
var pendingPromise = new Pending('core/paged_content_pages:startLoading');
|
||||
|
||||
Templates.render(TEMPLATES.LOADING, {visible: true})
|
||||
.then(function(html) {
|
||||
var loadingSpinner = $(html);
|
||||
@@ -93,6 +97,8 @@ define(
|
||||
loadingSpinner.remove();
|
||||
root.css('position', '');
|
||||
root.removeAttr('aria-busy');
|
||||
|
||||
pendingPromise.resolve();
|
||||
return;
|
||||
});
|
||||
|
||||
@@ -182,6 +188,7 @@ define(
|
||||
* @param {function} renderPagesContentCallback Render pages content.
|
||||
*/
|
||||
var showPages = function(root, pagesData, id, renderPagesContentCallback) {
|
||||
var pendingPromise = new Pending('core/paged_content_pages:showPages');
|
||||
var existingPages = [];
|
||||
var newPageData = [];
|
||||
var newPagesPromise = $.Deferred();
|
||||
@@ -256,7 +263,9 @@ define(
|
||||
.fail(Notification.exception)
|
||||
.always(function() {
|
||||
loadingPromise.resolve();
|
||||
});
|
||||
pendingPromise.resolve();
|
||||
})
|
||||
.catch();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,14 +25,16 @@ define([
|
||||
'core/custom_interaction_events',
|
||||
'core/paged_content_events',
|
||||
'core/str',
|
||||
'core/pubsub'
|
||||
'core/pubsub',
|
||||
'core/pending',
|
||||
],
|
||||
function(
|
||||
$,
|
||||
CustomEvents,
|
||||
PagedContentEvents,
|
||||
Str,
|
||||
PubSub
|
||||
PubSub,
|
||||
Pending
|
||||
) {
|
||||
|
||||
var SELECTORS = {
|
||||
@@ -451,6 +453,7 @@ function(
|
||||
* @param {string} id A uniqie id for this instance.
|
||||
*/
|
||||
var showPage = function(root, pageNumber, id) {
|
||||
var pendingPromise = new Pending('core/paged_content_paging_bar:showPage');
|
||||
var lastPageNumber = getLastPageNumber(root);
|
||||
var isSamePage = pageNumber == getActivePageNumber(root);
|
||||
var limit = getLimit(root);
|
||||
@@ -492,6 +495,8 @@ function(
|
||||
limit: limit,
|
||||
offset: offset
|
||||
}]);
|
||||
|
||||
pendingPromise.resolve();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -108,7 +108,7 @@ class behat_core_generator extends behat_generator_base {
|
||||
'activities' => [
|
||||
'singular' => 'activity',
|
||||
'datagenerator' => 'activity',
|
||||
'required' => ['activity', 'idnumber', 'course'],
|
||||
'required' => ['activity', 'course'],
|
||||
'switchids' => ['course' => 'course', 'gradecategory' => 'gradecat', 'grouping' => 'groupingid'],
|
||||
],
|
||||
'blocks' => [
|
||||
@@ -377,6 +377,17 @@ class behat_core_generator extends behat_generator_base {
|
||||
}
|
||||
}
|
||||
|
||||
if (!array_key_exists('idnumber', $data)) {
|
||||
$data['idnumber'] = $data['name'];
|
||||
if (strlen($data['name']) > 100) {
|
||||
throw new Exception(
|
||||
"Activity '{$activityname}' cannot be used as the default idnumber. " .
|
||||
"The idnumber has a max length of 100 chars. " .
|
||||
"Please manually specify an idnumber."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// We split $data in the activity $record and the course module $options.
|
||||
$cmoptions = array();
|
||||
$cmcolumns = $DB->get_columns('course_modules');
|
||||
|
||||
@@ -368,6 +368,25 @@ abstract class behat_generator_base {
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the course cmid for the specified activity based on the activity's idnumber.
|
||||
*
|
||||
* Note: this does not check the module type, only the idnumber.
|
||||
*
|
||||
* @throws Exception
|
||||
* @param string $idnumber
|
||||
* @return int
|
||||
*/
|
||||
protected function get_activity_id(string $idnumber) {
|
||||
global $DB;
|
||||
|
||||
if (!$id = $DB->get_field('course_modules', 'id', ['idnumber' => $idnumber])) {
|
||||
throw new Exception('The specified activity with idnumber "' . $idnumber . '" could not be found.');
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the group id from it's idnumber.
|
||||
* @throws Exception
|
||||
@@ -512,4 +531,46 @@ abstract class behat_generator_base {
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a coursemodule from an activity name or idnumber.
|
||||
*
|
||||
* @param string $activity
|
||||
* @param string $identifier
|
||||
* @return cm_info
|
||||
*/
|
||||
protected function get_cm_by_activity_name(string $activity, string $identifier): cm_info {
|
||||
global $DB;
|
||||
|
||||
$coursetable = new \core\dml\table('course', 'c', 'c');
|
||||
$courseselect = $coursetable->get_field_select();
|
||||
$coursefrom = $coursetable->get_from_sql();
|
||||
|
||||
$cmtable = new \core\dml\table('course_modules', 'cm', 'cm');
|
||||
$cmfrom = $cmtable->get_from_sql();
|
||||
|
||||
$acttable = new \core\dml\table($activity, 'a', 'a');
|
||||
$actselect = $acttable->get_field_select();
|
||||
$actfrom = $acttable->get_from_sql();
|
||||
|
||||
$sql = <<<EOF
|
||||
SELECT cm.id as cmid, {$courseselect}, {$actselect}
|
||||
FROM {$cmfrom}
|
||||
INNER JOIN {$coursefrom} ON c.id = cm.course
|
||||
INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
INNER JOIN {$actfrom} ON cm.instance = a.id
|
||||
WHERE cm.idnumber = :idnumber OR a.name = :name
|
||||
EOF;
|
||||
|
||||
$result = $DB->get_record_sql($sql, [
|
||||
'modname' => $activity,
|
||||
'idnumber' => $identifier,
|
||||
'name' => $identifier,
|
||||
], MUST_EXIST);
|
||||
|
||||
$course = $coursetable->extract_from_result($result);
|
||||
$instancedata = $acttable->extract_from_result($result);
|
||||
|
||||
return get_fast_modinfo($course)->get_cm($result->cmid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1437,4 +1437,137 @@ EOF;
|
||||
$timeout = self::get_real_timeout(30) * 1000 * $factor;
|
||||
$driver->getWebDriver()->getCommandExecutor()->setRequestTimeout($timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the course category id from an identifier.
|
||||
*
|
||||
* The category idnumber, and name are checked.
|
||||
*
|
||||
* @param string $identifier
|
||||
* @return int|null
|
||||
*/
|
||||
protected function get_category_id(string $identifier): ?int {
|
||||
global $DB;
|
||||
|
||||
$sql = <<<EOF
|
||||
SELECT id
|
||||
FROM {course_categories}
|
||||
WHERE idnumber = :idnumber
|
||||
OR name = :name
|
||||
EOF;
|
||||
|
||||
$result = $DB->get_field_sql($sql, [
|
||||
'idnumber' => $identifier,
|
||||
'name' => $identifier,
|
||||
]);
|
||||
|
||||
return $result ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the course id from an identifier.
|
||||
*
|
||||
* The course idnumber, shortname, and fullname are checked.
|
||||
*
|
||||
* @param string $identifier
|
||||
* @return int|null
|
||||
*/
|
||||
protected function get_course_id(string $identifier): ?int {
|
||||
global $DB;
|
||||
|
||||
$sql = <<<EOF
|
||||
SELECT id
|
||||
FROM {course}
|
||||
WHERE idnumber = :idnumber
|
||||
OR shortname = :shortname
|
||||
OR fullname = :fullname
|
||||
EOF;
|
||||
|
||||
$result = $DB->get_field_sql($sql, [
|
||||
'idnumber' => $identifier,
|
||||
'shortname' => $identifier,
|
||||
'fullname' => $identifier,
|
||||
]);
|
||||
|
||||
return $result ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activity course module id from its idnumber.
|
||||
*
|
||||
* Note: Only idnumber is supported here, not name at this time.
|
||||
*
|
||||
* @param string $identifier
|
||||
* @return cm_info|null
|
||||
*/
|
||||
protected function get_course_module_for_identifier(string $identifier): ?cm_info {
|
||||
global $DB;
|
||||
|
||||
$coursetable = new \core\dml\table('course', 'c', 'c');
|
||||
$courseselect = $coursetable->get_field_select();
|
||||
$coursefrom = $coursetable->get_from_sql();
|
||||
|
||||
$cmtable = new \core\dml\table('course_modules', 'cm', 'cm');
|
||||
$cmfrom = $cmtable->get_from_sql();
|
||||
|
||||
$sql = <<<EOF
|
||||
SELECT {$courseselect}, cm.id as cmid
|
||||
FROM {$cmfrom}
|
||||
INNER JOIN {$coursefrom} ON c.id = cm.course
|
||||
WHERE cm.idnumber = :idnumber
|
||||
EOF;
|
||||
|
||||
$result = $DB->get_record_sql($sql, [
|
||||
'idnumber' => $identifier,
|
||||
]);
|
||||
|
||||
if ($result) {
|
||||
$course = $coursetable->extract_from_result($result);
|
||||
return get_fast_modinfo($course)->get_cm($result->cmid);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a coursemodule from an activity name or idnumber.
|
||||
*
|
||||
* @param string $activity
|
||||
* @param string $identifier
|
||||
* @return cm_info
|
||||
*/
|
||||
protected function get_cm_by_activity_name(string $activity, string $identifier): cm_info {
|
||||
global $DB;
|
||||
|
||||
$coursetable = new \core\dml\table('course', 'c', 'c');
|
||||
$courseselect = $coursetable->get_field_select();
|
||||
$coursefrom = $coursetable->get_from_sql();
|
||||
|
||||
$cmtable = new \core\dml\table('course_modules', 'cm', 'cm');
|
||||
$cmfrom = $cmtable->get_from_sql();
|
||||
|
||||
$acttable = new \core\dml\table($activity, 'a', 'a');
|
||||
$actselect = $acttable->get_field_select();
|
||||
$actfrom = $acttable->get_from_sql();
|
||||
|
||||
$sql = <<<EOF
|
||||
SELECT cm.id as cmid, {$courseselect}, {$actselect}
|
||||
FROM {$cmfrom}
|
||||
INNER JOIN {$coursefrom} ON c.id = cm.course
|
||||
INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
INNER JOIN {$actfrom} ON cm.instance = a.id
|
||||
WHERE cm.idnumber = :idnumber OR a.name = :name
|
||||
EOF;
|
||||
|
||||
$result = $DB->get_record_sql($sql, [
|
||||
'modname' => $activity,
|
||||
'idnumber' => $identifier,
|
||||
'name' => $identifier,
|
||||
], MUST_EXIST);
|
||||
|
||||
$course = $coursetable->extract_from_result($result);
|
||||
$instancedata = $acttable->extract_from_result($result);
|
||||
|
||||
return get_fast_modinfo($course)->get_cm($result->cmid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,8 @@ class dataformat {
|
||||
* @param string $dataformat
|
||||
* @param array $columns
|
||||
* @param Iterable $iterator
|
||||
* @param callable|null $callback
|
||||
* @param callable|null $callback Optional callback method to apply to each record prior to writing, which accepts two
|
||||
* parameters as such: function($record, bool $supportshtml) returning formatted record
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public static function download_data(string $filename, string $dataformat, array $columns, Iterable $iterator,
|
||||
@@ -89,7 +90,7 @@ class dataformat {
|
||||
$rownum = 0;
|
||||
foreach ($iterator as $row) {
|
||||
if (is_callable($callback)) {
|
||||
$row = $callback($row);
|
||||
$row = $callback($row, $format->supports_html());
|
||||
}
|
||||
if ($row === null) {
|
||||
continue;
|
||||
@@ -131,7 +132,7 @@ class dataformat {
|
||||
$rownum = 0;
|
||||
foreach ($iterator as $row) {
|
||||
if (is_callable($callback)) {
|
||||
$row = $callback($row);
|
||||
$row = $callback($row, $format->supports_html());
|
||||
}
|
||||
if ($row === null) {
|
||||
continue;
|
||||
|
||||
@@ -55,14 +55,20 @@ class curl_security_helper extends curl_security_helper_base {
|
||||
];
|
||||
|
||||
/**
|
||||
* Checks whether the given URL is blacklisted by checking its address and port number against the black/white lists.
|
||||
* Checks whether the given URL is blocked by checking its address and port number against the block/allow lists.
|
||||
* The behaviour of this function can be classified as strict, as it returns true for URLs which are invalid or
|
||||
* could not be parsed, as well as those valid URLs which were found in the blacklist.
|
||||
* could not be parsed, as well as those valid URLs which were found in the list of blocked ones.
|
||||
*
|
||||
* @param string $urlstring the URL to check.
|
||||
* @return bool true if the URL is blacklisted or invalid and false if the URL is not blacklisted.
|
||||
* @param int $notused There used to be an optional parameter $maxredirects for a short while here, not used any more.
|
||||
* @return bool true if the URL is blocked or invalid and false if the URL is not blocked.
|
||||
*/
|
||||
public function url_is_blocked($urlstring) {
|
||||
public function url_is_blocked($urlstring, $notused = null) {
|
||||
|
||||
if ($notused !== null) {
|
||||
debugging('The $maxredirects parameter of curl_security_helper::url_is_blocked() has been dropped!', DEBUG_DEVELOPER);
|
||||
}
|
||||
|
||||
// If no config data is present, then all hosts/ports are allowed.
|
||||
if (!$this->is_enabled()) {
|
||||
return false;
|
||||
@@ -85,7 +91,7 @@ class curl_security_helper extends curl_security_helper_base {
|
||||
}
|
||||
|
||||
if ($parsed['port'] && $parsed['host']) {
|
||||
// Check the host and port against the blacklist/whitelist entries.
|
||||
// Check the host and port against the allow/block entries.
|
||||
return $this->host_is_blocked($parsed['host']) || $this->port_is_blocked($parsed['port']);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -186,10 +186,18 @@ class manager {
|
||||
$localisedeventdata->fullmessage = $eventdata->fullmessage;
|
||||
$localisedeventdata->fullmessagehtml = $eventdata->fullmessagehtml;
|
||||
if (!empty($localisedeventdata->fullmessage)) {
|
||||
// Prevent unclosed HTML elements.
|
||||
$localisedeventdata->fullmessage =
|
||||
\core_message\helper::prevent_unclosed_html_tags($localisedeventdata->fullmessage, true);
|
||||
|
||||
$localisedeventdata->fullmessage .= "\n\n---------------------------------------------------------------------\n"
|
||||
. $emailtagline;
|
||||
}
|
||||
if (!empty($localisedeventdata->fullmessagehtml)) {
|
||||
// Prevent unclosed HTML elements.
|
||||
$localisedeventdata->fullmessagehtml =
|
||||
\core_message\helper::prevent_unclosed_html_tags($localisedeventdata->fullmessagehtml, true);
|
||||
|
||||
$localisedeventdata->fullmessagehtml .=
|
||||
"<br><br>---------------------------------------------------------------------<br>" . $emailtagline;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user