Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a0a0458154 | |||
| 58d1571e08 | |||
| 75283557ad | |||
| 7d23d3ae3a | |||
| af2c368089 | |||
| 3b7f2aa473 | |||
| 00bef0f91d | |||
| 26e53e8cc2 | |||
| be0dea2892 | |||
| e522e7fc67 | |||
| 7e19a2a640 | |||
| 724275abbc | |||
| ea79441d28 | |||
| ca580b25b9 | |||
| 2f785afb00 | |||
| 7b6fddd811 | |||
| ac77c1c217 | |||
| 71bbfc2495 | |||
| c56c08e9ce | |||
| 4c3df551a4 | |||
| 516b81954a | |||
| 4caed80b37 | |||
| 7f6e49af41 | |||
| 69cca28118 | |||
| 3e30033749 | |||
| d61b9111fa | |||
| 07a30d6650 | |||
| 6f25549278 | |||
| 5ee4098ce5 | |||
| 44956b2463 | |||
| e65075c421 | |||
| 3d95f94ca6 | |||
| 1b51d716e2 | |||
| 7602fa7ad9 | |||
| f2ef4c6808 | |||
| 757f0a56c9 | |||
| e22f3d47c2 | |||
| 1ef498d989 | |||
| 747fff61c4 | |||
| 0f368a42cc | |||
| 186284ec1f | |||
| cdfdfc30d9 | |||
| 6c95086bc9 | |||
| 45ef55471b | |||
| ac270cb26f | |||
| b2ba6624ce | |||
| b57eb5dd88 | |||
| 9bf42e7aad | |||
| 4318e21563 |
@@ -65,6 +65,7 @@ jobs:
|
||||
extensions:
|
||||
db: mysqli
|
||||
- os: ubuntu-22.04
|
||||
extensions: mongodb-1.19.4
|
||||
php: 8.1
|
||||
db: pgsql
|
||||
|
||||
|
||||
@@ -4895,4 +4895,201 @@
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="5.1" requires="4.2.3">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="10.11.0" />
|
||||
<VENDOR name="mysql" version="8.4" />
|
||||
<VENDOR name="postgres" version="14" />
|
||||
<VENDOR name="mssql" version="14.0" />
|
||||
</DATABASE>
|
||||
<PHP version="8.2.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="mbstringrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
<PHP_EXTENSION name="sodium" level="required"/>
|
||||
<PHP_EXTENSION name="exif" level="optional"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="zend.exception_ignore_args" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingzendexceptionignoreargs" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="sixtyfourbitsrequired" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_max_input_vars" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_admin_dir_usage" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="iscustomadminwarnings" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_xmlrpc_usage" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mod_assignment" level="required">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_db_prefix_length" level="required">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_async_backup" level="recommended">
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
@@ -36,6 +36,10 @@ if (function_exists('opcache_reset')) {
|
||||
define('CLI_SCRIPT', true);
|
||||
define('CACHE_DISABLE_ALL', true);
|
||||
|
||||
// It makes no sense to use BEHAT_CLI for this script (the Behat launch scripts expect to start
|
||||
// from the normal environment), so in case user has set tne environment variable, disable it.
|
||||
putenv('BEHAT_CLI=0');
|
||||
|
||||
// Basic functions.
|
||||
require_once(__DIR__ . '/../../../../lib/clilib.php');
|
||||
require_once(__DIR__ . '/../../../../lib/behat/lib.php');
|
||||
|
||||
@@ -31,6 +31,10 @@ define('ABORT_AFTER_CONFIG', true);
|
||||
define('CACHE_DISABLE_ALL', true);
|
||||
define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
// It makes no sense to use BEHAT_CLI for this script (the Behat launch scripts expect to start
|
||||
// from the normal environment), so in case user has set tne environment variable, disable it.
|
||||
putenv('BEHAT_CLI=0');
|
||||
|
||||
require_once(__DIR__ .'/../../../../config.php');
|
||||
require_once(__DIR__.'/../../../../lib/clilib.php');
|
||||
require_once(__DIR__.'/../../../../lib/behat/lib.php');
|
||||
|
||||
@@ -39,6 +39,10 @@ define('NO_OUTPUT_BUFFERING', true);
|
||||
define('IGNORE_COMPONENT_CACHE', true);
|
||||
define('ABORT_AFTER_CONFIG', true);
|
||||
|
||||
// It makes no sense to use BEHAT_CLI for this script (the Behat launch scripts expect to start
|
||||
// from the normal environment), so in case user has set tne environment variable, disable it.
|
||||
putenv('BEHAT_CLI=0');
|
||||
|
||||
require_once(__DIR__ . '/../../../../lib/clilib.php');
|
||||
|
||||
// CLI options.
|
||||
|
||||
@@ -33,6 +33,10 @@ if (isset($_SERVER['REMOTE_ADDR'])) {
|
||||
die(); // No access from web!.
|
||||
}
|
||||
|
||||
// It makes no sense to use BEHAT_CLI for this script (the Behat launch scripts expect to start
|
||||
// from the normal environment), so in case user has set tne environment variable, disable it.
|
||||
putenv('BEHAT_CLI=0');
|
||||
|
||||
// Basic functions.
|
||||
require_once(__DIR__ . '/../../../../lib/clilib.php');
|
||||
require_once(__DIR__ . '/../../../../lib/behat/lib.php');
|
||||
|
||||
@@ -46,11 +46,11 @@ Feature: See the competencies for an activity on the course competencies page.
|
||||
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"
|
||||
And I set the competency filter "Filter competencies by resource or activity" to "PageName1"
|
||||
And I press the enter key
|
||||
And I should see "Test-Comp1"
|
||||
And I should not see "Test-Comp2"
|
||||
And I set the field "Filter competencies by resource or activity" to "PageName2"
|
||||
And I set the competency filter "Filter competencies by resource or activity" to "PageName2"
|
||||
And I press the enter key
|
||||
And I should not see "Test-Comp1"
|
||||
And I should not see "Test-Comp2"
|
||||
|
||||
@@ -34,6 +34,10 @@ if (function_exists('opcache_reset')) {
|
||||
|
||||
define('IGNORE_COMPONENT_CACHE', true);
|
||||
|
||||
// It makes no sense to use BEHAT_CLI for this script (you cannot initialise PHPunit starting from
|
||||
// the Behat environment), so in case user has set tne environment variable, disable it.
|
||||
putenv('BEHAT_CLI=0');
|
||||
|
||||
require_once(__DIR__.'/../../../../lib/clilib.php');
|
||||
require_once(__DIR__.'/../../../../lib/phpunit/bootstraplib.php');
|
||||
require_once(__DIR__.'/../../../../lib/testing/lib.php');
|
||||
|
||||
@@ -30,6 +30,10 @@ if (isset($_SERVER['REMOTE_ADDR'])) {
|
||||
|
||||
define('IGNORE_COMPONENT_CACHE', true);
|
||||
|
||||
// It makes no sense to use BEHAT_CLI for this script (you cannot initialise PHPunit starting from
|
||||
// the Behat environment), so in case user has set tne environment variable, disable it.
|
||||
putenv('BEHAT_CLI=0');
|
||||
|
||||
require_once(__DIR__.'/../../../../lib/clilib.php');
|
||||
require_once(__DIR__.'/../../../../lib/phpunit/bootstraplib.php');
|
||||
require_once(__DIR__.'/../../../../lib/testing/lib.php');
|
||||
|
||||
@@ -56,7 +56,7 @@ if ($action == 'delete' && $confirm && confirm_sesskey()) {
|
||||
} else {
|
||||
$msg = get_string('sitebackpacknotdeleted', 'badges');
|
||||
}
|
||||
} else if ($action == 'moveup' || $action == 'movedown') {
|
||||
} else if (($action == 'moveup' || $action == 'movedown') && confirm_sesskey()) {
|
||||
// If no backpack has been selected, there isn't anything to move.
|
||||
if (empty($id)) {
|
||||
redirect($url);
|
||||
|
||||
@@ -56,13 +56,13 @@
|
||||
<td> {{{backpackweburl}}} </td>
|
||||
<td>
|
||||
{{#canmoveup}}
|
||||
<a href="{{baseurl}}?id={{id}}&action=moveup">{{#pix}}t/up, core,{{#str}}moveup{{/str}}{{/pix}}</a>
|
||||
<a href="{{baseurl}}?id={{id}}&sesskey={{sesskey}}&action=moveup">{{#pix}}t/up, core,{{#str}}moveup{{/str}}{{/pix}}</a>
|
||||
{{/canmoveup}}
|
||||
{{^canmoveup}}
|
||||
{{#pix}}spacer, moodle{{/pix}}
|
||||
{{/canmoveup}}
|
||||
{{#canmovedown}}
|
||||
<a href="{{baseurl}}?id={{id}}&action=movedown">{{#pix}}t/down, core,{{#str}}movedown{{/str}}{{/pix}}</a>
|
||||
<a href="{{baseurl}}?id={{id}}&sesskey={{sesskey}}&action=movedown">{{#pix}}t/down, core,{{#str}}movedown{{/str}}{{/pix}}</a>
|
||||
{{/canmovedown}}
|
||||
{{^canmovedown}}
|
||||
{{#pix}}spacer, moodle{{/pix}}
|
||||
|
||||
@@ -4127,7 +4127,7 @@ class core_course_external extends external_api {
|
||||
|
||||
self::validate_context($usercontext);
|
||||
|
||||
if ($userid != $USER->id and !has_capability('moodle/user:viewdetails', $usercontext)) {
|
||||
if ($userid != $USER->id && !has_capability('moodle/user:viewalldetails', $usercontext)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
+8
-1
@@ -1757,7 +1757,14 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
// This is a request for the course information.
|
||||
$courseid = required_param('courseid', PARAM_INT);
|
||||
|
||||
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
|
||||
$course = $DB->get_record('course', ['id' => $courseid], '*', IGNORE_MISSING);
|
||||
if ($course === false) {
|
||||
throw new \moodle_exception('invalidcourseid');
|
||||
}
|
||||
$coursecontext = context_course::instance($course->id, MUST_EXIST);
|
||||
if ($course->visible == 0 && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
|
||||
throw new \moodle_exception('invalidcourseid');
|
||||
}
|
||||
|
||||
$chelper = new coursecat_helper();
|
||||
$chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
|
||||
|
||||
@@ -3639,6 +3639,12 @@ final class externallib_test extends externallib_advanced_testcase {
|
||||
$this->assignUserCapability('moodle/user:viewdetails', $usercontext, $teacherroleid);
|
||||
|
||||
// Sorted by course id DESC.
|
||||
// User without moodle/user:viewalldetails capability will not be able to see the course details.
|
||||
$result = core_course_external::get_recent_courses($student->id);
|
||||
$this->assertCount(0, $result);
|
||||
|
||||
// User with moodle/user:viewalldetails capability will be able to see the course details.
|
||||
$this->assignUserCapability('moodle/user:viewalldetails', $usercontext, $teacherroleid);
|
||||
$result = core_course_external::get_recent_courses($student->id);
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertEquals($courses[0]->id, array_shift($result)->id);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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'] = 'Idioma';
|
||||
$string['moodlelogo'] = 'Logo de Moodle';
|
||||
$string['next'] = 'Següent';
|
||||
$string['previous'] = 'Anterior';
|
||||
$string['reload'] = 'Torna a carregar';
|
||||
@@ -34,8 +34,8 @@ $string['cliansweryes'] = 's';
|
||||
$string['cliincorrectvalueerror'] = 'Error, valor incorrecte "{$a->value}" for "{$a->option}"';
|
||||
$string['cliincorrectvalueretry'] = 'Valor incorrecte, per favor essaya novemente';
|
||||
$string['clitypevalue'] = 'valor de typo';
|
||||
$string['clitypevaluedefault'] = 'valor de typo,pressa Enter per usar le vaor predefinite ({$a})';
|
||||
$string['cliunknowoption'] = 'Uotiones non recognoscite:
|
||||
$string['clitypevaluedefault'] = 'valor de typo, pressa Enter per usar le valor predefinite ({$a})';
|
||||
$string['cliunknowoption'] = 'Optiones non recognoscite:
|
||||
{$a}
|
||||
Per favor usa le option --help.';
|
||||
$string['cliyesnoprompt'] = 'typa s (significa si) o n (significa no)';
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
*
|
||||
* Do not edit this file manually! It contains just a subset of strings
|
||||
* needed during the very first steps of installation. This file was
|
||||
* generated automatically by export-installer.php (which is part of AMOS
|
||||
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
|
||||
* list of strings defined in /install/stringnames.txt.
|
||||
*
|
||||
* @package installer
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['cannotcreatedboninstall'] = '<p>Non pote crear le base de datos.</p>
|
||||
<p>Le base de datos specificate non existe e le date usator non ha le permission a crear le base de datos.</p>
|
||||
<p>Le administrator de sito deberea verificar le configuration de base de datos.</p>';
|
||||
$string['cannotcreatelangdir'] = 'Non pote crear directorio de linguage (lang)';
|
||||
$string['cannotcreatetempdir'] = 'Non pote crear directorio temp';
|
||||
$string['cannotdownloadcomponents'] = 'Non pote discargar componentes';
|
||||
$string['cannotdownloadzipfile'] = 'Non pote discargar file ZIP';
|
||||
$string['cannotfindcomponent'] = 'Non pote trovar componente';
|
||||
$string['cannotsavemd5file'] = 'Non pote salveguardar file md5';
|
||||
$string['cannotsavezipfile'] = 'Non pote salveguardar file ZIP';
|
||||
$string['cannotunzipfile'] = 'Non pote extraher (unzip) file Zip';
|
||||
$string['componentisuptodate'] = 'Componente es actualisate';
|
||||
$string['dmlexceptiononinstall'] = '<p>Un error de base de datos ha occurrite [{$a->errorcode}].<br />{$a->debuginfo}</p>';
|
||||
$string['downloadedfilecheckfailed'] = 'Verifica de file discargate falleva';
|
||||
$string['invalidmd5'] = 'Le variabile de verifica esseva errate - essaya novemente';
|
||||
$string['missingrequiredfield'] = 'Alcun campo requirite es mancante';
|
||||
$string['remotedownloaderror'] = '<p>Le discargamento delcomponente sur tu servitor falleva. Per favor verifica preferentias de proxy; le extension de PHP cURL ed fortemente recommendate.</p>
|
||||
<p>Tu debe discargar le file <a href="{$a->url}">{$a->url}</a> manualmente, copiar lo a "{$a->dest}" in tu servitor e extraher lo (unzip) illac.</p>';
|
||||
$string['wrongdestpath'] = 'Percurso de destination errate';
|
||||
$string['wrongsourcebase'] = 'Base de URL de fonte errate';
|
||||
$string['wrongzipfilename'] = 'Nomine de file ZIP errate';
|
||||
@@ -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['parentlanguage'] = 'pl';
|
||||
$string['thislanguage'] = 'Polski (Workplace)';
|
||||
@@ -1239,6 +1239,7 @@ $string['settingmaxinputvars'] = 'PHP setting max_input_vars is recommended to b
|
||||
$string['settingmaxinputvarsrequired'] = 'PHP setting max_input_vars must be at least 5000.';
|
||||
$string['settingmemorylimit'] = 'Insufficient memory detected, please set higher memory limit in PHP settings.';
|
||||
$string['settingsafemode'] = 'Moodle is not fully compatible with safe mode, please ask server administrator to turn it off. Running Moodle under safe mode is not supported, please expect various problems if you do so.';
|
||||
$string['settingzendexceptionignoreargs'] = 'It is strongly recommended that the PHP setting zend.exception_ignore_args be enabled as a security precaution.';
|
||||
$string['setupsearchengine'] = 'Setup search engine';
|
||||
$string['showcommentscount'] = 'Show comments count';
|
||||
$string['showdetails'] = 'Show details';
|
||||
|
||||
@@ -136,7 +136,8 @@ class ADODB_postgres64 extends ADOConnection{
|
||||
// get the last id - never tested
|
||||
function pg_insert_id($tablename,$fieldname)
|
||||
{
|
||||
$result=pg_query($this->_connectionID, 'SELECT last_value FROM '. $tablename .'_'. $fieldname .'_seq');
|
||||
$sequence = pg_escape_identifier($this->_connectionID, $tablename .'_'. $fieldname .'_seq');
|
||||
$result = pg_query($this->_connectionID, 'SELECT last_value FROM '. $sequence);
|
||||
if ($result) {
|
||||
$arr = @pg_fetch_row($result,0);
|
||||
pg_free_result($result);
|
||||
|
||||
@@ -23,3 +23,7 @@ Removed:
|
||||
Added:
|
||||
* index.html - prevent directory browsing on misconfigured servers
|
||||
* readme_moodle.txt - this file ;-)
|
||||
|
||||
Changelog:
|
||||
* MDL-85375:
|
||||
- Fix SQL injection in pg_insert_id() - https://github.com/ADOdb/ADOdb/commit/11107d6d6e5160b62e05dff8a3a2678cf0e3a426
|
||||
|
||||
@@ -72,7 +72,6 @@ class behat_field_manager {
|
||||
* @return behat_form_field
|
||||
*/
|
||||
public static function get_form_field(NodeElement $fieldnode, Session $session) {
|
||||
|
||||
// Get the field type if is part of a moodleform.
|
||||
if (self::is_moodleform_field($fieldnode)) {
|
||||
$type = self::get_field_node_type($fieldnode, $session);
|
||||
|
||||
@@ -326,4 +326,13 @@ class behat_form_field implements behat_session_interface {
|
||||
|
||||
return $this->fieldlocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field node.
|
||||
*
|
||||
* @return NodeElement
|
||||
*/
|
||||
public function get_node(): NodeElement {
|
||||
return $this->field;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,6 +341,11 @@ function behat_is_test_site() {
|
||||
if (empty($CFG->behat_wwwroot)) {
|
||||
return false;
|
||||
}
|
||||
if (defined('CLI_SCRIPT') && CLI_SCRIPT && getenv('BEHAT_CLI')) {
|
||||
// Environment variable makes CLI script run on Behat instance.
|
||||
echo "BEHAT_CLI: This command line script is running on the acceptance testing site.\n\n";
|
||||
return true;
|
||||
}
|
||||
if (isset($_SERVER['REMOTE_ADDR']) and behat_is_requested_url($CFG->behat_wwwroot)) {
|
||||
// Something is accessing the web server like a real browser.
|
||||
return true;
|
||||
|
||||
@@ -84,8 +84,8 @@ class encryption {
|
||||
throw new \moodle_exception('encryption_keyalreadyexists', 'error');
|
||||
}
|
||||
|
||||
// Don't make it read-only in Behat or it will fail to clear for future runs.
|
||||
if (defined('BEHAT_SITE_RUNNING')) {
|
||||
// Don't make it read-only in tests or it will fail to clear for future runs.
|
||||
if (defined('BEHAT_SITE_RUNNING') || PHPUNIT_TEST) {
|
||||
$chmod = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,21 @@ class curl_security_helper extends curl_security_helper_base {
|
||||
'https' => 443
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string the host of the URL being checked by the helper.
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* @var array IP address or addresses the URL is allowed to be requested from (passed the blocked hosts check).
|
||||
*/
|
||||
protected $allowedips = [];
|
||||
|
||||
/**
|
||||
* @var ?int The port the URL is allowed to be requested from (passed the allowed port check).
|
||||
*/
|
||||
protected $allowedport;
|
||||
|
||||
/**
|
||||
* Checks whether the given URL is blocked by checking its address and port number against the allow/block lists.
|
||||
* The behaviour of this function can be classified as strict, as it returns true for URLs which are invalid or
|
||||
@@ -85,6 +100,8 @@ class curl_security_helper extends curl_security_helper_base {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->host = $parsed['host'];
|
||||
|
||||
// The port will be empty unless explicitly set in the $url (uncommon), so try to infer it from the supported schemes.
|
||||
if (!$parsed['port'] && $parsed['scheme'] && isset($this->transportschemes[$parsed['scheme']])) {
|
||||
$parsed['port'] = $this->transportschemes[$parsed['scheme']];
|
||||
@@ -162,12 +179,17 @@ class curl_security_helper extends curl_security_helper_base {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If any of the returned IPs are in the blocklist, block the request.
|
||||
// If any of the returned IPs are in the blocklist, block the request. Otherwise, temporarily record the IPs.
|
||||
$allowedips = [];
|
||||
foreach ($hostips as $hostip) {
|
||||
if ($this->address_explicitly_blocked($hostip)) {
|
||||
return true;
|
||||
}
|
||||
$allowedips[] = $hostip;
|
||||
}
|
||||
|
||||
// If none of the IPs are blocked, set them on the allow list so we can enforce them on subsequent requests.
|
||||
$this->allowedips = $allowedips;
|
||||
}
|
||||
} else {
|
||||
// Was not something we consider to be a valid IP or domain name, block it.
|
||||
@@ -201,7 +223,15 @@ class curl_security_helper extends curl_security_helper_base {
|
||||
return true;
|
||||
}
|
||||
$allowedports = $this->get_allowed_ports();
|
||||
return !empty($allowedports) && !in_array($portnum, $allowedports);
|
||||
|
||||
$isblocked = !empty($allowedports) && !in_array($portnum, $allowedports);
|
||||
|
||||
// If port is allowed, add it to our allow list so we can enforce it on subsequent requests.
|
||||
if (!$isblocked) {
|
||||
$this->allowedport = $portnum;
|
||||
}
|
||||
|
||||
return $isblocked;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,4 +321,23 @@ class curl_security_helper extends curl_security_helper_base {
|
||||
return !empty($entry);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper that returns host, IP and port information for the URL that has passed the blocked hosts/allowed ports checks.
|
||||
*
|
||||
* This data is in a format compatible with CURLOPT_RESOLVE, so it can be passed directly into that option.
|
||||
* Doing so will prevent cURL re-fetching the info from DNS, preventing subsequent requests to the remote host from
|
||||
* modifying the IP/port to ones that haven't been validated.
|
||||
*
|
||||
* @return array of strings in the format hostname:port:ip_address.
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public function get_resolve_info(): array {
|
||||
if (empty($this->host || empty($this->allowedips) || empty($this->allowedport))) {
|
||||
$exception = 'In the curl_security_helper class, url_is_blocked() must be called before get_resolve_info() is called.';
|
||||
throw new \core\exception\coding_exception($exception);
|
||||
}
|
||||
|
||||
return array_map(fn($ip) => "$this->host:$this->allowedport:$ip", $this->allowedips);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3163,6 +3163,8 @@ class curl {
|
||||
private $ignoresecurity;
|
||||
/** @var array $mockresponses For unit testing only - return the head of this list instead of making the next request. */
|
||||
private static $mockresponses = [];
|
||||
/** @var array $curlresolveinfo Resolve addresses for the URL that have passed cuRL security checks, in a CURLOPT_RESOLVE compatible format. */
|
||||
private $curlresolveinfo = [];
|
||||
|
||||
/**
|
||||
* Curl constructor.
|
||||
@@ -3759,6 +3761,9 @@ class curl {
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
// Set allowed resolve info if the URL is not blocked.
|
||||
$this->curlresolveinfo = $this->securityhelper->get_resolve_info();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -3794,6 +3799,10 @@ class curl {
|
||||
// Set the URL as a curl option.
|
||||
$this->setopt(array('CURLOPT_URL' => $url));
|
||||
|
||||
// Force cURL to only resolve the URL from IP/port combinations that were validated by the security helper.
|
||||
// This prevents re-fetching DNS data on subsequent requests, which could return un-validated hosts/ports.
|
||||
$this->setopt(['CURLOPT_RESOLVE' => $this->curlresolveinfo]);
|
||||
|
||||
// Create curl instance.
|
||||
$curl = curl_init();
|
||||
|
||||
@@ -3904,6 +3913,10 @@ class curl {
|
||||
|
||||
curl_setopt($curl, CURLOPT_URL, $redirecturl);
|
||||
|
||||
// Force cURL to only resolve the URL from IP/port combinations that were validated by the security helper.
|
||||
// This prevents re-fetching DNS data on subsequent requests, which could return un-validated hosts/ports.
|
||||
$this->setopt(['CURLOPT_RESOLVE' => $this->curlresolveinfo]);
|
||||
|
||||
// If CURLOPT_UNRESTRICTED_AUTH is empty/false, don't send credentials to other hosts.
|
||||
// Ref: https://curl.se/libcurl/c/CURLOPT_UNRESTRICTED_AUTH.html.
|
||||
$isdifferenthost = parse_url($currenturl)['host'] !== parse_url($redirecturl)['host'];
|
||||
|
||||
@@ -32,26 +32,8 @@ namespace core;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
final class encryption_test extends \advanced_testcase {
|
||||
/**
|
||||
* Clear junk created by tests.
|
||||
*/
|
||||
protected function tearDown(): void {
|
||||
global $CFG;
|
||||
$keyfile = encryption::get_key_file(encryption::METHOD_OPENSSL);
|
||||
if (file_exists($keyfile)) {
|
||||
chmod($keyfile, 0700);
|
||||
}
|
||||
$keyfile = encryption::get_key_file(encryption::METHOD_SODIUM);
|
||||
if (file_exists($keyfile)) {
|
||||
chmod($keyfile, 0700);
|
||||
}
|
||||
remove_dir($CFG->dataroot . '/secret');
|
||||
unset($CFG->nokeygeneration);
|
||||
}
|
||||
|
||||
protected function setUp(): void {
|
||||
$this->tearDown();
|
||||
|
||||
require_once(__DIR__ . '/fixtures/testable_encryption.php');
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -2410,8 +2410,7 @@ function send_headers($contenttype, $cacheable = true) {
|
||||
@header('Expires: ');
|
||||
} else {
|
||||
// Do everything we can to always prevent clients and proxies caching.
|
||||
@header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
@header('Cache-Control: post-check=0, pre-check=0, no-transform', false);
|
||||
@header('Cache-Control: no-store, no-cache, must-revalidate, no-transform');
|
||||
@header('Pragma: no-cache');
|
||||
@header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
|
||||
@header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
@@ -49,6 +49,7 @@ $context = context_system::instance();
|
||||
$PAGE->set_url("$CFG->wwwroot/login/index.php");
|
||||
$PAGE->set_context($context);
|
||||
$PAGE->set_pagelayout('login');
|
||||
$PAGE->set_cacheable(false);
|
||||
|
||||
/// Initialize variables
|
||||
$errormsg = '';
|
||||
|
||||
@@ -55,7 +55,7 @@ Feature: View activity completion in the assignment activity
|
||||
And the manual completion button of "Music history" is displayed as "Done"
|
||||
|
||||
@javascript
|
||||
Scenario: The manual completion button will not be shown on the course page if the Show activity completion conditions is set to No
|
||||
Scenario: Assign module manual completion button hidden if Show activity completion is set to No
|
||||
Given I am on the "Course 1" course page logged in as teacher1
|
||||
And I navigate to "Settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
@mod @mod_assign
|
||||
Feature: Additional files for use in assignments can be hidden
|
||||
In order to display additional files only during submission
|
||||
As a teacher
|
||||
I need to check the 'Only show files during submission' checkbox
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | One | teacher1@example.com |
|
||||
| student1 | Student | One | student1@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
|
||||
@javascript @_file_upload
|
||||
Scenario: Additional files are only shown during submission
|
||||
Given the following "activities" exist:
|
||||
| activity | course | name | submissionattachments | assignsubmission_onlinetext_enabled | assignsubmission_file_enabled |
|
||||
| assign | C1 | Assign 1 | 1 | 1 | 0 |
|
||||
And I am on the "Assign 1" "assign activity editing" page logged in as teacher1
|
||||
And I upload "/mod/assign/tests/fixtures/submissionsample01.txt" file to "Additional files" filemanager
|
||||
And I press "Save and return to course"
|
||||
When I am on the "Assign 1" "assign activity" page logged in as student1
|
||||
# Confirm that Additional files are not displayed on assignment activity when student logs in.
|
||||
Then "submissionsample01.txt" "link" should not exist
|
||||
And I press "Add submission"
|
||||
# Additional files are displayed after student presses "Add submission" button and goes to submission phase.
|
||||
And "submissionsample01.txt" "link" should exist
|
||||
And following "submissionsample01.txt" should download a file that:
|
||||
| Has mimetype | text/plain |
|
||||
| Contains text | This is just a submission testing sample. |
|
||||
@@ -68,6 +68,8 @@ $context = $instance->get_context();
|
||||
|
||||
require_login($course, true, $cm);
|
||||
|
||||
require_sesskey();
|
||||
|
||||
// Note : this uses the group optional_param as a value to decide which groupid.
|
||||
$groupid = groups_get_activity_group($cm, true) ?: null;
|
||||
if ($groupid) {
|
||||
@@ -144,14 +146,18 @@ switch (strtolower($action)) {
|
||||
break;
|
||||
|
||||
case 'play':
|
||||
$recording = recording::get_record(['id' => $rid]);
|
||||
if ($href = $recording->get_remote_playback_url($rtype)) {
|
||||
logger::log_recording_played_event($instance, $rid);
|
||||
redirect(urldecode($href));
|
||||
} else {
|
||||
$recordings = $instance->get_recordings();
|
||||
if (!isset($recordings[$rid])) {
|
||||
notification::add(get_string('recordingnotfound', 'mod_bigbluebuttonbn'), notification::ERROR);
|
||||
redirect($instance->get_view_url());
|
||||
}
|
||||
$href = $recordings[$rid]->get_remote_playback_url($rtype);
|
||||
if (!$href) {
|
||||
notification::add(get_string('recordingurlnotfound', 'mod_bigbluebuttonbn'), notification::ERROR);
|
||||
redirect($instance->get_view_url());
|
||||
}
|
||||
logger::log_recording_played_event($instance, $rid);
|
||||
redirect(urldecode($href));
|
||||
// We should never reach this point.
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1034,7 +1034,8 @@ EOF;
|
||||
return new moodle_url('/mod/bigbluebuttonbn/bbb_view.php', [
|
||||
'action' => 'logout',
|
||||
'id' => $this->cm->id,
|
||||
'courseid' => $this->cm->course // Used to find the course if ever the activity is deleted
|
||||
'courseid' => $this->cm->course, // Used to find the course if ever the activity is deleted
|
||||
'sesskey' => sesskey(),
|
||||
// while the meeting is running.
|
||||
]);
|
||||
}
|
||||
@@ -1073,6 +1074,7 @@ EOF;
|
||||
'action' => 'join',
|
||||
'id' => $this->cm->id,
|
||||
'bn' => $this->instancedata->id,
|
||||
'sesskey' => sesskey(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -577,6 +577,7 @@ class recording extends persistent {
|
||||
'bn' => $this->raw_get('bigbluebuttonbnid'),
|
||||
'rid' => $this->get('id'),
|
||||
'rtype' => $clone['type'],
|
||||
'sesskey' => sesskey(),
|
||||
]);
|
||||
|
||||
return $clone;
|
||||
|
||||
@@ -648,4 +648,5 @@ $string['taskname:check_dismissed_recordings'] = 'Check for recordings that have
|
||||
$string['userlimitreached'] = 'The number of users allowed in a session has been reached.';
|
||||
$string['waitformoderator'] = 'Waiting for a moderator to join.';
|
||||
|
||||
$string['recordingnotfound'] = 'The recording was not found.';
|
||||
$string['recordingurlnotfound'] = 'The recording URL is invalid.';
|
||||
|
||||
@@ -529,7 +529,9 @@ function mod_bigbluebuttonbn_core_calendar_provide_event_action(
|
||||
'action' => 'join',
|
||||
'id' => $cm->id,
|
||||
'bn' => $bigbluebuttonbn->id,
|
||||
'timeline' => 1]
|
||||
'timeline' => 1,
|
||||
'sesskey' => sesskey(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,19 +6,58 @@ Feature: In a lesson activity, teachers can review student attempts
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| student2 | Student | 2 | student2@example.com |
|
||||
# Force group mode so you don't need to set it manually on the activity.
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
| fullname | shortname | category | groupmodeforce |
|
||||
| Course 1 | C1 | 0 | |
|
||||
| Course 2 | C2 | | 1 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
| teacher1 | C2 | editingteacher |
|
||||
| student1 | C2 | student |
|
||||
| student2 | C2 | student |
|
||||
And the following "activities" exist:
|
||||
| activity | name | course | idnumber | retake |
|
||||
| lesson | Test lesson name | C1 | lesson1 | 1 |
|
||||
| activity | name | course | idnumber | retake |
|
||||
| lesson | Test lesson name | C1 | lesson1 | 1 |
|
||||
| lesson | Lesson 1 | C2 | | |
|
||||
And the following "groups" exist:
|
||||
| name | course | idnumber |
|
||||
| Group 1 | C2 | G1 |
|
||||
| Group 2 | C2 | G2 |
|
||||
And the following "group members" exist:
|
||||
| user | group |
|
||||
| student1 | G1 |
|
||||
| student2 | G2 |
|
||||
And the following "mod_lesson > page" exist:
|
||||
| lesson | qtype | title | content |
|
||||
| Lesson 1 | multichoice | Multichoice question | This is a multichoice question. |
|
||||
| Lesson 1 | essay | Essay question | Write an essay about anything. |
|
||||
| Lesson 1 | content | Content page | First page contents. |
|
||||
And the following "mod_lesson > answers" exist:
|
||||
| page | answer | jumpto | score |
|
||||
| Multichoice question | Correct answer | Next page | 1 |
|
||||
| Multichoice question | Incorrect answer | This page | 0 |
|
||||
| Essay question | | Next page | 1 |
|
||||
| Content page | Previous page | Previous page | 0 |
|
||||
| Content page | Next page | Next page | 0 |
|
||||
And I am on the "Lesson 1" "lesson activity" page logged in as student1
|
||||
And I set the following fields to these values:
|
||||
| Correct answer | 1 |
|
||||
And I press "Submit"
|
||||
And I set the field "Your answer" to "School is awesome!"
|
||||
And I press "Submit"
|
||||
And I am on the "Lesson 1" "lesson activity" page logged in as student2
|
||||
And I set the following fields to these values:
|
||||
| Incorrect answer | 1 |
|
||||
And I press "Submit"
|
||||
And I set the field "Your answer" to "Once upon an essay."
|
||||
And I press "Submit"
|
||||
|
||||
Scenario: View student attempts in a lesson containing both content and question pages
|
||||
Given the following "mod_lesson > pages" exist:
|
||||
@@ -96,3 +135,53 @@ Feature: In a lesson activity, teachers can review student attempts
|
||||
And I should not see "High score"
|
||||
And I should not see "Average score"
|
||||
And I should not see "Low score"
|
||||
|
||||
Scenario Outline: Teacher can filter lesson essay grading by group
|
||||
Given I am on the "C2" "course editing" page logged in as teacher1
|
||||
And I set the field "Group mode" to "<groupmode>"
|
||||
And I press "Save and display"
|
||||
And I am on the "Lesson 1" "lesson activity" page
|
||||
And I press "Grade essays"
|
||||
When I select "Group 1" from the "<groupmode>" singleselect
|
||||
Then I should see "Student 1"
|
||||
And I should not see "Student 2"
|
||||
And I select "Group 2" from the "<groupmode>" singleselect
|
||||
And I should see "Student 2"
|
||||
And I should not see "Student 1"
|
||||
And I select "All participants" from the "<groupmode>" singleselect
|
||||
And I should see "Student 1"
|
||||
And I should see "Student 2"
|
||||
|
||||
Examples:
|
||||
| groupmode |
|
||||
| Separate groups |
|
||||
| Visible groups |
|
||||
|
||||
Scenario Outline: Teacher can filter lesson reports by group
|
||||
Given I am on the "C2" "course editing" page logged in as teacher1
|
||||
And I set the field "Group mode" to "<groupmode>"
|
||||
And I press "Save and display"
|
||||
And I am on the "Lesson 1" "lesson activity" page
|
||||
And I navigate to "Reports" in current page administration
|
||||
When I select "Group 1" from the "<groupmode>" singleselect
|
||||
Then I should see "Student 1"
|
||||
And I should not see "Student 2"
|
||||
And I select "Group 2" from the "<groupmode>" singleselect
|
||||
And I should see "Student 2"
|
||||
And I should not see "Student 1"
|
||||
And I select "All participants" from the "<groupmode>" singleselect
|
||||
And I should see "Student 1"
|
||||
And I should see "Student 2"
|
||||
|
||||
Examples:
|
||||
| groupmode |
|
||||
| Separate groups |
|
||||
| Visible groups |
|
||||
|
||||
@javascript
|
||||
Scenario: Teacher can delete selected attempts
|
||||
Given I am on the "Lesson 1" "lesson activity" page logged in as teacher1
|
||||
And I navigate to "Reports" in current page administration
|
||||
When I click on "selectall-attempts" "checkbox"
|
||||
And I select "Delete selected" from the "With selected attempts..." singleselect
|
||||
Then I should see "No attempts have been made on this lesson."
|
||||
|
||||
@@ -14,9 +14,11 @@ Feature: View activity completion information in the Page resource
|
||||
| Course 1 | C1 | 0 | 1 | 1 |
|
||||
| Course 2 | C2 | 0 | 1 | 0 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| student1 | C2 | student |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| teacher1 | C2 | editingteacher |
|
||||
|
||||
Scenario: A teacher can view a page resource automatic completion items
|
||||
Given the following "activity" exists:
|
||||
@@ -69,7 +71,7 @@ Feature: View activity completion information in the Page resource
|
||||
And I toggle the manual completion state of "Music history"
|
||||
And the manual completion button of "Music history" is displayed as "Done"
|
||||
|
||||
Scenario: The manual completion button will not be shown on the course page if the Show activity completion conditions is set to No as teacher
|
||||
Scenario Outline: Page module manual completion button hidden if Show activity completion is set to No
|
||||
Given the following "activity" exists:
|
||||
| activity | page |
|
||||
| course | C2 |
|
||||
@@ -77,16 +79,11 @@ Feature: View activity completion information in the Page resource
|
||||
| name | Music history |
|
||||
| intro | A lesson learned in life |
|
||||
| completion | 1 |
|
||||
When I am on the "Music history" "page activity" page logged in as teacher1
|
||||
When I am on the "Course 2" course page logged in as <user>
|
||||
# Course 2 has 'Show activity completion conditions' set to No, so the manual completion button should not be displayed.
|
||||
Then the manual completion button for "Music history" should not exist
|
||||
|
||||
Scenario: The manual completion button will not be shown on the course page if the Show activity completion conditions is set to No as student
|
||||
Given the following "activity" exists:
|
||||
| activity | page |
|
||||
| course | C2 |
|
||||
| idnumber | page1 |
|
||||
| name | Music history |
|
||||
| intro | A lesson learned in life |
|
||||
| completion | 1 |
|
||||
When I am on the "Music history" "page activity" page logged in as student1
|
||||
Then the manual completion button for "Music history" should not exist
|
||||
Examples:
|
||||
| user |
|
||||
| teacher1 |
|
||||
| student1 |
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
@mod @mod_quiz
|
||||
Feature: Blocks can be displayed on quiz attempt pages
|
||||
In order to see blocks on quiz attempt pages
|
||||
As a teacher
|
||||
I need to be able to set blocks display settings for quiz
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username |
|
||||
| student |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student | C1 | student |
|
||||
|
||||
Scenario Outline: Blocks display on quiz attempt pages can be toggled
|
||||
Given the following "activities" exist:
|
||||
| activity | course | name | idnumber | showblocks |
|
||||
| quiz | C1 | Quiz 1 | q1 | <blockdisplay> |
|
||||
And the following "question categories" exist:
|
||||
| contextlevel | reference | name |
|
||||
| Activity module | q1 | Test questions |
|
||||
And the following "questions" exist:
|
||||
| questioncategory | qtype | name |
|
||||
| Test questions | truefalse | TF1 |
|
||||
And quiz "Quiz 1" contains the following questions:
|
||||
| question | page |
|
||||
| TF1 | 1 |
|
||||
And the following "blocks" exist:
|
||||
| blockname | contextlevel | reference | pagetypepattern | defaultregion |
|
||||
| comments | Activity module | q1 | mod-quiz-attempt | side-pre |
|
||||
When I am on the "Quiz 1" "quiz activity" page logged in as student
|
||||
And I press "Attempt quiz"
|
||||
Then "Comments" "block" <blockvisibility> exist
|
||||
|
||||
Examples:
|
||||
| blockdisplay | blockvisibility |
|
||||
| 1 | should |
|
||||
| 0 | should not |
|
||||
@@ -37,7 +37,7 @@ Feature: Preview a Multiple choice question
|
||||
And I should see "Parts, but only parts, of your response are correct."
|
||||
|
||||
@javascript @_switch_window
|
||||
Scenario: Preview a Multiple choice question and submit a correct response.
|
||||
Scenario: Preview a Multiple choice question and submit multiple correct responses.
|
||||
When I am on the "Multi-choice-001" "core_question > preview" page logged in as teacher
|
||||
And I expand all fieldsets
|
||||
And I set the field "How questions behave" to "Immediate feedback"
|
||||
@@ -53,7 +53,7 @@ Feature: Preview a Multiple choice question
|
||||
And I should see "The correct answers are: One, Three"
|
||||
|
||||
@javascript @_switch_window
|
||||
Scenario: Preview a Multiple choice question and submit a correct response.
|
||||
Scenario: Preview a Multiple choice question and submit a single correct response.
|
||||
When I am on the "Multi-choice-002" "core_question > preview" page logged in as teacher
|
||||
And I expand all fieldsets
|
||||
And I set the field "How questions behave" to "Immediate feedback"
|
||||
|
||||
@@ -76,4 +76,52 @@ class behat_report_competency extends behat_base {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a competency filter.
|
||||
*
|
||||
* @When /^I set the competency filter "([^"]*)" to "([^"]*)"$/
|
||||
* @param string $fieldlocator The field locator.
|
||||
* @param string $value The value to set.
|
||||
* @throws Exception
|
||||
*/
|
||||
public function set_competency_filter(
|
||||
string $fieldlocator,
|
||||
string $value
|
||||
): void {
|
||||
$field = behat_field_manager::get_form_field_from_label($fieldlocator, $this)->get_node();
|
||||
$session = $this->getSession();
|
||||
|
||||
$value = trim($value);
|
||||
|
||||
// Click into the field.
|
||||
$field->click();
|
||||
|
||||
// Remove any existing text.
|
||||
do {
|
||||
behat_base::type_keys($session, [behat_keys::BACKSPACE, behat_keys::DELETE]);
|
||||
} while (strlen($field->getValue()) > 0);
|
||||
$this->wait_for_pending_js();
|
||||
|
||||
// Type in the new value.
|
||||
behat_base::type_keys($session, str_split($value));
|
||||
$this->wait_for_pending_js();
|
||||
|
||||
// If the autocomplete found suggestions, then it will have:
|
||||
// 1) marked itself as expanded; and
|
||||
// 2) have an aria-selected suggestion in the list.
|
||||
$expanded = $field->getAttribute('aria-expanded');
|
||||
$suggestion = $field->getParent()->getParent()->find('css', '.form-autocomplete-suggestions > [aria-selected="true"]');
|
||||
|
||||
if ($expanded && null !== $suggestion) {
|
||||
// A suggestion was found.
|
||||
// Click on the first item in the list.
|
||||
$suggestion->click();
|
||||
} else {
|
||||
throw new \InvalidArgumentException(
|
||||
"Unable to find '{$value}' in the list of options."
|
||||
);
|
||||
}
|
||||
|
||||
$this->wait_for_pending_js();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ Feature: See the competencies for an activity
|
||||
Scenario: Go to the competency breakdown report
|
||||
When I navigate to "Reports" in current page administration
|
||||
And I click on "Competency breakdown" "link"
|
||||
And I set the field "Filter competencies by resource or activity" to "PageName1"
|
||||
And I set the competency filter "Filter competencies by resource or activity" to "PageName1"
|
||||
Then I should see "Test-Comp1"
|
||||
And I should not see "Test-Comp2"
|
||||
And I should see "Ann, Jill, Grainne, Beauchamp"
|
||||
|
||||
+2
-2
@@ -29,9 +29,9 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2022112818.00; // 20221128 = branching date YYYYMMDD - do not modify!
|
||||
$version = 2022112819.00; // 20221128 = branching date YYYYMMDD - do not modify!
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
$release = '4.1.18 (Build: 20250414)'; // Human-friendly version name
|
||||
$release = '4.1.19 (Build: 20250609)'; // Human-friendly version name
|
||||
$branch = '401'; // This version's branch.
|
||||
$maturity = MATURITY_STABLE; // This version's maturity level.
|
||||
|
||||
Reference in New Issue
Block a user