Compare commits

...

88 Commits

Author SHA1 Message Date
Mihail Geshoski a0a0458154 Moodle release 4.1.19 2025-06-06 12:26:16 +08:00
Sara Arjona 58d1571e08 weekly release 4.1.18+ 2025-06-05 16:43:46 +02:00
Michael Hawkins 75283557ad MDL-83762 core_files: Bind resolve IPs and ports to cURL calls 2025-06-05 12:10:26 +07:00
Vincent Schneider 7d23d3ae3a MDL-84518 course: improved course visibility state handling 2025-06-05 11:23:19 +07:00
Mark Johnson af2c368089 MDL-85323 core: Don't cache the login page 2025-06-05 11:02:54 +07:00
meirzamoodle 3b7f2aa473 MDL-85375 libraries: Escaping a query parameter on pg_insert_id() 2025-06-02 09:55:58 +07:00
Vincent Schneider 00bef0f91d MDL-84706 bigbluebuttonbn: improved authorization and mitigate CSRF risks 2025-05-30 08:40:21 +00:00
Huong Nguyen 26e53e8cc2 MDL-79993 course: Fix PHPUnit failure 2025-05-30 08:40:21 +00:00
raortegar be0dea2892 MDL-79993 course: Improve user access to recent courses service 2025-05-30 08:40:21 +00:00
Vincent Schneider e522e7fc67 MDL-84497 badges: fix missing action sesskey checks. 2025-05-30 08:40:21 +00:00
Michael Hawkins 7e19a2a640 weekly release 4.1.18+ 2025-05-30 16:29:50 +08:00
Michael Hawkins 724275abbc Merge branch 'install_401_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_401_STABLE 2025-05-30 16:29:43 +08:00
Huong Nguyen ea79441d28 Merge branch 'MDL-85525-401' of https://github.com/andelacruz/moodle into MOODLE_401_STABLE 2025-05-29 09:52:08 +07:00
Huong Nguyen ca580b25b9 Merge branch 'MDL-85400-401' of https://github.com/lameze/moodle into MOODLE_401_STABLE 2025-05-29 09:00:17 +07:00
Huong Nguyen 2f785afb00 Merge branch 'MDL-85389-401' of https://github.com/andelacruz/moodle into MOODLE_401_STABLE 2025-05-29 08:53:08 +07:00
AMOS bot 7b6fddd811 Automatically generated installer lang files 2025-05-29 00:07:40 +00:00
andelacruz ac77c1c217 MDL-85525 mod_quiz: Behat test for quiz attempt page block display 2025-05-28 16:20:56 +08:00
andelacruz 71bbfc2495 MDL-85389 mod_lesson: Behat for filtering lesson reports/essays by group 2025-05-28 15:42:08 +08:00
sam marshall c56c08e9ce MDL-85400 PHPunit: Hard to test code that uses encryption
This change stops the encryption code setting created key files to
read-only during PHPunit runs, which means they will be safely
deleted when the system resets after a test.
2025-05-28 13:14:34 +08:00
Huong Nguyen 4c3df551a4 weekly release 4.1.18+ 2025-05-23 11:04:05 +07:00
Huong Nguyen 516b81954a Merge branch 'install_401_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_401_STABLE 2025-05-23 11:04:04 +07:00
Huong Nguyen 4caed80b37 Merge branch 'MDL-84793-m401' of https://github.com/sammarshallou/moodle into MOODLE_401_STABLE 2025-05-22 10:49:00 +07:00
Huong Nguyen 7f6e49af41 Merge branch 'MDL-84918-401' of https://github.com/HuongNV13/moodle into MOODLE_401_STABLE 2025-05-22 10:35:05 +07:00
David Woloszyn 69cca28118 MDL-84918 core: Add zend.exception_ignore_args environment check 2025-05-22 10:12:03 +07:00
Huong Nguyen 3e30033749 MDL-84918 core: Add initial environment requirements for Moodle 5.1 2025-05-22 10:12:03 +07:00
Huong Nguyen d61b9111fa Merge branch 'MDL-85374-401' of https://github.com/andrewnicols/moodle into MOODLE_401_STABLE 2025-05-21 09:56:32 +07:00
AMOS bot 07a30d6650 Automatically generated installer lang files 2025-05-21 00:07:37 +00:00
Andrew Nicols 6f25549278 MDL-85374 report_competency: Create custom step for setting autocomplete
The autocompletes used in the competency report have been bastardised to
redirect on selection. Unfortunately this also applies when when making
null-op changes like deleting the initial "Please select a value"
option.

The autocomplete code removes all current options before creating an
option, and because of this redirect the browser is redirecting before
the new value can be entered, but we retain a reference to the field as
`$this->field`, which is now stale.
2025-05-20 13:09:53 +08:00
Mihail Geshoski 5ee4098ce5 weekly release 4.1.18+ 2025-05-16 12:59:25 +08:00
Mihail Geshoski 44956b2463 Merge branch 'install_401_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_401_STABLE 2025-05-16 12:59:18 +08:00
sam marshall e65075c421 MDL-84793 Behat: Allow CLI scripts to run on Behat instance 2025-05-15 08:51:54 +01:00
Huong Nguyen 3d95f94ca6 Merge branch 'MDL-85279-401' of https://github.com/andelacruz/moodle into MOODLE_401_STABLE 2025-05-15 11:43:10 +07:00
andelacruz 1b51d716e2 MDL-85279 mod_assign: Renamed behat test to a more specific name 2025-05-14 15:17:01 +08:00
andelacruz 7602fa7ad9 MDL-85279 mod_page: Fix behat to match the expected behaviour 2025-05-14 15:13:15 +08:00
AMOS bot f2ef4c6808 Automatically generated installer lang files 2025-05-10 00:07:38 +00:00
Huong Nguyen 757f0a56c9 weekly release 4.1.18+ 2025-05-09 09:50:36 +07:00
Huong Nguyen e22f3d47c2 Merge branch 'install_401_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_401_STABLE 2025-05-09 09:50:35 +07:00
Huong Nguyen 1ef498d989 Merge branch 'MDL-84654-401' of https://github.com/andelacruz/moodle into MOODLE_401_STABLE 2025-05-08 08:44:56 +07:00
AMOS bot 747fff61c4 Automatically generated installer lang files 2025-05-07 00:07:33 +00:00
Angelia Dela Cruz 0f368a42cc MDL-84654 mod_assign: Assignment additional files can be hidden 2025-05-06 13:24:37 +08:00
Huong Nguyen 186284ec1f Merge branch 'MDL-85310-401' of https://github.com/andelacruz/moodle into MOODLE_401_STABLE 2025-05-06 10:32:40 +07:00
Huong Nguyen cdfdfc30d9 Merge branch 'MDL-85226-401' of https://github.com/mackensen/moodle into MOODLE_401_STABLE 2025-05-06 10:30:38 +07:00
AMOS bot 6c95086bc9 Automatically generated installer lang files 2025-05-01 00:07:39 +00:00
Angelia Dela Cruz 45ef55471b MDL-85310 qtype: Renamed duplicate scenario names to fix warning 2025-04-30 13:36:18 +08:00
AMOS bot ac270cb26f Automatically generated installer lang files 2025-04-23 00:07:39 +00:00
AMOS bot b2ba6624ce Automatically generated installer lang files 2025-04-20 00:08:01 +00:00
AMOS bot b57eb5dd88 Automatically generated installer lang files 2025-04-19 00:07:37 +00:00
AMOS bot 9bf42e7aad Automatically generated installer lang files 2025-04-18 00:07:40 +00:00
Charles Fulton 4318e21563 MDL-85226 gha: Pin mongodb extension version 2025-04-16 15:28:57 -04:00
Jun Pataleta 2a900d6ed9 Moodle release 4.1.18 2025-04-13 09:52:10 +08:00
Huong Nguyen 385721f105 weekly release 4.1.17+ 2025-04-11 22:02:30 +07:00
Huong Nguyen 83c5af5001 Merge branch 'install_401_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_401_STABLE 2025-04-11 22:02:29 +07:00
Sara Arjona 1b995b811d MDL-83994 course: Adapt PHPUnit to lower PHP versions 2025-04-11 19:24:57 +08:00
Huong Nguyen 52ad957d74 MDL-72704 message: Fix fetching for user has already contacted 2025-04-11 17:46:51 +08:00
Paul Holden edb68975d1 MDL-84865 cohort: improve system report validation of parameters. 2025-04-11 12:47:07 +08:00
Jun Pataleta 9d84286479 MDL-72704 message: Additional optimisation tweaks
* Return early if the `userids` params is empty or if the filtered
user IDs end up being empty.
2025-04-11 12:19:28 +08:00
Stevani Andolo 7b8be76dd9 MDL-72704 message: Fix fetching of conversation member info 2025-04-11 12:19:28 +08:00
raortegar c2bcdedbd6 MDL-85104 lib: Add additional check to local URLs params. 2025-04-11 10:22:58 +07:00
James C ca40241461 MDL-83994 course: Respect course_can_delete_section() in AJAX 2025-04-11 13:07:26 +10:00
Paul Holden 5e7ec29c65 MDL-84473 repository_equella: safer unserializing of file references. 2025-04-11 09:52:40 +07:00
Paul Holden 193e6b0fad MDL-84475 repository_dropbox: safer unserializing of file references. 2025-04-11 09:34:44 +07:00
Michael Hawkins b7a8375e65 MDL-85152 filter_tex: Update deny list and slash handling 2025-04-10 14:21:39 +08:00
Paul Holden 10e93d074b MDL-84478 tool_brickfield: verify sesskey prior to course analysis. 2025-04-10 13:07:36 +08:00
Paul Holden 0e6246db29 MDL-84499 block_rss_client: user access checks for viewing feed. 2025-04-10 12:32:57 +08:00
Paul Holden 58efa0fa10 MDL-65356 mod_data: update entry editing/deletion to not leak sesskey. 2025-04-10 10:08:23 +07:00
Paul Holden afc3bfc296 MDL-84750 course: require user be able to view course participants. 2025-04-10 09:31:11 +08:00
Paul Holden 9af8958b03 MDL-84479 tool_usertours: verify sesskey prior to tour duplication. 2025-04-08 16:16:42 +07:00
AMOS bot 896da8f473 Automatically generated installer lang files 2025-04-08 00:07:36 +00:00
Huong Nguyen 90fa1113f2 weekly release 4.1.17+ 2025-04-04 22:20:22 +07:00
Jun Pataleta aaf985001e Merge branch 'MDL-84907-401' of https://github.com/HuongNV13/moodle into MOODLE_401_STABLE 2025-04-04 11:31:11 +08:00
Shamim Rezaie 9b81be597c Merge branch 'MDL-85000-401' of https://github.com/HuongNV13/moodle into MOODLE_401_STABLE 2025-04-03 06:14:42 +11:00
Huong Nguyen 998f6ce060 MDL-85000 media_videojs: Enable Ogv.JS Tech for all browsers 2025-04-02 13:10:37 +07:00
Sara Arjona 881ad82c44 weekly release 4.1.17+ 2025-03-28 12:48:32 +01:00
Sara Arjona 5c163fff89 Merge branch 'install_401_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_401_STABLE 2025-03-28 12:48:31 +01:00
Paul Holden 1ac64c87f9 MDL-85048 core: define mapping for new America/Coyhaique zone.
Follows same process as other timezone additions in d5ec3594.
2025-03-28 08:58:49 +00:00
Huong Nguyen ce35013ebf Merge branch 'MDL-85048-401' of https://github.com/lameze/moodle into MOODLE_401_STABLE 2025-03-28 15:01:38 +07:00
Simey Lameze 8de6b2d6f4 MDL-85048 phpunit: add america/coyhaique timezone 2025-03-28 15:00:39 +08:00
AMOS bot 81fc25e7fa Automatically generated installer lang files 2025-03-28 00:08:28 +00:00
Huong Nguyen 5efe1cbf3c weekly release 4.1.17+ 2025-03-26 08:56:13 +07:00
Huong Nguyen 5deff67cea Merge branch 'install_401_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_401_STABLE 2025-03-26 08:56:12 +07:00
Huong Nguyen 14cb950ff3 Merge branch 'MDL-83160-401' of https://github.com/mickhawkins/moodle into MOODLE_401_STABLE 2025-03-24 14:55:08 +07:00
AMOS bot 374028b418 Automatically generated installer lang files 2025-03-22 00:07:37 +00:00
Huong Nguyen f0133eaae1 weekly release 4.1.17+ 2025-03-21 22:04:26 +07:00
Huong Nguyen 18998a6051 Merge branch 'install_401_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_401_STABLE 2025-03-21 22:04:25 +07:00
AMOS bot 00b94dec34 Automatically generated installer lang files 2025-03-21 00:07:37 +00:00
Huong Nguyen 71269d9c8f MDL-84905 Behat: Fix I set the following administration settings failure 2025-03-19 09:17:38 +07:00
Huong Nguyen 61c44c40d8 MDL-84907 backup: Fix invalid CTRL characters regex 2025-03-18 14:13:52 +07:00
Michael Hawkins 4629c45815 MDL-83160 core: Update security.txt expiry 2025-03-13 20:36:10 +08:00
79 changed files with 1124 additions and 166 deletions
+1
View File
@@ -65,6 +65,7 @@ jobs:
extensions:
db: mysqli
- os: ubuntu-22.04
extensions: mongodb-1.19.4
php: 8.1
db: pgsql
+197
View File
@@ -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>
+2
View File
@@ -82,6 +82,8 @@ class behat_admin extends behat_base {
$this->execute('behat_forms::i_set_the_field_with_xpath_to', [$fieldxpath, $value]);
$this->execute("behat_general::i_click_on", [get_string('savechanges'), 'button']);
// Wait for the page to be redirected.
$this->execute("behat_general::i_wait_to_be_redirected");
}
}
+4
View File
@@ -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');
+4
View File
@@ -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');
+4
View File
@@ -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.
+4
View File
@@ -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');
+1
View File
@@ -87,6 +87,7 @@ $action = optional_param('action', '', PARAM_ALPHA);
// Handle any single operation actions.
if ($action == 'requestanalysis') {
if ($courseid != 0) {
require_sesskey();
scheduler::request_course_analysis($courseid);
if ($courseid == SITEID) {
redirect(accessibility::get_plugin_url());
@@ -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"
+4
View File
@@ -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');
+4
View File
@@ -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');
+2 -3
View File
@@ -218,12 +218,11 @@ class helper {
* @return moodle_url The URL.
*/
public static function get_duplicate_tour_link($tourid) {
$link = new \moodle_url('/admin/tool/usertours/configure.php', [
return new \moodle_url('/admin/tool/usertours/configure.php', [
'action' => manager::ACTION_DUPLICATETOUR,
'id' => $tourid,
'sesskey' => sesskey(),
]);
return $link;
}
/**
+2 -1
View File
@@ -512,8 +512,9 @@ class manager {
* @param int $tourid The ID of the tour to duplicate.
*/
protected function duplicate_tour($tourid) {
$tour = helper::get_tour($tourid);
require_sesskey();
$tour = helper::get_tour($tourid);
$export = $tour->to_record();
// Remove the id.
unset($export->id);
@@ -168,8 +168,7 @@ abstract class restore_qtype_plugin extends restore_plugin {
$this->questionanswercacheid = $newquestionid;
// Cache all cleaned answers for a simple text match.
foreach ($answers as $answer) {
// MDL-30018: Clean in the same way as {@link xml_writer::xml_safe_utf8()}.
$clean = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is','', $answer->answer); // Clean CTRL chars.
$clean = core_text::trim_ctrl_chars($answer->answer); // Clean CTRL chars.
$clean = preg_replace("/\r\n|\r/", "\n", $clean); // Normalize line ending.
$this->questionanswercache[$clean] = $answer->id;
}
+1 -2
View File
@@ -5154,8 +5154,7 @@ class restore_create_categories_and_questions extends restore_structure_step {
$potentialhints = $DB->get_records('question_hints',
array('questionid' => $newquestionid), '', 'id, hint');
foreach ($potentialhints as $potentialhint) {
// Clean in the same way than {@link xml_writer::xml_safe_utf8()}.
$cleanhint = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is','', $potentialhint->hint); // Clean CTRL chars.
$cleanhint = core_text::trim_ctrl_chars($potentialhint->hint); // Clean CTRL chars.
$cleanhint = preg_replace("/\r\n|\r/", "\n", $cleanhint); // Normalize line ending.
if ($cleanhint === $data->hint) {
$newitemid = $data->id;
+3 -3
View File
@@ -253,14 +253,14 @@ class xml_writer {
}
/**
* Perform some UTF-8 cleaning, stripping the control chars (\x0-\x1f)
* but tabs (\x9), newlines (\xa) and returns (\xd). The delete control
* Perform some UTF-8 cleaning, stripping the control chars (\x00-\x1f)
* but tabs (\x09), newlines (\xa) and returns (\xd). The delete control
* char (\x7f) is also included. All them are forbiden in XML 1.0 specs.
* The expression below seems to be UTF-8 safe too because it simply
* ignores the rest of characters. Also normalize linefeeds and return chars.
*/
protected function xml_safe_utf8($content) {
$content = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is', '', $content ?? ''); // clean CTRL chars.
$content = core_text::trim_ctrl_chars($content ?? '');
$content = preg_replace("/\r\n|\r/", "\n", $content); // Normalize line&return=>line
return fix_utf8($content);
}
+1 -1
View File
@@ -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}}
+16 -6
View File
@@ -15,7 +15,7 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Script to let a user edit the properties of a particular RSS feed.
* Script to let a user view the output of a particular RSS feed.
*
* @package block_rss_client
* @copyright 2009 Tim Hunt
@@ -26,9 +26,6 @@ require_once(__DIR__ . '/../../config.php');
require_once($CFG->libdir .'/simplepie/moodle_simplepie.php');
require_login();
if (isguestuser()) {
throw new \moodle_exception('guestsarenotallowed');
}
$returnurl = optional_param('returnurl', '', PARAM_LOCALURL);
$courseid = optional_param('courseid', 0, PARAM_INT);
@@ -46,6 +43,11 @@ if ($courseid) {
$PAGE->set_context($context);
}
$managesharedfeeds = has_capability('block/rss_client:manageanyfeeds', $context);
if (!$managesharedfeeds) {
require_capability('block/rss_client:manageownfeeds', $context);
}
$urlparams = array('rssid' => $rssid);
if ($courseid) {
$urlparams['courseid'] = $courseid;
@@ -56,10 +58,18 @@ if ($returnurl) {
$PAGE->set_url('/blocks/rss_client/viewfeed.php', $urlparams);
$PAGE->set_pagelayout('popup');
$rssrecord = $DB->get_record('block_rss_client', array('id' => $rssid), '*', MUST_EXIST);
if ($managesharedfeeds) {
$select = 'id = :id AND (userid = :userid OR shared = 1)';
} else {
$select = 'id = :id AND userid = :userid';
}
$rssrecord = $DB->get_record_select('block_rss_client', $select, [
'id' => $rssid,
'userid' => $USER->id,
], '*', MUST_EXIST);
$rss = new moodle_simplepie($rssrecord->url);
if ($rss->error()) {
debugging($rss->error());
throw new \moodle_exception('errorfetchingrssfeed');
@@ -55,11 +55,11 @@ class cohorts extends system_report {
"{$entitymainalias}.component");
// Check if report needs to show a specific category.
$contextid = $this->get_parameter('contextid', 0, PARAM_INT);
$showall = $this->get_parameter('showall', true, PARAM_BOOL);
if (!$showall) {
if (!$this->get_context() instanceof context_system || !$this->get_parameter('showall', false, PARAM_BOOL)) {
$paramcontextid = database::generate_param_name();
$this->add_base_condition_sql("{$entitymainalias}.contextid = :$paramcontextid", [$paramcontextid => $contextid]);
$this->add_base_condition_sql("{$entitymainalias}.contextid = :{$paramcontextid}", [
$paramcontextid => $this->get_context()->id,
]);
}
// Now we can call our helper methods to add the content we want to include in the report.
@@ -77,14 +77,7 @@ class cohorts extends system_report {
* @return bool
*/
protected function can_view(): bool {
$contextid = $this->get_parameter('contextid', 0, PARAM_INT);
if ($contextid) {
$context = context::instance_by_id($contextid, MUST_EXIST);
} else {
$context = context_system::instance();
}
return has_any_capability(['moodle/cohort:manage', 'moodle/cohort:view'], $context);
return has_any_capability(['moodle/cohort:manage', 'moodle/cohort:view'], $this->get_context());
}
/**
@@ -98,10 +91,8 @@ class cohorts extends system_report {
public function add_columns(cohort $cohortentity): void {
$entitymainalias = $cohortentity->get_table_alias('cohort');
$showall = $this->get_parameter('showall', false, PARAM_BOOL);
// Category column. An extra callback is appended in order to extend the current column formatting.
if ($showall) {
if ($this->get_context() instanceof context_system && $this->get_parameter('showall', false, PARAM_BOOL)) {
$this->add_column_from_entity('cohort:context')
->add_callback(static function(string $value, stdClass $cohort): string {
$context = context::instance_by_id($cohort->contextid);
@@ -195,10 +186,11 @@ class cohorts extends system_report {
*/
protected function add_actions(): void {
$contextid = $this->get_parameter('contextid', 0, PARAM_INT);
$showall = $this->get_parameter('showall', true, PARAM_BOOL);
$returnurl = (new moodle_url('/cohort/index.php',
['id' => ':id', 'contextid' => $contextid, 'showall' => $showall]))->out(false);
$returnurl = (new moodle_url('/cohort/index.php', [
'id' => ':id',
'contextid' => $this->get_context()->id,
'showall' => $this->get_parameter('showall', false, PARAM_BOOL),
]))->out(false);
// Hide action. It will be only shown if the property 'visible' is true and user has 'moodle/cohort:manage' capabillity.
$this->add_action((new action(
+1 -2
View File
@@ -104,8 +104,7 @@ if ($editcontrols = cohort_edit_controls($context, $baseurl)) {
echo $OUTPUT->render($editcontrols);
}
$reportparams = ['contextid' => $context->id, 'showall' => $showall];
$report = system_report_factory::create(cohorts::class, $context, '', '', 0, $reportparams);
$report = system_report_factory::create(cohorts::class, $context, '', '', 0, ['showall' => $showall]);
// Check if it needs to search by name.
if (!empty($searchquery)) {
+13 -7
View File
@@ -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();
}
@@ -4177,22 +4177,28 @@ class core_course_external extends external_api {
* @param int $groupid Group id from which the users will be obtained
* @param bool $onlyactive Whether to return only the active enrolled users or all enrolled users in the course.
* @return array List of users
* @throws invalid_parameter_exception
*/
public static function get_enrolled_users_by_cmid(int $cmid, int $groupid = 0, bool $onlyactive = false) {
global $PAGE;
global $PAGE;
$warnings = [];
self::validate_parameters(self::get_enrolled_users_by_cmid_parameters(), [
'cmid' => $cmid,
'groupid' => $groupid,
'onlyactive' => $onlyactive,
[
'cmid' => $cmid,
'groupid' => $groupid,
'onlyactive' => $onlyactive,
] = self::validate_parameters(self::get_enrolled_users_by_cmid_parameters(), [
'cmid' => $cmid,
'groupid' => $groupid,
'onlyactive' => $onlyactive,
]);
list($course, $cm) = get_course_and_cm_from_cmid($cmid);
$coursecontext = context_course::instance($course->id);
self::validate_context($coursecontext);
course_require_view_participants($coursecontext);
$enrolledusers = get_enrolled_users($coursecontext, '', $groupid, 'u.*', null, 0, 0, $onlyactive);
$users = array_map(function ($user) use ($PAGE) {
+3
View File
@@ -242,6 +242,9 @@ class stateactions {
foreach ($ids as $sectionid) {
$section = $modinfo->get_section_info_by_id($sectionid, MUST_EXIST);
if (!course_can_delete_section($course, $section)) {
continue;
}
// Send all activity deletions.
if (!empty($modinfo->sections[$section->section])) {
foreach ($modinfo->sections[$section->section] as $modnumber) {
+120
View File
@@ -0,0 +1,120 @@
<?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/>.
declare(strict_types=1);
namespace core_courseformat\external;
defined('MOODLE_INTERNAL') || die();
use core_courseformat\stateactions;
use core_courseformat\stateupdates;
global $CFG;
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
/**
* Tests for the delete section test class.
*
* @package core_courseformat
* @copyright 2025 Laurent David <laurent.david@moodle.com>
* @category test
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \core_courseformat\stateactions
*/
final class delete_section_test extends \externallib_advanced_testcase {
/**
* Setup to ensure that fixtures are loaded.
*/
public static function setupBeforeClass(): void { // phpcs:ignore
global $CFG;
require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittestdelete.php');
}
/**
* Test the webservice can execute the section_delete action.
*
* @covers ::section_delete
* @dataProvider section_delete_provider
* @param int $sectionum
* @param string $format
* @param array $formatoptions
* @param int $expectedsectionum
*
* @throws \moodle_exception
*/
public function test_delete_section(int $sectionum, string $format, array $formatoptions, int $expectedsectionum): void {
$this->resetAfterTest();
$course =
$this->getDataGenerator()->create_course(array_merge(
['numsections' => $sectionum, 'format' => $format],
$formatoptions,
));
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
// Execute the method.
$courseformat = course_get_format($course->id);
$updates = new stateupdates($courseformat);
$modinfo = get_fast_modinfo($course);
$sections = $modinfo->get_section_info_all();
$sectionsid = array_map(function ($section) {
return $section->id;
}, $sections);
$actions = new stateactions();
$this->setUser($teacher);
$actions->section_delete(
$updates,
$course,
$sectionsid
);
// Check result.
$modinfo = get_fast_modinfo($course);
$sections = $modinfo->get_section_info_all();
$this->assertCount($expectedsectionum, $sections);
if ($format == 'theunittestdelete') {
$this->assertDebuggingCalled();
}
}
/**
* Data provider for the test_delete_section method.
*
* @return array
*/
public static function section_delete_provider(): array {
return [
'format topic' => [
'sectionum' => 1,
'format' => 'topics',
'formatoptions' => [],
'expectedsectionum' => 1,
],
'format theunittestdelete' => [
'sectionum' => 1,
'format' => 'theunittestdelete',
'formatoptions' => [],
'expectedsectionum' => 2,
],
'format theunittestdelete can delete' => [
'sectionum' => 1,
'format' => 'theunittestdelete',
'formatoptions' => ['can_delete_sections' => true],
'expectedsectionum' => 1,
],
];
}
}
@@ -0,0 +1,70 @@
<?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();
require_once(__DIR__ . '/format_theunittest.php');
/**
* Fixture for fake course format testing course format API.
*
* @package core_courseformat
* @copyright 2025 Laurent David <laurent.david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class format_theunittestdelete extends format_theunittest {
/**
* Definitions of the additional options that format uses
*
* @param bool $foreditform
* @return array of options
*/
public function course_format_options($foreditform = false) {
static $courseformatoptions = false;
if ($courseformatoptions === false) {
$courseformatoptions = parent::course_format_options(true);
$courseformatoptionsadditional = [
'can_delete_sections' => [
'default' => false,
'type' => PARAM_BOOL,
],
];
$courseformatoptions = array_merge_recursive($courseformatoptions, $courseformatoptionsadditional);
}
return $courseformatoptions;
}
/**
* Whether this format allows to delete sections
*
* Here for test purpose we just can delete one section every two sections
*
* Do not call this function directly, instead use course_can_delete_section()
*
* @param int|stdClass|section_info $section
* @return bool
*/
public function can_delete_section($section) {
return $this->get_format_options()['can_delete_sections'] ?? false;
}
/**
* Returns true if this course format uses sections
*/
public function uses_sections() {
return true;
}
}
+8 -1
View File
@@ -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);
+12
View File
@@ -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);
@@ -3742,6 +3748,12 @@ final class externallib_test extends externallib_advanced_testcase {
$this->assertEquals(2, count($users['users']));
$this->assertEquals($expectedusers, $users);
// Prohibit the capability for viewing course participants.
$this->unassignUserCapability('moodle/course:viewparticipants', null, null, $course1->id);
$this->expectException(required_capability_exception::class);
$this->expectExceptionMessage('Sorry, but you do not currently have permissions to do that (View participants)');
core_course_external::get_enrolled_users_by_cmid($forum1->cmid);
}
/**
+10 -1
View File
@@ -84,11 +84,20 @@ function filter_tex_sanitize_formula(string $texexp): string {
'\afterassignment', '\expandafter', '\noexpand', '\special',
'\let', '\futurelet', '\else', '\fi', '\chardef', '\makeatletter', '\afterground',
'\noexpand', '\line', '\mathcode', '\item', '\section', '\mbox', '\declarerobustcommand',
'\ExplSyntaxOn', '\pdffiledump',
'\ExplSyntaxOn', '\pdffiledump', '\mathtex',
];
$allowlist = ['inputenc'];
// Add encoded backslash (&#92;) versions of backslashed items to deny list.
$encodedslashdenylist = array_map(function($value) {
$encoded = str_replace('\\', '&#92;', $value);
// Return an encoded slash version if a slash is found, otherwise null so we can filter it off.
return $encoded != $value ? $encoded : null;
}, $denylist);
$encodedslashdenylist = array_filter($encodedslashdenylist);
$denylist = array_merge($denylist, $encodedslashdenylist);
// Prepare the denylist for regular expression.
$denylist = array_map(function($value){
return '/' . preg_quote($value, '/') . '/i';
+36
View File
@@ -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';
+1 -1
View File
@@ -71,7 +71,7 @@ $string['pathserrcreatedataroot'] = 'Instalatzaileak ezin du datu-direktorioa ({
$string['pathshead'] = 'Egiaztatu bideak';
$string['pathsrodataroot'] = 'Dataroot direktorioa ez da idazteko modukoa.';
$string['pathsroparentdataroot'] = 'Goragoko direktorioa ({$a->parent}) ez da idazteko modukoa. Instalatzaileak ezin du datu-direktorioa ({$a->dataroot}) sortu.';
$string['pathssubadmindir'] = 'Web ostalari gutxi batzuk /admin URL berezi gisa erabiltzen dute kontrol-panel edo antzekora sarbidea emateko. Zoritxarrez, honek Moodleren kudeatze-orrien lehenetsitako kokapenarekin gatazka sortzen du. Hau konpondu dezakezu zure instalazioko admin direktorioa berrizendatuz, eta izen berria hemen sartuta. Adibidez <em>moodleadmin</em>. Honek Moodleko admin estekak konponduko du.';
$string['pathssubadmindir'] = 'Web ostalari gutxi batzuk /admin URL berezi gisa erabiltzen dute kontrol-panel edo antzekora sarbidea emateko. Zoritxarrez, honek Moodleko kudeatze-orrien lehenetsitako kokapenarekin gatazka sortzen du. Hau konpondu dezakezu zure instalazioko admin direktorioa berrizendatuz, eta izen berria hemen sartuta. Adibidez <em>moodleadmin</em>. Honek Moodleko admin estekak konponduko du.';
$string['pathssubdataroot'] = '<p>Moodlek erabiltzaileek igotako fitxategien edukiak bilduko dituen direktorio bat.</p>
<p>Direktorio honetan web-zerbitzariaren erabiltzaileak irakurtzeko eta idazteko baimena izan beharko ditu (normalean \'www-data\', \'nobody\', edo \'apache\').</p>
<p>Ez litzateke web bidez eskuragarri egon beharko.</p>
+1 -1
View File
@@ -30,7 +30,7 @@
defined('MOODLE_INTERNAL') || die();
$string['language'] = 'Hizkuntza';
$string['moodlelogo'] = 'Moodleren logoa';
$string['moodlelogo'] = 'Moodleko logoa';
$string['next'] = 'Hurrengoa';
$string['previous'] = 'Aurrekoa';
$string['reload'] = 'Berriz kargatu';
+2 -2
View File
@@ -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)';
+52
View File
@@ -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';
+4 -4
View File
@@ -84,10 +84,10 @@ $string['phpversionhelp'] = '<p>Moodleには少なくとも5.6.5または7.1のP
<p>現在、あなたはバージョン {$a} を動作させています。</p>
<p>PHPをアップグレードするか新しいバージョンのPHPがインストールされているホストに移動する必要があります。</p>';
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
$string['welcomep20'] = 'インストール正常完了して、あなたのコンピュータで <strong>{$a->packname} {$a->packversion}</strong> パッケージが起動されたため、このページが表示されています。おめでとうございます!';
$string['welcomep30'] = 'このリリース <strong>{$a->installername}</strong> には<strong>Moodle</strong>で環境を作成するアプリケーションが含まれています。すなわち:';
$string['welcomep40'] = 'パッケージには <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong> も含まれています。';
$string['welcomep50'] = 'このパッケージ内すべてのアプリケーションの使用は個別のライセンスによって規定されています。全体の<strong>{$a->installername}</strong>パッケージは<a href="https://www.opensource.org/docs/definition_plain.html">オープンソース</a>であり、<a href="https://www.gnu.org/copyleft/gpl.html">GPL</a>ライセンスの下で配布されています。';
$string['welcomep20'] = 'インストール正常完了、あなたのコンピュータで<strong>{$a->packname} {$a->packversion}</strong>パッケージが起動されたため、このページが表示されています。おめでとうございます!';
$string['welcomep30'] = 'このリリース<strong>{$a->installername}</strong>には<strong>Moodle</strong>で環境を作成するアプリケーションが含まれています。すなわち:';
$string['welcomep40'] = 'パッケージには<strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>も含まれています。';
$string['welcomep50'] = 'このパッケージ内すべてのアプリケーションの使用は個別のライセンスによ規定されています。全体の<strong>{$a->installername}</strong>パッケージは<a href="https://www.opensource.org/docs/definition_plain.html">オープンソース</a>であり、<a href="https://www.gnu.org/copyleft/gpl.html">GPL</a>ライセンスの下で配布されています。';
$string['welcomep60'] = '次からのページはあなたのコンピュータに<strong>Moodle</strong>を簡単に設定およびセットアップする手順にしたがって進みます。あなたはデフォルトの設定を使用することも、必要に応じて任意で設定を変更することもできます。';
$string['welcomep70'] = '<strong>Moodle</strong>のセットアップを続けるには「次へ」ボタンをクリックしてください。';
$string['wwwroot'] = 'ウェブアドレス';
+36
View File
@@ -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'] = 'Fiteny';
$string['moodlelogo'] = 'Sarim-pamantarana ny Moodle';
$string['next'] = 'Manaraka';
$string['previous'] = 'Teo aloha';
$string['reload'] = 'Avereno';
+33
View File
@@ -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)';
+1
View File
@@ -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';
+1
View File
@@ -114,6 +114,7 @@ $string['america/chicago'] = 'America/Chicago';
$string['america/chihuahua'] = 'America/Chihuahua';
$string['america/ciudad_juarez'] = 'America/Ciudad_Juarez';
$string['america/costa_rica'] = 'America/Costa_Rica';
$string['america/coyhaique'] = 'America/Coyhaique';
$string['america/creston'] = 'America/Creston';
$string['america/cuiaba'] = 'America/Cuiaba';
$string['america/curacao'] = 'America/Curacao';
+2 -1
View File
@@ -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);
+4
View File
@@ -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
-1
View File
@@ -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;
}
}
+5
View File
@@ -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;
+1
View File
@@ -756,6 +756,7 @@ class core_date {
if ($intltz === null) {
// Where intl doesn't know about a recent timezone, map it to an equivalent existing zone.
$intltzname = strtr($tz->getName(), [
'America/Coyhaique' => 'America/Santiago',
'America/Ciudad_Juarez' => 'America/Denver',
'America/Nuuk' => 'America/Godthab',
'Europe/Kyiv' => 'Europe/Kiev',
+2 -2
View File
@@ -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;
}
+51 -2
View File
@@ -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);
}
}
+12
View File
@@ -676,4 +676,16 @@ class core_text {
return mb_convert_case($text, MB_CASE_TITLE, 'UTF-8');
}
/**
* Trims control characters out of a string.
* Example: (\x00-\x1f) and (\x7f)
*
* @param string $text Input string
* @return string Cleaned string value
*/
public static function trim_ctrl_chars(string $text): string {
// Remove control characters text.
return preg_replace('/[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]/i', '', $text);
}
}
@@ -29,8 +29,11 @@ Feature: Add media to Atto
And the field "Enter name" matches value "moodle-logo.webm"
And I wait until the page is ready
And I click on "Insert media" "button"
When I click on "Save changes" "button"
Then "//a[. = 'moodle-logo.webm']" "xpath_element" should exist
And I click on "Show more buttons" "button"
When I click on "HTML" "button"
Then I should see "moodle-logo.webm\">moodle-logo.webm</a>"
And I click on "Save changes" "button"
And ".video-js" "css_element" should exist
@javascript @atto_media_video
Scenario: Insert some media as a plain video
+13
View File
@@ -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'];
+3 -3
View File
@@ -1105,10 +1105,10 @@ function clean_param($param, $type) {
} else if (preg_match('/^' . preg_quote($CFG->wwwroot . '/', '/') . '/i', $param)) {
// Absolute, and matches our wwwroot.
} else {
// Relative - let's make sure there are no tricks.
if (validateUrlSyntax('/' . $param, 's-u-P-a-p-f+q?r?') && !preg_match('/javascript:/i', $param)) {
// Looks ok.
if (validateUrlSyntax('/' . $param, 's-u-P-a-p-f+q?r?') &&
!preg_match('/javascript(?:.*\/{2,})?:/i', rawurldecode($param))) {
// Valid relative local URL.
} else {
$param = '';
}
-18
View File
@@ -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');
}
+4
View File
@@ -1,6 +1,10 @@
This files describes API changes in core libraries and APIs,
information provided here is intended especially for developers.
=== 4.1.18 ===
* A new method, `core_text::trim_ctrl_chars()`, has been introduced to clean control characters from text.
This ensures cleaner input handling and prevents issues caused by invisible or non-printable characters
=== 4.1.12 ===
* Added the ability for unit tests to autoload classes in the `\[component]\tests\` namespace from the `[path/to/component]/tests/classes` directory.
+1 -2
View File
@@ -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');
+1
View File
@@ -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 = '';
+11 -2
View File
@@ -308,8 +308,17 @@ class media_videojs_plugin extends core_media_player_native {
// Ogv.JS Tech.
$this->ogvtech = false;
if (in_array($ext, $this->ogvsupportedextensions) &&
(core_useragent::is_safari() || core_useragent::is_ios())) {
/* The following browsers do not support OGV natively:
- Chrome (since version 120)
- Edge (since version 122)
- Safari
- Safari on iOS
- Firefox (since version 130)
- Opera (since version 106)
Refer: https://caniuse.com/ogv.
We need to enable Ogv.JS Tech plugin for all browsers that do not support OGV natively.
*/
if (in_array($ext, $this->ogvsupportedextensions)) {
$this->ogvtech = true;
$result[] = $url;
continue;
+30 -2
View File
@@ -3388,7 +3388,7 @@ class core_message_external extends external_api {
bool $includecontactrequests = false,
bool $includeprivacyinfo = false
) {
global $CFG, $USER;
global $CFG, $USER, $DB;
// All the business logic checks that really shouldn't be in here.
if (empty($CFG->messaging)) {
@@ -3408,9 +3408,37 @@ class core_message_external extends external_api {
throw new moodle_exception('You do not have permission to perform this action.');
}
// Return early if no userids are provided.
if (empty($params['userids'])) {
return [];
}
// Filter the user IDs, removing the IDs of the users that the current user cannot view.
require_once($CFG->dirroot . '/user/lib.php');
$userfieldsapi = \core_user\fields::for_userpic()->including('username', 'deleted');
$userfields = $userfieldsapi->get_sql('', false, '', '', false)->selects;
$users = $DB->get_records_list('user', 'id', $userids, '', $userfields, 0, 100);
$filteredids = array_filter($params['userids'], function($userid) use ($users, $params) {
$targetuser = $users[$userid];
// Check if the user has the contact already.
$iscontact = \core_message\api::is_contact($params['referenceuserid'], $userid);
if ($iscontact) {
// User is a contact, so we can return the info for this user.
return true;
} else {
// User is not a contact, so we need to check if the user is allowed to see the profile or not.
return user_can_view_profile($targetuser);
}
});
// Return early if no user IDs are left after filtering.
if (empty($filteredids)) {
return [];
}
return \core_message\helper::get_member_info(
$params['referenceuserid'],
$params['userids'],
$filteredids,
$params['includecontactrequests'],
$params['includeprivacyinfo']
);
@@ -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. |
+11 -5
View File
@@ -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;
}
+3 -1
View File
@@ -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.';
+3 -1
View File
@@ -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(),
]
);
}
-2
View File
@@ -745,7 +745,6 @@ class template {
$editurl = new moodle_url('/mod/data/edit.php', $this->baseurl->params());
$editurl->params([
'rid' => $entry->id,
'sesskey' => sesskey(),
'backto' => urlencode($backurl->out(false))
]);
@@ -758,7 +757,6 @@ class template {
// Delete entry.
$deleteurl = new moodle_url($this->baseurl, [
'delete' => $entry->id,
'sesskey' => sesskey(),
'mode' => 'single',
]);
-5
View File
@@ -80,11 +80,6 @@ if ($manager->can_manage_templates()) {
}
}
if ($rid) {
// When editing an existing record, we require the session key.
require_sesskey();
}
// Get Group information for permission testing and record creation.
$currentgroup = groups_get_activity_group($cm);
$groupmode = groups_get_activity_groupmode($cm);
+2 -2
View File
@@ -393,7 +393,7 @@ final class template_test extends \advanced_testcase {
],
'Teacher actionsmenu tag with default options' => [
'templatecontent' => 'Some ##actionsmenu## tag',
'expected' => '|Some .*edit.*{entryid}.*sesskey.*Edit.* .*delete.*{entryid}.*sesskey.*Delete.* tag|',
'expected' => '|Some .*edit.*{entryid}.*Edit.* .*delete.*{entryid}.*Delete.* tag|',
'rolename' => 'editingteacher',
],
'Teacher actionsmenu tag with default options (check Show more is not there)' => [
@@ -689,7 +689,7 @@ final class template_test extends \advanced_testcase {
],
'Student actionsmenu tag with default options' => [
'templatecontent' => 'Some ##actionsmenu## tag',
'expected' => '|Some .*edit.*{entryid}.*sesskey.*Edit.* .*delete.*{entryid}.*sesskey.*Delete.* tag|',
'expected' => '|Some .*edit.*{entryid}.*Edit.* .*delete.*{entryid}.*Delete.* tag|',
'rolename' => 'student',
],
'Student actionsmenu tag with default options (check Show more is not there)' => [
+10 -5
View File
@@ -243,7 +243,7 @@ if (!empty(trim($search))) {
$PAGE->set_title(implode(moodle_page::TITLE_SEPARATOR, $titleparts));
$PAGE->set_heading($course->fullname);
$PAGE->force_settings_menu(true);
if ($delete && confirm_sesskey() && (data_user_can_manage_entry($delete, $data, $context))) {
if ($delete && data_user_can_manage_entry($delete, $data, $context)) {
$PAGE->activityheader->disable();
}
@@ -284,8 +284,10 @@ if ($data->intro and empty($page) and empty($record) and $mode != 'single') {
/// Delete any requested records
if ($delete && confirm_sesskey() && (data_user_can_manage_entry($delete, $data, $context))) {
if ($delete && data_user_can_manage_entry($delete, $data, $context)) {
if ($confirm) {
require_sesskey();
if (data_delete_record($delete, $data, $course->id, $cm->id)) {
echo $OUTPUT->notification(get_string('recorddeleted','data'), 'notifysuccess');
}
@@ -320,8 +322,10 @@ if ($serialdelete) {
$multidelete = json_decode($serialdelete);
}
if ($multidelete && confirm_sesskey() && $canmanageentries) {
if ($confirm = optional_param('confirm', 0, PARAM_INT)) {
if ($multidelete && $canmanageentries) {
if ($confirm) {
require_sesskey();
foreach ($multidelete as $value) {
data_delete_record($value, $data, $course->id, $cm->id);
}
@@ -369,7 +373,8 @@ if ($showactivity) {
// Approve or disapprove any requested records
$approvecap = has_capability('mod/data:approve', $context);
if (($approve || $disapprove) && confirm_sesskey() && $approvecap) {
if (($approve || $disapprove) && $approvecap) {
require_sesskey();
$newapproved = $approve ? true : false;
$recordid = $newapproved ? $approve : $disapprove;
if ($approverecord = $DB->get_record('data_records', array('id' => $recordid))) { // Need to check this is valid
+99 -10
View File
@@ -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 |
@@ -157,13 +157,10 @@ class restore_qtype_match_plugin extends restore_qtype_plugin {
$this->questionsubcacheid = $newquestionid;
// Cache all cleaned answers and questiontext.
foreach ($potentialsubs as $potentialsub) {
// Clean in the same way than {@link xml_writer::xml_safe_utf8()}.
$cleanquestion = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is',
'', $potentialsub->questiontext); // Clean CTRL chars.
$cleanquestion = core_text::trim_ctrl_chars($potentialsub->questiontext); // Clean CTRL chars.
$cleanquestion = preg_replace("/\r\n|\r/", "\n", $cleanquestion); // Normalize line ending.
$cleananswer = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is',
'', $potentialsub->answertext); // Clean CTRL chars.
$cleananswer = core_text::trim_ctrl_chars($potentialsub->answertext); // Clean CTRL chars.
$cleananswer = preg_replace("/\r\n|\r/", "\n", $cleananswer); // Normalize line ending.
$this->questionsubcache[$cleanquestion][$cleananswer] = $potentialsub->id;
@@ -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"
+5 -5
View File
@@ -111,7 +111,7 @@ class repository_dropbox extends repository {
*/
public function get_reference_details($reference, $filestatus = 0) {
global $USER;
$ref = unserialize($reference);
$ref = unserialize_object($reference);
$detailsprefix = $this->get_name();
if (isset($ref->userid) && $ref->userid != $USER->id && isset($ref->username)) {
$detailsprefix .= ' ('.$ref->username.')';
@@ -343,8 +343,8 @@ class repository_dropbox extends repository {
* @return string New serialized reference
*/
protected function fix_old_style_reference($packed) {
$ref = unserialize($packed);
$ref = $this->dropbox->get_file_share_info($ref->path);
$ref = unserialize_object($packed);
$ref = $this->dropbox->get_file_share_info($ref->path ?? '');
if (!$ref || empty($ref->url)) {
// Some error occurred, do not fix reference for now.
return $packed;
@@ -396,10 +396,10 @@ class repository_dropbox extends repository {
* @return object The unpacked reference
*/
protected function unpack_reference($packed) {
$reference = unserialize($packed);
$reference = unserialize_object($packed);
if (empty($reference->url)) {
// The reference is missing some information. Attempt to update it.
return unserialize($this->fix_old_style_reference($packed));
return unserialize_object($this->fix_old_style_reference($packed));
}
return $reference;
+17 -7
View File
@@ -163,6 +163,17 @@ class repository_equella extends repository {
return ($countfailures[$sess] < 3);
}
/**
* Returned unserialized object from base64 encoded file reference data
*
* @param string $reference
* @return stdClass
*/
private function unserialize_reference(string $reference): stdClass {
$decoded = base64_decode($reference);
return unserialize_object($decoded);
}
/**
* Download a file, this function can be overridden by subclass. {@link curl}
*
@@ -175,7 +186,7 @@ class repository_equella extends repository {
*/
public function get_file($reference, $filename = '') {
global $USER, $CFG;
$ref = @unserialize(base64_decode($reference));
$ref = $this->unserialize_reference($reference);
if (!isset($ref->url) || !($url = $this->appendtoken($ref->url))) {
// Occurs when the user isn't known..
return null;
@@ -201,7 +212,7 @@ class repository_equella extends repository {
// if we had several unsuccessfull attempts to connect to server - do not try any more.
return false;
}
$ref = @unserialize(base64_decode($file->get_reference()));
$ref = $this->unserialize_reference($file->get_reference());
if (!isset($ref->url) || !($url = $this->appendtoken($ref->url))) {
// Occurs when the user isn't known..
$file->set_missingsource();
@@ -248,9 +259,8 @@ class repository_equella extends repository {
* @param array $options additional options affecting the file serving
*/
public function send_file($stored_file, $lifetime=null , $filter=0, $forcedownload=false, array $options = null) {
$reference = unserialize(base64_decode($stored_file->get_reference()));
$url = $this->appendtoken($reference->url);
if ($url) {
$ref = $this->unserialize_reference($stored_file->get_reference());
if (isset($ref->url) && $url = $this->appendtoken($ref->url)) {
header('Location: ' . $url);
} else {
send_file_not_found();
@@ -421,8 +431,8 @@ class repository_equella extends repository {
*/
public function get_reference_details($reference, $filestatus = 0) {
if (!$filestatus) {
$ref = unserialize(base64_decode($reference));
return $this->get_name(). ': '. $ref->filename;
$ref = $this->unserialize_reference($reference);
return $this->get_name(). ': '. ($ref->filename ?? '');
} else {
return get_string('lostsource', 'repository', '');
}
+1 -1
View File
@@ -15,4 +15,4 @@ Policy: https://moodledev.io/general/development/process/security
# Expiry date of this document
# This information is considered current and up to date until 3 weeks after the next major Moodle LMS release
Expires: 2025-05-05T01:00:00.000Z
Expires: 2025-10-27T01:00:00.000Z
@@ -89,8 +89,9 @@ class behat_theme_classic_behat_admin extends behat_admin {
}
$this->execute('behat_forms::i_set_the_field_with_xpath_to', [$fieldxpath, $value]);
$this->execute("behat_general::i_click_on", [get_string('savechanges'), 'button']);
// Wait for the page to be redirected.
$this->execute("behat_general::i_wait_to_be_redirected");
}
}
}
+2 -2
View File
@@ -29,9 +29,9 @@
defined('MOODLE_INTERNAL') || die();
$version = 2022112817.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.17 (Build: 20250317)'; // 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.