Compare commits

..

67 Commits

Author SHA1 Message Date
Andrew Nicols 2d41ac46f4 Moodle release 4.2.8 2024-06-07 14:55:25 +08:00
Jun Pataleta afa68b9685 weekly release 4.2.7+ 2024-06-06 22:30:43 +08:00
Jun Pataleta f5c82455b8 NOBUG: Fixed file access permissions 2024-06-06 22:30:43 +08:00
Huong Nguyen 684a375b45 Merge branch 'MDL-81989-402' of https://github.com/lameze/moodle into MOODLE_402_STABLE 2024-06-06 22:21:42 +08:00
meirzamoodle dc84fcfab0 MDL-81890 assign: confirm_sesskey fixed in view_fix_rescaled_null_grades 2024-06-06 22:21:42 +08:00
meirzamoodle 093aedf798 MDL-81890 enrol_lti: Fix sesskey checks 2024-06-06 22:21:41 +08:00
meirzamoodle c1aacb3e28 MDL-81890 course: Fix sesskey checks 2024-06-06 22:21:41 +08:00
meirzamoodle c5b1604e81 MDL-81890 tool_moodlenet: Fix sesskey checks 2024-06-06 22:21:41 +08:00
Paul Holden 647b9dc064 MDL-81778 mod_bigbluebuttonbn: access checks when getting meeting URL. 2024-06-06 22:21:41 +08:00
Stevani Andolo 3048a162cb MDL-81412 calendar: Sanitise calendar event names 2024-06-06 22:21:24 +08:00
lameze b48193c056 MDL-81989 install: remove unnecessary closing div tags 2024-06-05 14:06:16 +08:00
Cameron Ball 3e38c84315 MDL-81774 curl: Strip auth headers when redirecting to different host 2024-06-05 02:03:17 +00:00
Huong Nguyen 5bf91592cd Merge branch 'MDL-81751-MOODLE_402_STABLE' of https://github.com/durzo/moodle into MOODLE_402_STABLE 2024-06-05 08:57:05 +07:00
Huong Nguyen b3b1d731ab Merge branch 'MDL-81955-402' of https://github.com/paulholden/moodle into MOODLE_402_STABLE 2024-06-04 10:34:18 +07:00
Juan Leyva 0caedaab7c MDL-80959 tool_mobile: Use different user keys for QR and auto login 2024-06-04 10:34:20 +08:00
Paul Holden 4461d3f6d6 MDL-81955 assignfeedback_editpdf: ensure ghostscript present in tests. 2024-05-31 07:14:10 +01:00
Ilya Tregubov 742bc470ab Merge branch 'MDL-77685-402' of https://github.com/paulholden/moodle into MOODLE_402_STABLE 2024-05-31 09:09:05 +08:00
Sara Arjona be10cd772b weekly release 4.2.7+ 2024-05-30 17:00:37 +02:00
Andrew Nicols c1aae7d366 MDL-81897 tool_mobile: Coding style fix 2024-05-28 11:44:33 +08:00
Andrew Nicols 19ea4c183d Merge branch 'MDL-81897-402' of https://github.com/jleyva/moodle into MOODLE_402_STABLE 2024-05-28 11:44:28 +08:00
Juan Leyva e5348292f2 MDL-81897 tool_mobile: Force partitioned cookies on inapp browser 2024-05-17 10:22:03 +02:00
Jordan Tomkinson 1d186d7f36 MDL-81751 ddl: new reserved keywords in Aurora MySQL database engine 2024-05-16 13:41:26 +01:00
Huong Nguyen 7ecbcb3c33 weekly release 4.2.7+ 2024-05-16 18:25:40 +07:00
Huong Nguyen a645dc7051 Merge branch 'install_402_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_402_STABLE 2024-05-16 18:25:38 +07:00
Huong Nguyen 00d0724b66 Merge branch 'MDL-79838-402' of https://github.com/andelacruz/moodle into MOODLE_402_STABLE 2024-05-16 09:22:32 +07:00
Angelia Dela Cruz 108fc684de MDL-79838 mod_h5pactivity: Behat for H5P activity grade settings control 2024-05-15 16:19:51 +08:00
Sara Arjona e1b3e14886 Merge branch 'MDL-81805-402' of https://github.com/lameze/moodle into MOODLE_402_STABLE 2024-05-14 17:05:52 +02:00
AMOS bot 80e8737375 Automatically generated installer lang files 2024-05-12 00:14:25 +00:00
Ilya Tregubov 95b5ca87eb weekly release 4.2.7+ 2024-05-10 09:09:45 +08:00
Jun Pataleta ab0792d943 Merge branch 'MDL-80972-402' of https://github.com/lameze/moodle into MOODLE_402_STABLE 2024-05-09 11:42:53 +08:00
info@eWallah.net 022a3d71fd MDL-80972 behat: Behat check_server_status can provide better info. 2024-05-09 11:07:53 +08:00
Huong Nguyen 9dbd03e51d Merge branch 'MDL-80453-402' of https://github.com/andelacruz/moodle into MOODLE_402_STABLE 2024-05-09 09:48:53 +07:00
Huong Nguyen 93b53c3bb1 Merge branch 'MDL-79702-402' of https://github.com/lameze/moodle into MOODLE_402_STABLE 2024-05-09 09:34:25 +07:00
Paul Holden c92cfe9aed MDL-77685 grade: remove Behat specific report rendering hacks.
These changes follow the same approach as that taken elsewhere in
the Gradebook in 3643f48e0f.
2024-05-08 12:29:56 +01:00
Mihail Geshoski 1fc3e502f0 MDL-77685 core_grades: Fix row_column_of_table_should_contain()
Backport from MDL-77632.
2024-05-08 12:29:56 +01:00
Angelia Dela Cruz b49bb7a6b5 MDL-80453 mod_glossary: Behat for different glossary format display 2024-05-08 17:36:07 +08:00
Simey Lameze cb49940156 MDL-81805 behat: remove unnecessary @javascript from tests 2024-05-07 09:10:41 +08:00
Simey Lameze 981272bc2c MDL-79702 behat: improvements and fixes to new tests
This commit adds the handling of tags as string separated by comma
to forum and glossary modules.
2024-05-06 20:02:47 +08:00
Angelia Dela Cruz 3cdf09eb8b MDL-79702 core_tag: Behat for activity tags deletion during course reset 2024-05-06 20:02:47 +08:00
Huong Nguyen 1f3550847b Merge branch 'MDL-80589-402' of https://github.com/lameze/moodle into MOODLE_402_STABLE 2024-05-06 09:37:41 +07:00
Ilya Tregubov 3fdb0f3580 weekly release 4.2.7+ 2024-05-03 08:22:46 +08:00
Ilya Tregubov 4ff803a9f3 Merge branch 'install_402_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_402_STABLE 2024-05-03 08:22:43 +08:00
AMOS bot 43386365b8 Automatically generated installer lang files 2024-05-03 00:10:40 +00:00
Simey Lameze b034a4a24a MDL-80589 behat: fixes and improvements to new tests 2024-05-02 13:34:07 +08:00
Angelia Dela Cruz f0ba09aabe MDL-80589 qtype_numerical: Behat to add, edit, preview numeric question 2024-05-02 13:34:07 +08:00
Huong Nguyen 13aa29cbae Merge branch 'MDL-79851-402' of https://github.com/lameze/moodle into MOODLE_402_STABLE 2024-05-01 16:12:03 +07:00
Angelia Dela Cruz d78d270031 MDL-79851 mod_h5pactivity: Behat for h5p activity access control 2024-05-01 14:26:31 +08:00
Sara Arjona c852ec91ab Merge branch 'MDL-81632-402' of https://github.com/paulholden/moodle into MOODLE_402_STABLE 2024-04-30 15:09:47 +02:00
Huong Nguyen 1f4489ed59 Merge branch 'MDL-79149-402' of https://github.com/paulholden/moodle into MOODLE_402_STABLE 2024-04-30 11:48:45 +07:00
Paul Holden 479da404db MDL-79149 report_participation: more resilient activity access test.
Use the "Forum" module rather than the "Book" module, because the latter
emits two events when a student views it - if these two events happened
either side of a single second, then the report  would count two distinct
views (when it was trying to assert only one).
2024-04-26 16:08:24 +01:00
Sara Arjona c862fc046c Merge branch 'MDL-77445-402' of https://github.com/andelacruz/moodle into MOODLE_402_STABLE 2024-04-26 10:40:45 +02:00
Sara Arjona 7b172f51b9 weekly release 4.2.7+ 2024-04-25 15:43:39 +02:00
Sara Arjona 2936dbdd3f Merge branch 'install_402_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_402_STABLE 2024-04-25 15:43:35 +02:00
Huong Nguyen 3d847c154a MDL-77998 Behat: Fix Behat failure 2024-04-25 10:31:58 +07:00
Paul Holden 1b0d03b87e MDL-81632 block_recentlyaccesseditems: deterministic item ordering.
Behat tests of this functionality could trigger the unlikely (in real
world usage) scenario where a user has an identical "timeaccess" value
for multiple course activities.

This led to random failures in said tests when the DB ordered items in
apparently random order, where the "timeaccess" value was equal.
2024-04-24 20:57:44 +01:00
Sara Arjona d5002415ce Merge branch 'MDL-77998-402' of https://github.com/andelacruz/moodle into MOODLE_402_STABLE 2024-04-24 14:48:11 +02:00
Angelia Dela Cruz 5eef98ee05 MDL-77998 Behat: Add behat coverage for preferences navigation 2024-04-24 19:47:51 +08:00
Angelia Dela Cruz f4220e56eb MDL-77445 Behat: Coverage for activity chooser navigation 2024-04-24 16:41:37 +08:00
Huong Nguyen dda25ae3c8 Merge branch 'MOODLE_402_MDL-81613' of https://github.com/danielneis/moodle into MOODLE_402_STABLE 2024-04-24 15:13:48 +07:00
Huong Nguyen e97accec36 Merge branch 'MDL-80107-402' of https://github.com/andelacruz/moodle into MOODLE_402_STABLE 2024-04-24 08:36:25 +07:00
Sara Arjona d5814b74be Merge branch 'MDL-81209-402' of https://github.com/andrewnicols/moodle into MOODLE_402_STABLE 2024-04-23 15:30:54 +02:00
Sara Arjona ea1bc14949 Merge branch 'MDL-81457-402' of https://github.com/andrewnicols/moodle into MOODLE_402_STABLE 2024-04-23 14:57:45 +02:00
Angelia Dela Cruz 0015c873ce MDL-80107 gradingform: Behat test for defining marking guide 2024-04-23 15:20:01 +08:00
Daniel Neis Araujo a95b757384 MDL-81613 report_log: fix user fullname export for log report 2024-04-22 14:36:42 -03:00
AMOS bot 2d1c685828 Automatically generated installer lang files 2024-04-22 00:10:28 +00:00
Andrew Nicols 25e4619ae7 MDL-81209 core: Unit test should not care about plugin details 2024-04-08 20:54:25 +08:00
Andrew Nicols 341ec5ba81 MDL-81457 core: Do not mark tests as Incomplete when they are wrong
This check was marking a test as Incomplete if there was a bug in any
part of the test, rather than failing the test in such a way that the
test bug may be found.

https://docs.phpunit.de/en/9.6/incomplete-and-skipped-tests.html
2024-04-08 08:46:59 +08:00
45 changed files with 1017 additions and 136 deletions
+2 -2
View File
@@ -432,13 +432,13 @@ class api {
public static function get_qrlogin_key(stdClass $mobilesettings) {
global $USER;
// Delete previous keys.
delete_user_key('tool_mobile', $USER->id);
delete_user_key('tool_mobile/qrlogin', $USER->id);
// Create a new key.
$iprestriction = !empty($mobilesettings->qrsameipcheck) ? getremoteaddr(null) : null;
$qrkeyttl = !empty($mobilesettings->qrkeyttl) ? $mobilesettings->qrkeyttl : self::LOGIN_QR_KEY_TTL;
$validuntil = time() + $qrkeyttl;
return create_user_key('tool_mobile', $USER->id, null, $iprestriction, $validuntil);
return create_user_key('tool_mobile/qrlogin', $USER->id, null, $iprestriction, $validuntil);
}
/**
+2 -2
View File
@@ -648,8 +648,8 @@ class external extends external_api {
api::check_autologin_prerequisites($params['userid']); // Checks https, avoid site admins using this...
// Validate and delete the key.
$key = validate_user_key($params['qrloginkey'], 'tool_mobile', null);
delete_user_key('tool_mobile', $params['userid']);
$key = validate_user_key($params['qrloginkey'], 'tool_mobile/qrlogin', null);
delete_user_key('tool_mobile/qrlogin', $params['userid']);
// Double check key belong to user.
if ($key->userid != $params['userid']) {
@@ -66,7 +66,7 @@ class provider implements
FROM {user_private_key} k
JOIN {user} u ON k.userid = u.id
JOIN {context} ctx ON ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel
WHERE k.userid = :userid AND k.script = 'tool_mobile'";
WHERE k.userid = :userid AND (k.script = 'tool_mobile' OR k.script = 'tool_mobile/qrlogin')";
$params = ['userid' => $userid, 'contextlevel' => CONTEXT_USER];
$contextlist = new contextlist();
$contextlist->add_from_sql($sql, $params);
@@ -88,6 +88,7 @@ class provider implements
// Add users based on userkey.
\core_userkey\privacy\provider::get_user_contexts_with_script($userlist, $context, 'tool_mobile');
\core_userkey\privacy\provider::get_user_contexts_with_script($userlist, $context, 'tool_mobile/qrlogin');
}
/**
@@ -108,6 +109,7 @@ class provider implements
}
// Export associated userkeys.
\core_userkey\privacy\provider::export_userkeys($context, [], 'tool_mobile');
\core_userkey\privacy\provider::export_userkeys($context, [], 'tool_mobile/qrlogin');
}
/**
* Export all user preferences for the plugin.
@@ -138,6 +140,7 @@ class provider implements
$userid = $context->instanceid;
// Delete all the userkeys.
\core_userkey\privacy\provider::delete_userkeys('tool_mobile', $userid);
\core_userkey\privacy\provider::delete_userkeys('tool_mobile/qrlogin', $userid);
}
/**
* Delete all user data for the specified user, in the specified contexts.
@@ -158,6 +161,7 @@ class provider implements
$userid = $context->instanceid;
// Delete all the userkeys.
\core_userkey\privacy\provider::delete_userkeys('tool_mobile', $userid);
\core_userkey\privacy\provider::delete_userkeys('tool_mobile/qrlogin', $userid);
}
/**
@@ -178,5 +182,6 @@ class provider implements
// Delete all the userkeys.
\core_userkey\privacy\provider::delete_userkeys('tool_mobile', $userid);
\core_userkey\privacy\provider::delete_userkeys('tool_mobile/qrlogin', $userid);
}
}
+8
View File
@@ -69,6 +69,14 @@ if (empty($service)) {
throw new moodle_exception('servicenotavailable', 'webservice');
}
// If the user is using the inapp (embedded) browser, we need to set the Secure and Partitioned attributes to the session cookie.
if (\core_useragent::is_moodle_app()) {
\core\session\utility\cookie_helper::add_attributes_to_cookie_response_header(
cookiename: "MoodleSession{$CFG->sessioncookie}",
attributes: ['Secure', 'Partitioned'],
);
}
require_login(0, false);
// Require an active user: not guest, not suspended.
@@ -87,7 +87,8 @@ class provider_test extends \core_privacy\tests\provider_testcase {
$context1 = \context_user::instance($user1->id);
$context2 = \context_user::instance($user2->id);
$key1 = get_user_key('tool_mobile', $user1->id);
$key2 = get_user_key('tool_mobile', $user2->id);
$key2 = get_user_key('tool_mobile/qrlogin', $user1->id);
$key3 = get_user_key('tool_mobile', $user2->id);
// Ensure only user1 is found in context1.
$userlist = new \core_privacy\local\request\userlist($context1, $component);
@@ -175,12 +176,15 @@ class provider_test extends \core_privacy\tests\provider_testcase {
$context1 = \context_user::instance($user1->id);
$context2 = \context_user::instance($user2->id);
$keyvalue1 = get_user_key('tool_mobile', $user1->id);
$keyvalue2 = get_user_key('tool_mobile', $user2->id);
$keyvalue2 = get_user_key('tool_mobile/qrlogin', $user1->id);
$keyvalue3 = get_user_key('tool_mobile', $user2->id);
$key1 = $DB->get_record('user_private_key', ['value' => $keyvalue1]);
// Before deletion, we should have 2 user_private_keys.
// Before deletion, we should have 2 user_private_keys for tool_mobile and one for tool_mobile/qrlogin.
$count = $DB->count_records('user_private_key', ['script' => 'tool_mobile']);
$this->assertEquals(2, $count);
$count = $DB->count_records('user_private_key', ['script' => 'tool_mobile/qrlogin']);
$this->assertEquals(1, $count);
// Ensure deleting wrong user in the user context does nothing.
$approveduserids = [$user2->id];
@@ -198,6 +202,8 @@ class provider_test extends \core_privacy\tests\provider_testcase {
// Ensure only user1's data is deleted, user2's remains.
$count = $DB->count_records('user_private_key', ['script' => 'tool_mobile']);
$this->assertEquals(1, $count);
$count = $DB->count_records('user_private_key', ['script' => 'tool_mobile/qrlogin']);
$this->assertEquals(0, $count);
$params = ['script' => $component];
$userid = $DB->get_field_select('user_private_key', 'userid', 'script = :script', $params);
+1 -1
View File
@@ -63,7 +63,7 @@ if ($cancel) {
}
redirect($url);
} else if ($continue) {
confirm_sesskey();
require_sesskey();
// Handle backups.
if (strtolower($importinfo->get_resource()->get_extension()) == 'mbz') {
+1 -1
View File
@@ -81,7 +81,7 @@ switch ($config->type) {
}
if ($import && $module) {
confirm_sesskey();
require_sesskey();
$handlerinfo = $handlerregistry->get_resource_handler_for_mod_and_strategy($importinfo->get_resource(), $module, $strategy);
if (is_null($handlerinfo)) {
@@ -58,7 +58,7 @@ class helper {
FROM {block_recentlyaccesseditems} rai
JOIN {course} c ON c.id = rai.courseid
WHERE userid = :userid
ORDER BY rai.timeaccess DESC";
ORDER BY rai.timeaccess DESC, rai.id DESC";
$records = $DB->get_records_sql($sql, $paramsql);
$order = 0;
+12
View File
@@ -3579,6 +3579,18 @@ function calendar_get_view(\calendar_information $calendar, $view, $includenavig
}
}
// Check if $data has events.
if (isset($data->events)) {
// Let's check and sanitize all "name" in $data->events before it's sent to front end.
foreach ($data->events as $d) {
$name = $d->name ?? null;
// Encode special characters if our decoded name does not match the original name.
if ($name && (html_entity_decode($name) !== $name)) {
$d->name = htmlspecialchars(html_entity_decode($name), ENT_QUOTES, 'utf-8');
}
}
}
return [$data, $template];
}
+1 -1
View File
@@ -46,7 +46,7 @@ $filename = str_replace('/', '', str_replace(' ', '_', $courseinfo->shortname))
// If download confirmed, prepare and start the zipstream of the course download content.
if ($isdownload) {
confirm_sesskey();
require_sesskey();
$exportoptions = null;
+87 -4
View File
@@ -9,11 +9,13 @@ Feature: Display and choose from the available activities in course
| username | firstname | lastname | email |
| teacher | Teacher | 1 | teacher@example.com |
And the following "courses" exist:
| fullname | shortname | format |
| Course | C | topics |
| fullname | shortname | format | startdate |
| Course | C | topics | |
| Course 2 | C2 | weeks | 95713920 |
And the following "course enrolments" exist:
| user | course | role |
| teacher | C | editingteacher |
| user | course | role |
| teacher | C | editingteacher |
| teacher | C2 | editingteacher |
And the following config values are set as admin:
| enablemoodlenet | 0 | tool_moodlenet |
And I log in as "teacher"
@@ -44,6 +46,12 @@ Feature: Display and choose from the available activities in course
When I click on "Information about the Assignment activity" "button" in the "Add an activity or resource" "dialogue"
Then I should see "Assignment" in the "help" "core_course > Activity chooser screen"
And I should see "The assignment activity module enables a teacher to communicate tasks, collect work and provide grades and feedback."
# Confirm show summary also works for weekly format course
And I am on "C2" course homepage with editing mode on
And I click on "Add an activity or resource" "button" in the "13 January - 19 January" "section"
And I click on "Information about the Assignment activity" "button" in the "Add an activity or resource" "dialogue"
And I should see "Assignment" in the "help" "core_course > Activity chooser screen"
And I should see "The assignment activity module enables a teacher to communicate tasks, collect work and provide grades and feedback."
Scenario: Hide summary
Given I click on "Add an activity or resource" "button" in the "Topic 1" "section"
@@ -55,6 +63,15 @@ Feature: Display and choose from the available activities in course
And "help" "core_course > Activity chooser screen" should not be visible
And "Back" "button" should not exist in the "modules" "core_course > Activity chooser screen"
And I should not see "The assignment activity module enables a teacher to communicate tasks, collect work and provide grades and feedback." in the "Add an activity or resource" "dialogue"
# Confirm hide summary also works for weekly format course
And I am on "C2" course homepage with editing mode on
And I click on "Add an activity or resource" "button" in the "13 January - 19 January" "section"
And I click on "Information about the Assignment activity" "button" in the "Add an activity or resource" "dialogue"
And I click on "Back" "button" in the "help" "core_course > Activity chooser screen"
And "modules" "core_course > Activity chooser screen" should be visible
And "help" "core_course > Activity chooser screen" should not be visible
And "Back" "button" should not exist in the "modules" "core_course > Activity chooser screen"
And I should not see "The assignment activity module enables a teacher to communicate tasks, collect work and provide grades and feedback." in the "Add an activity or resource" "dialogue"
Scenario: View recommended activities
When I log out
@@ -233,3 +250,69 @@ Feature: Display and choose from the available activities in course
And I reload the page
And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
And "Recommended" "link" should appear before "Activities" "link" in the "Add an activity or resource" "dialogue"
Scenario: Teacher can navigate through activity chooser in Topics format course
When I click on "Add an activity or resource" "button" in the "Topic 1" "section"
Then I should see "All" in the "Add an activity or resource" "dialogue"
And I press the tab key
And I press the tab key
And I press the tab key
And I press the tab key
# Confirm right key works
And I press the right key
And I press the right key
And the focused element is "Chat" "menuitem" in the "Add an activity or resource" "dialogue"
# Confirm left key works
And I press the left key
And the focused element is "Book" "menuitem" in the "Add an activity or resource" "dialogue"
# Confirm clicking "x" button closes modal
And I click on "Close" "button" in the "Add an activity or resource" "dialogue"
And "Add an activity or resource" "dialogue" should not be visible
And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
# Confirm escape key closes the modal
And I press the escape key
And "Add an activity or resource" "dialogue" should not be visible
Scenario: Teacher can navigate through activity chooser in Weekly format course
Given I am on "C2" course homepage with editing mode on
When I click on "Add an activity or resource" "button" in the "13 January - 19 January" "section"
Then I should see "All" in the "Add an activity or resource" "dialogue"
And I press the tab key
And I press the tab key
And I press the tab key
And I press the tab key
# Confirm right key works
And I press the right key
And I press the right key
And the focused element is "Chat" "menuitem" in the "Add an activity or resource" "dialogue"
# Confirm left key works
And I press the left key
And the focused element is "Book" "menuitem" in the "Add an activity or resource" "dialogue"
# Confirm clicking "x" button closes modal
And I click on "Close" "button" in the "Add an activity or resource" "dialogue"
And "Add an activity or resource" "dialogue" should not be visible
And I click on "Add an activity or resource" "button" in the "13 January - 19 January" "section"
# Confirm escape key closes the modal
And I press the escape key
And "Add an activity or resource" "dialogue" should not be visible
Scenario: Teacher can access 'More help' from activity information in activity chooser
Given I click on "Add an activity or resource" "button" in the "Topic 1" "section"
When I click on "Information about the Assignment activity" "button" in the "Add an activity or resource" "dialogue"
# Confirm more help link exists
Then "More help" "link" should exist
# Confirm that corresponding help icon exist
And ".iconhelp" "css_element" should exist
# Confirm that link opens in new window
And "Opens in new window" "link" should be visible
# Confirm the same behaviour for weekly format course
And I am on "C2" course homepage with editing mode on
And I click on "Add an activity or resource" "button" in the "13 January - 19 January" "section"
And I should see "All" in the "Add an activity or resource" "dialogue"
And I click on "Information about the Assignment activity" "button" in the "Add an activity or resource" "dialogue"
# Confirm more help link exists
And "More help" "link" should exist
# Confirm that corresponding help icon exist
And ".iconhelp" "css_element" should exist
# Confirm that link opens in new window
And "Opens in new window" "link" should be visible
@@ -0,0 +1,110 @@
@core @core_course @core_tag
Feature: Delete activity tags during course reset
As an admin,
I should be able to delete activity tags by performing course reset
Background:
Given the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "activities" exist:
| activity | name | course | idnumber |
| book | Test Book | C1 | book1 |
| forum | Test Forum | C1 | forum1 |
| glossary | Test Glossary | C1 | glossary1 |
@javascript
Scenario: Delete book chapter tags using course reset
# Added multiple tags to confirm that all tags are deleted on course reset.
Given the following "mod_book > chapters" exist:
| book | title | content | tags |
| Test Book | Chapter 1 | Chapter 1 content | SampleTag, ChapterTag |
# Perform course reset without checking anything.
And I log in as "admin"
And I am on the "Course 1" "reset" page
And I press "Reset"
And I press "Continue"
# Confirm that book chapter tags are not deleted.
When I am on the "Test Book" "book activity" page
Then I should see "SampleTag"
And I should see "ChapterTag"
# Delete book chapter tags using course reset.
And I am on the "Course 1" "reset" page
And I expand all fieldsets
And I click on "Remove all book tags" "checkbox"
And I press "Reset"
# Confirm that book chapter tags are deleted.
And I should see "Book tags have been deleted" in the "Books" "table_row"
And I press "Continue"
And I am on the "Test Book" "book activity" page
And I should not see "SampleTag"
And I should not see "ChapterTag"
@javascript
Scenario Outline: Delete forum discussion tags using course reset
Given the following "mod_forum > discussions" exist:
| user | forum | name | message | tags |
| admin | forum1 | Discussion 1 | Discussion 1 message | SampleTag, DiscussionTag |
# Perform course reset without checking anything.
And I am on the "Course 1" "reset" page logged in as admin
And I press "Reset"
And I press "Continue"
# Confirm that forum discussion tags are not deleted.
When I am on the "Test Forum" "forum activity" page
And I follow "Discussion 1"
Then I should see "SampleTag"
And I should see "DiscussionTag"
And I am on the "Course 1" "reset" page
And I expand all fieldsets
# Depending on <resetcheck> value, either delete all discussion posts or remove all forum discussion tags only.
And I click on "<resetcheck>" "checkbox"
# Confirm `Remove all forum tags` is disabled when `Delete all posts` on previous step is checked.
And the "Remove all forum tags" "checkbox" should be <canbechecked>
And I press "Reset"
And I should see "<resetmessage>" in the "Forums" "table_row"
And I press "Continue"
And I am on the "Test Forum" "forum activity" page
# Confirm discussion is deleted when `Delete all posts` was checked.
And I <forumview> see "There are no discussion topics yet in this forum"
# Confirm all discussion tags are deleted.
And I should not see "SampleTag"
And I should not see "DiscussionTag"
Examples:
| resetcheck | resetmessage | canbechecked | forumview |
| Delete all posts | Delete all posts | disabled | should |
| Remove all forum tags | Forum tags have been deleted | enabled | should not |
@javascript
Scenario Outline: Delete glossary entry tags using course reuse
Given the following "mod_glossary > entries" exist:
| glossary | concept | definition | user | tags |
| Test Glossary | Aubergine | Also eggpgplant | admin | SampleTag, GlossaryTag |
# Perform course reset without checking anything.
And I am on the "Course 1" "reset" page logged in as admin
And I press "Reset"
And I press "Continue"
# Confirm that glossary entry tags are not deleted.
When I am on the "Test Glossary" "glossary activity" page
Then I should see "SampleTag"
And I should see "GlossaryTag"
And I am on the "Course 1" "reset" page
And I expand all fieldsets
# Depending on <resetcheck> value, either delete all glossary entries or remove all glossary entry tags only.
And I click on "<resetcheck>" "checkbox"
# Confirm `Remove all forum tags` is disabled when `Delete entries from all glossaries` on previous step is checked.
And the "Remove all glossary tags" "checkbox" should be <canbechecked>
And I press "Reset"
And I should see "<resetmessage>" in the "Glossaries" "table_row"
And I press "Continue"
And I am on the "Test Glossary" "glossary activity" page
# Confirm glossary entries are deleted when `Delete entries from all glossaries` is checked.
And I <glossaryview> see "No entries found in this section"
# Confirm that glossary entry tags are deleted.
And I should not see "SampleTag"
And I should not see "GlossaryTag"
Examples:
| resetcheck | resetmessage | canbechecked | glossaryview |
| Delete entries from all glossaries | Delete entries from all glossaries | disabled | should |
| Remove all glossary tags | Glossary tags have been deleted | enabled | should not |
+1 -2
View File
@@ -39,8 +39,7 @@ require_once(__DIR__.'/lib.php');
global $CFG, $DB, $PAGE, $USER;
require_once($CFG->libdir . '/filelib.php');
require_login(null, false);
confirm_sesskey();
require_sesskey();
$launchid = required_param('launchid', PARAM_TEXT);
$modules = optional_param_array('modules', [], PARAM_INT);
$grades = optional_param_array('grades', [], PARAM_INT);
+2 -1
View File
@@ -328,8 +328,9 @@ class externallib_test extends externallib_advanced_testcase {
$this->expectExceptionMessage($exception['message']);
} else {
// Failed, only canview and exception are supported.
$this->markTestIncomplete('Incomplete, only canview and exception are supported');
throw new \coding_exception('Incomplete, only canview and exception are supported');
}
// Switch to the user and assign the role.
$this->setUser(${$user});
role_assign($roleid, $USER->id, $coursecontext);
+2 -10
View File
@@ -835,16 +835,8 @@ class grade_edit_tree_column_name extends grade_edit_tree_column {
$itemicon = \html_writer::div($params['icon'], 'mr-1');
$itemtype = \html_writer::span($params['type'], 'd-block text-uppercase small dimmed_text');
// Generate the content for a cell that represents a grade item.
// If a behat test site is running avoid outputting the information about the type of the grade item.
// This additional information causes issues in behat particularly with the existing xpath used to
// interact with table elements.
if (!defined('BEHAT_SITE_RUNNING')) {
$content = \html_writer::div($itemtype . $params['name']);
} else {
$content = \html_writer::div($params['name']);
}
$itemtitle = html_writer::div($params['name'], 'rowtitle');
$content = \html_writer::div($itemtype . $itemtitle);
$moveaction = isset($params['moveaction']) ? $params['moveaction'] : '';
@@ -0,0 +1,74 @@
@gradingform @gradingform_guide
Feature: Teacher can define a marking guide
As a teacher,
I should be able to define a marking guide
Background:
Given the following "users" exist:
| username | firtname | lastname | email |
| teacher1 | Teacher | One | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
And the following "activities" exist:
| activity | course | name | advancedgradingmethod_submissions |
| assign | C1 | Assign 1 | guide |
And I am on the "Course 1" course page logged in as teacher1
And I go to "Assign 1" advanced grading definition page
And I set the following fields to these values:
| Name | Marking guide 1 |
Scenario: No criterion added to marking guide
When I press "Save as draft"
# Confirm that criterion parameters are required
Then I should see "Criterion name can not be empty"
And I should see "Criterion max score can not be empty"
# Confirm that marking guide is not saved due to the missing criterion
And I should not see "Marking guide 1 Draft"
And I should not see "Please note: the advanced grading form is not ready at the moment. Simple grading method will be used until the form has a valid status."
@javascript
Scenario: Marking guide criterion is added to marking guide
Given I define the following marking guide:
| Criterion name | Description for students | Description for markers | Maximum score |
| Criteria 1 | Criteria 1 description for student | Criteria 1 description for marker | 70 |
| Criteria 2 | Criteria 2 description for student | Criteria 2 description for marker | 30 |
# Move Criteria 1 below Criteria 2
And I click on "Move down" "button" in the "Criteria 1" "table_row"
When I press "Save as draft"
And I go to "Assign 1" advanced grading definition page
# Confirm that the order of criterion shown matches input -- Criteria 2 is listed before Criteria 1
Then "Move down" "button" in the "Criteria 2" "table_row" should be visible
And "Move up" "button" in the "Criteria 2" "table_row" should not be visible
And "Move up" "button" in the "Criteria 1" "table_row" should be visible
And "Move down" "button" in the "Criteria 1" "table_row" should not be visible
# Confirm the other information entered were saved
And I should see "Criteria 2 description for student" in the "Criteria 2" "table_row"
And I should see "Criteria 2 description for marker" in the "Criteria 2" "table_row"
And I should see "30" in the "Criteria 2" "table_row"
And I should see "Criteria 1 description for student" in the "Criteria 1" "table_row"
And I should see "Criteria 1 description for marker" in the "Criteria 1" "table_row"
And I should see "70" in the "Criteria 1" "table_row"
Scenario: Marking guide options and frequently used comment are added to marking guide
Given I define the following marking guide:
| Criterion name | Description for students | Description for markers | Maximum score |
| Criteria 1 | Criteria 1 description for student | Criteria 1 description for marker | 50 |
| Criteria 2 | Criteria 2 description for student | Criteria 2 description for marker | 50 |
# Add frequently used comments and other marking guide options
And I define the following frequently used comments:
| Comment 1 |
| Comment 2 |
And I set the following fields to these values:
| Show guide definition to students | 1 |
| Show marks per criterion to students | 0 |
When I press "Save as draft"
And I go to "Assign 1" advanced grading definition page
# Confirm that frequently used comments and marking guide options specified during registration are retained
Then I should see "Comment 1"
And I should see "Comment 2"
And the field "Show guide definition to students" matches value "1"
And the field "Show marks per criterion to students" matches value "0"
@@ -190,14 +190,9 @@ class user extends tablelike implements selectable_items {
$itemicon = html_writer::div($this->format_icon($item), 'mr-1');
$itemtype = \html_writer::span($this->structure->get_element_type_string($gradetreeitem),
'd-block text-uppercase small dimmed_text');
// If a behat test site is running avoid outputting the information about the type of the grade item.
// This additional information currently causes issues in behat particularly with the existing xpath used to
// interact with table elements.
if (!defined('BEHAT_SITE_RUNNING')) {
$itemcontent = html_writer::div($itemtype . $itemname);
} else {
$itemcontent = html_writer::div($itemname);
}
$itemtitle = html_writer::div($itemname, 'rowtitle');
$itemcontent = html_writer::div($itemtype . $itemtitle);
$line = [
html_writer::div($itemicon . $itemcontent . $lockicon, "{$type} d-flex align-items-center"),
+2 -8
View File
@@ -551,14 +551,8 @@ class user extends grade_report {
}
// Generate the content for a cell that represents a grade item.
// If a behat test site is running avoid outputting the information about the type of the grade item.
// This additional information causes issues in behat particularly with the existing xpath used to
// interact with table elements.
if (!defined('BEHAT_SITE_RUNNING')) {
$content = \html_writer::div($itemtype . $fullname);
} else {
$content = \html_writer::div($fullname);
}
$itemtitle = \html_writer::div($fullname, 'rowtitle');
$content = \html_writer::div($itemtype . $itemtitle);
// Name.
$data['itemname']['content'] = \html_writer::div($itemicon . $content, "{$type} d-flex align-items-center");
+1 -4
View File
@@ -478,7 +478,7 @@ if ($config->stage == INSTALL_DATABASE) {
if ($hint_database !== '') {
echo '<div class="alert alert-danger">'.$hint_database.'</div>';
}
echo '</div>';
install_print_footer($config);
die;
}
@@ -610,11 +610,8 @@ if ($config->stage == INSTALL_PATHS) {
if ($hint_admindir !== '') {
echo '<div class="alert alert-danger">'.$hint_admindir.'</div>';
}
echo '</div>';
}
echo '</div>';
install_print_footer($config);
die;
}
+4 -1
View File
@@ -39,4 +39,7 @@ $string['cannotdownloadzipfile'] = 'Не може да се изтегли ZIP
$string['cannotfindcomponent'] = 'Не можа да намери компонент';
$string['cannotunzipfile'] = 'Файлът не може да се разархивира';
$string['componentisuptodate'] = 'Компонентът е актуален';
$string['remotedownloaderror'] = 'Изтеглянето на компонента към вашия сървър пропадна, проверете настройките на proxy, препоръчително е PHP разширението cURL.<br /><br />Вие трябва ръчно да изтеглите файла <a href="{$a->url}">{$a->url}</a>, да го копирате в директория {$a->dest} на вашия сървър и да го разархивирате там.';
$string['remotedownloaderror'] = '<p>Изтеглянето на компонента към вашия сървър пропадна, проверете настройките на proxy; препоръчително е PHP разширението cURL.</p><p>Вие трябва ръчно да изтеглите файла <a href="{$a->url}">{$a->url}</a>, да го копирате в директория "{$a->dest}" на вашия сървър и да го разархивирате там.</p>';
$string['wrongdestpath'] = 'Грешен път към целта';
$string['wrongsourcebase'] = 'Грешен изходен адрес';
$string['wrongzipfilename'] = 'Грешно име на ZIP файл-а';
+4 -4
View File
@@ -32,7 +32,7 @@ defined('MOODLE_INTERNAL') || die();
$string['admindirname'] = 'Pasta de administração';
$string['availablelangs'] = 'Pacotes linguísticos disponíveis';
$string['chooselanguagehead'] = 'Selecione um idioma';
$string['chooselanguagesub'] = 'Selecione o idioma a utilizar durante a instalação. O idioma escolhido será definido como o predefinido mas poderá depois selecionar outro(s) idioma(s) para o site e para os utilizadores.';
$string['chooselanguagesub'] = 'Selecione o idioma a utilizar durante a instalação. O idioma escolhido será definido como o predefinido, mas poderá depois selecionar outro(s) idioma(s) para o site e para os utilizadores.';
$string['clialreadyconfigured'] = 'O ficheiro config.php já existe. Use \'admin/cli/install_database.php\' para instalar o Moodle para este site.';
$string['clialreadyinstalled'] = 'O ficheiro config.php já existe, use admin/cli/install_database.php para atualizar o Moodle para este site.';
$string['cliinstallheader'] = 'Programa para instalação do Moodle <b>{$a}</b> através da linha de comandos';
@@ -56,9 +56,9 @@ $string['pathshead'] = 'Confirmar caminhos';
$string['pathsrodataroot'] = 'A pasta dos dados é só de leitura (não permite escrita).';
$string['pathsroparentdataroot'] = 'A pasta ascendente <b>{$a->parent}</b> não tem permissões de escrita. O programa de instalação não conseguiu criar a pasta <b>{$a->dataroot}</b>.';
$string['pathssubadmindir'] = 'Alguns servidores Web utilizam a pasta <strong>admin</strong> em URLs especiais de acesso a funcionalidades especiais, como é o caso de painéis de controlo. Algumas situações podem criar conflitos com a localização normal das páginas de administração do Moodle. Estes problemas podem ser resolvidos renomeando a pasta <strong>admin</strong> na instalação do Moodle e indicando aqui o novo nome a utilizar. Exemplo:<br /><br /><b>moodleadmin</b><br /><br />Esta ação resolverá os problemas de acesso das hiperligações para as funcionalidades de administração do Moodle.';
$string['pathssubdataroot'] = '<p>Pasta onde o Moodle irá armazenar todo o conteúdo de ficheiros enviados pelos utilizadores.</p>
<p>O utilizador do Moodle no servidor web (normalmente <b>nobody</b>, <b>apache</b> ou <b>www-data</b>) deve ter permissão de <b>leitura</b> e <b>escrita</b> nessa pasta.
<p>Não deve ser acessível diretamente através da web.</p>
$string['pathssubdataroot'] = '<p>Pasta onde o Moodle irá armazenar todo o conteúdo dos ficheiros enviados pelos utilizadores.</p>
<p>O utilizador do Moodle no servidor web (normalmente <b>nobody</b>, <b>apache</b> ou <b>www-data</b>) deve ter permissão de <b>leitura</b> e de <b>escrita</b> nessa pasta.
<p>Não pode ser acessível diretamente através da web.</p>
<p>Se a pasta não existir, o processo de instalação tentará criá-la.</p>';
$string['pathssubdirroot'] = 'Caminho completo para a pasta que contém o código Moodle.';
$string['pathssubwwwroot'] = 'Endereço web completo de acesso ao Moodle. Não é possível aceder ao Moodle usando mais do que um endereço. Se o site tiver mais do que um endereço público, devem ser configurados redirecionamentos permanentes em todos eles, à exceção deste. Se o site pode ser acedido a partir da Internet e de Intranet, use o endereço público aqui. Se o endereço atual não está correto, altere o endereço indicado na barra de endereço do seu navegador e reinicie a instalação.';
+34
View File
@@ -0,0 +1,34 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
/**
* Automatically generated strings for Moodle installer
*
* Do not edit this file manually! It contains just a subset of strings
* needed during the very first steps of installation. This file was
* generated automatically by export-installer.php (which is part of AMOS
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
* list of strings defined in /install/stringnames.txt.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$string['cliincorrectvalueretry'] = 'Noto\'g\'ri qiymat, iltimos qayta urinib ko\'ring';
$string['clitypevalue'] = 'qiymatni kiritish';
$string['clitypevaluedefault'] = 'qiymatni kiriting, ({$a}) odatiy qiymatni ishlatish uchun Enter tumgasini bosing';
+1 -1
View File
@@ -226,7 +226,7 @@ class behat_util extends testing_util {
behat_error (BEHAT_EXITCODE_REQUIREMENT, $CFG->behat_wwwroot . ' is not available, ensure you specified ' .
'correct url and that the server is set up and started.' . PHP_EOL . ' More info in ' .
behat_command::DOCS_URL . PHP_EOL);
behat_command::DOCS_URL . PHP_EOL . parent::get_site_info());
}
// Check if cli version is same as web version.
+4 -1
View File
@@ -638,7 +638,10 @@ class mysql_sql_generator extends sql_generator {
'_filename', 'admin', 'cume_dist', 'dense_rank', 'empty', 'except', 'first_value', 'grouping', 'groups',
'json_table', 'lag', 'last_value', 'lead', 'nth_value', 'ntile',
'of', 'over', 'percent_rank', 'persist', 'persist_only', 'rank', 'recursive', 'row_number',
'system', 'window'
'system', 'window',
// Added in Amazon Aurora MySQL version 3.06.0:
// https://docs.aws.amazon.com/AmazonRDS/latest/AuroraMySQLReleaseNotes/AuroraMySQL.Updates.3060.html .
'accept', 'aws_bedrock_invoke_model', 'aws_sagemaker_invoke_endpoint', 'content_type', 'timeout_ms',
);
return $reserved_words;
}
+7
View File
@@ -3847,6 +3847,7 @@ class curl {
$redirects++;
$currenturl = $redirecturl ?? $url;
$redirecturl = null;
if (isset($this->info['redirect_url'])) {
if (preg_match('|^https?://|i', $this->info['redirect_url'])) {
@@ -3904,6 +3905,12 @@ class curl {
}
curl_setopt($curl, CURLOPT_URL, $redirecturl);
if (parse_url($currenturl)['host'] !== parse_url($redirecturl)['host']) {
curl_setopt($curl, CURLOPT_HTTPAUTH, null);
curl_setopt($curl, CURLOPT_USERPWD, null);
}
$ret = curl_exec($curl);
$this->info = curl_getinfo($curl);
+2 -2
View File
@@ -1456,8 +1456,8 @@ EOF;
// Check if value exists in specific row/column.
// Get row xpath.
// GoutteDriver uses DomCrawler\Crawler and it is making XPath relative to the current context, so use descendant.
$rowxpath = $tablexpath."/tbody/tr[descendant::th[normalize-space(.)=" . $rowliteral .
"] | descendant::td[normalize-space(.)=" . $rowliteral . "]]";
$rowxpath = $tablexpath . "/tbody/tr[descendant::*[@class='rowtitle'][normalize-space(.)=" . $rowliteral . "] | " . "
descendant::th[normalize-space(.)=" . $rowliteral . "] | descendant::td[normalize-space(.)=" . $rowliteral . "]]";
$columnvaluexpath = $rowxpath . $columnpositionxpath . "[contains(normalize-space(.)," . $valueliteral . ")]";
+119 -33
View File
@@ -24,8 +24,7 @@
*
* @covers \core_component
*/
class component_test extends advanced_testcase {
final class component_test extends advanced_testcase {
/**
* To be changed if number of subsystems increases/decreases,
* this is defined here to annoy devs that try to add more without any thinking,
@@ -474,8 +473,10 @@ class component_test extends advanced_testcase {
$this->assertEquals(array(), array_keys($list));
}
public function test_get_component_classes_in_namespace() {
/**
* Tests for get_component_classes_in_namespace.
*/
public function test_get_component_classes_in_namespace(): void {
// Unexisting.
$this->assertCount(0, core_component::get_component_classes_in_namespace('core_unexistingcomponent', 'something'));
$this->assertCount(0, core_component::get_component_classes_in_namespace('auth_cas', 'something'));
@@ -485,35 +486,6 @@ class component_test extends advanced_testcase {
$this->assertCount(0, core_component::get_component_classes_in_namespace('core_user', 'course'));
$this->assertCount(0, core_component::get_component_classes_in_namespace('mod_forum', 'output\\emaildigest'));
$this->assertCount(0, core_component::get_component_classes_in_namespace('mod_forum', '\\output\\emaildigest'));
$this->assertCount(2, core_component::get_component_classes_in_namespace('mod_forum', 'output\\email'));
$this->assertCount(2, core_component::get_component_classes_in_namespace('mod_forum', '\\output\\email'));
$this->assertCount(2, core_component::get_component_classes_in_namespace('mod_forum', 'output\\email\\'));
$this->assertCount(2, core_component::get_component_classes_in_namespace('mod_forum', '\\output\\email\\'));
// Prefix with backslash if it doesn\'t come prefixed.
$this->assertCount(1, core_component::get_component_classes_in_namespace('auth_cas', 'task'));
$this->assertCount(1, core_component::get_component_classes_in_namespace('auth_cas', '\\task'));
// Core as a component works, the function can normalise the component name.
$this->assertCount(7, core_component::get_component_classes_in_namespace('core', 'update'));
$this->assertCount(7, core_component::get_component_classes_in_namespace('', 'update'));
$this->assertCount(7, core_component::get_component_classes_in_namespace('moodle', 'update'));
// Multiple levels.
$this->assertCount(5, core_component::get_component_classes_in_namespace('core_user', '\\output\\myprofile\\'));
$this->assertCount(5, core_component::get_component_classes_in_namespace('core_user', 'output\\myprofile\\'));
$this->assertCount(5, core_component::get_component_classes_in_namespace('core_user', '\\output\\myprofile'));
$this->assertCount(5, core_component::get_component_classes_in_namespace('core_user', 'output\\myprofile'));
// Without namespace it returns classes/ classes.
$this->assertCount(6, core_component::get_component_classes_in_namespace('tool_mobile', ''));
$this->assertCount(2, core_component::get_component_classes_in_namespace('tool_filetypes'));
// When no component is specified, classes are returned for the namespace in all components.
// (We don't assert exact amounts here as the count of `output` classes will change depending on plugins installed).
$this->assertGreaterThan(
count(\core_component::get_component_classes_in_namespace('core', 'output')),
count(\core_component::get_component_classes_in_namespace(null, 'output')));
// Without either a component or namespace it returns an empty array.
$this->assertEmpty(\core_component::get_component_classes_in_namespace());
@@ -521,6 +493,120 @@ class component_test extends advanced_testcase {
$this->assertEmpty(\core_component::get_component_classes_in_namespace(null, ''));
}
/**
* Test that the get_component_classes_in_namespace() function returns classes in the correct namespace.
*
* @dataProvider get_component_classes_in_namespace_provider
* @param array $methodargs
* @param string $expectedclassnameformat
*/
public function test_get_component_classes_in_namespace_provider(
array $methodargs,
string $expectedclassnameformat,
): void {
$classlist = core_component::get_component_classes_in_namespace(...$methodargs);
$this->assertGreaterThan(0, count($classlist));
foreach (array_keys($classlist) as $classname) {
$this->assertStringMatchesFormat($expectedclassnameformat, $classname);
}
}
/**
* Data provider for get_component_classes_in_namespace tests.
*
* @return array
*/
public static function get_component_classes_in_namespace_provider(): array {
return [
// Matches the last namespace level name not partials.
[
['mod_forum', 'output\\email'],
'mod_forum\output\email\%s',
],
[
['mod_forum', '\\output\\email'],
'mod_forum\output\email\%s',
],
[
['mod_forum', 'output\\email\\'],
'mod_forum\output\email\%s',
],
[
['mod_forum', '\\output\\email\\'],
'mod_forum\output\email\%s',
],
// Prefix with backslash if it doesn\'t come prefixed.
[
['auth_cas', 'task'],
'auth_cas\task\%s',
],
[
['auth_cas', '\\task'],
'auth_cas\task\%s',
],
// Core as a component works, the function can normalise the component name.
[
['core', 'update'],
'core\update\%s',
],
[
['', 'update'],
'core\update\%s',
],
[
['moodle', 'update'],
'core\update\%s',
],
// Multiple levels.
[
['core_user', '\\output\\myprofile\\'],
'core_user\output\myprofile\%s',
],
[
['core_user', 'output\\myprofile\\'],
'core_user\output\myprofile\%s',
],
[
['core_user', '\\output\\myprofile'],
'core_user\output\myprofile\%s',
],
[
['core_user', 'output\\myprofile'],
'core_user\output\myprofile\%s',
],
// Without namespace it returns classes/ classes.
[
['tool_mobile', ''],
'tool_mobile\%s',
],
[
['tool_filetypes'],
'tool_filetypes\%s',
],
// Multiple levels.
[
['core_user', '\\output\\myprofile\\'],
'core_user\output\myprofile\%s',
],
// When no component is specified, classes are returned for the namespace in all components.
// (We don't assert exact amounts here as the count of `output` classes will change depending on plugins installed).
[
['core', 'output'],
'core\%s',
],
[
[null, 'output'],
'%s',
],
];
}
/**
* Data provider for classloader test
*/
@@ -396,6 +396,7 @@ class feedback_test extends \advanced_testcase {
* and false when not modified.
*/
public function test_is_feedback_modified() {
$this->require_ghostscript();
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
+14 -12
View File
@@ -9504,20 +9504,22 @@ class assign {
$confirm = optional_param('confirm', 0, PARAM_BOOL);
if ($confirm) {
confirm_sesskey();
// Fix the grades.
$this->fix_null_grades();
unset_config('has_rescaled_null_grades_' . $instance->id, 'assign');
// Display the notice.
$o .= $this->get_renderer()->notification(get_string('fixrescalednullgradesdone', 'assign'), 'notifysuccess');
if (confirm_sesskey()) {
// Fix the grades.
$this->fix_null_grades();
unset_config('has_rescaled_null_grades_' . $instance->id, 'assign');
// Display the success notice.
$o .= $this->get_renderer()->notification(get_string('fixrescalednullgradesdone', 'assign'), 'notifysuccess');
} else {
// If the sesskey is not valid, then display the error notice.
$o .= $this->get_renderer()->notification(get_string('invalidsesskey', 'error'), 'notifyerror');
}
$url = new moodle_url(
'/mod/assign/view.php',
array(
url: '/mod/assign/view.php',
params: [
'id' => $this->get_course_module()->id,
'action' => 'grading'
)
'action' => 'grading',
],
);
$o .= $this->get_renderer()->continue_button($url);
} else {
+6
View File
@@ -55,6 +55,8 @@ class get_join_url extends external_api {
* @param int $cmid the bigbluebuttonbn course module id
* @param null|int $groupid
* @return array (empty array for now)
*
* @throws restricted_context_exception
*/
public static function execute(
int $cmid,
@@ -81,7 +83,11 @@ class get_join_url extends external_api {
}
$instance->set_group_id($groupid);
// Validate that the user has access to this activity and to join the meeting.
self::validate_context($instance->get_context());
if (!$instance->can_join()) {
throw new restricted_context_exception();
}
try {
$result['join_url'] = meeting::join_meeting($instance);
@@ -16,7 +16,9 @@
namespace mod_bigbluebuttonbn\external;
use context_course;
use core_external\external_api;
use core_external\restricted_context_exception;
use mod_bigbluebuttonbn\instance;
use mod_bigbluebuttonbn\test\testcase_helper_trait;
use moodle_exception;
@@ -85,6 +87,28 @@ class get_join_url_test extends \externallib_advanced_testcase {
$this->get_join_url($instance->get_cm_id());
}
/**
* Test execution with a user who doesn't have the capability to join the meeting
*/
public function test_execute_without_capability(): void {
global $DB;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$record = $this->getDataGenerator()->create_module('bigbluebuttonbn', ['course' => $course->id]);
$instance = instance::get_from_instanceid($record->id);
$user = $this->getDataGenerator()->create_and_enrol($course);
$this->setUser($user);
$student = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST);
assign_capability('mod/bigbluebuttonbn:join', CAP_PROHIBIT, $student, context_course::instance($course->id), true);
$this->expectException(restricted_context_exception::class);
$this->get_join_url($instance->get_cm_id());
}
/**
* Test execute API CALL with invalid login
*/
+4 -1
View File
@@ -3277,7 +3277,10 @@ function forum_add_discussion($discussion, $mform=null, $unused=null, $userid=nu
}
if (isset($discussion->tags)) {
core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post->id, context_module::instance($cm->id), $discussion->tags);
$tags = is_array($discussion->tags) ? $discussion->tags : explode(',', $discussion->tags);
core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post->id,
context_module::instance($cm->id), $tags);
}
if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) {
@@ -0,0 +1,99 @@
@mod @mod_glossary
Feature: Glossary can be set to various display formats
In order to display different glossary formats
As a teacher
I can set the glossary activity display format
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | One | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
Given the following "activities" exist:
| activity | course | name |
| glossary | C1 | Glossary 1 |
And the following "mod_glossary > entries" exist:
| glossary | concept | definition |
| Glossary 1 | Entry 1 | Entry 1 definition |
| Glossary 1 | Entry 2 | Entry 2 definition |
Scenario: Glossary display format is entry list style
Given I am on the "Glossary 1" "glossary activity editing" page logged in as teacher1
And I set the following fields to these values:
| displayformat | entrylist |
When I press "Save and display"
# Confirm that glossary display format is entry list.
# In this format, the concept definitions are not displayed.
Then I should not see "by Admin User"
And I should not see "Entry 1 definition"
And I should not see "Entry 2 definition"
And ".entrylist" "css_element" should exist
Scenario: Glossary display format is FAQ-style
Given I am on the "Glossary 1" "glossary activity editing" page logged in as teacher1
And I set the following fields to these values:
| displayformat | faq |
When I press "Save and display"
# Confirm that glossary format is FAQ.
# In this format, the words Question and Answer are displayed.
Then I should see "Question:"
And I should see "Answer:"
And ".faq" "css_element" should exist
@_file_upload @javascript
Scenario: Glossary display format is full without author style
Given I am on the "Glossary 1" "glossary activity editing" page logged in as teacher1
And I set the following fields to these values:
| displayformat | fullwithoutauthor |
And I press "Save and display"
And I press "Add entry"
# Add an entry with an attachment.
And I set the following fields to these values:
| Concept | Entry 3 |
| Definition | Entry 3 definition |
| Attachment | lib/tests/fixtures/gd-logo.png |
When I press "Save changes"
# Confirm that glossary format is full without author style.
# In this format, the image link should exist and author's name should not be visible.
Then "gd-logo.png" "link" should exist
And I should not see "by Admin User"
And ".fullwithoutauthor" "css_element" should exist
@_file_upload @javascript
Scenario: Glossary display format is encyclopedia style
Given I am on the "Glossary 1" "glossary activity editing" page logged in as teacher1
And I set the following fields to these values:
| displayformat | encyclopedia |
And I press "Save and display"
And I press "Add entry"
# Add an entry with an attachment.
And I set the following fields to these values:
| Concept | Entry 3 |
| Definition | Entry 3 definition |
| Attachment | lib/tests/fixtures/gd-logo.png |
When I press "Save changes"
# Confirm that glossary format is encyclopedia.
# In this format, the image element should be displayed.
Then "//img[contains(@src, 'gd-logo.png')]" "xpath_element" should exist
And ".encyclopedia" "css_element" should exist
Scenario Outline: Glossary display format can be set to dictionary, continuous and full with author
Given I am on the "Glossary 1" "glossary activity editing" page logged in as teacher1
# Assign the corresponding display format to glossary activity.
And I set the following fields to these values:
| displayformat | <display_format> |
When I press "Save and display"
# Confirm that glossary format is the display format set in the previous step.
Then I should <visibility> "by Admin User"
And ".<display_format>" "css_element" should exist
Examples:
| display_format | visibility |
| dictionary | not see |
| continuous | not see |
| fullwithauthor | see |
+11 -1
View File
@@ -188,6 +188,16 @@ class mod_glossary_generator extends testing_module_generator {
$DB->insert_record('glossary_entries_categories', ['entryid' => $id, 'categoryid' => $categoryid]);
}
return $DB->get_record('glossary_entries', array('id' => $id), '*', MUST_EXIST);
$entries = $DB->get_record('glossary_entries', ['id' => $id], '*', MUST_EXIST);
if (isset($record['tags'])) {
$cm = get_coursemodule_from_instance('glossary', $glossary->id);
$tags = is_array($record['tags']) ? $record['tags'] : explode(',', $record['tags']);
core_tag_tag::set_item_tags('mod_glossary', 'glossary_entries', $id,
context_module::instance($cm->id), $tags);
}
return $entries;
}
}
@@ -0,0 +1,41 @@
@mod @mod_h5pactivity @core_5hp
Feature: Control H5P activity availability for students
In order to restrict student access to H5P activity
As a teacher
I need to control the availability of the H5P activity
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | 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 |
And the following "activities" exist:
| activity | course | name |
| h5pactivity | C1 | H5P Test |
@javascript
Scenario Outline: Restrict H5P activity access by date
Given I am on the "H5P Test" "h5pactivity activity editing" page logged in as teacher1
And I expand all fieldsets
And I click on "Add restriction..." "button"
And I click on "Date" "button" in the "Add restriction..." "dialogue"
And I set the following fields to these values:
| Direction | from |
| x[day] | 1 |
| x[month] | 1 |
| x[year] | <year> |
And I press "Save and return to course"
When I am on the "Course 1" course page logged in as student1
Then I <fromvisibility> see "Available from"
Examples:
| year | fromvisibility |
| ## -1 year ## %Y ## | should not |
| ## +1 year ## %Y ## | should |
@@ -0,0 +1,100 @@
@mod @mod_h5pactivity @core_h5p
Feature: Teacher can control h5p activity grading setting
In order to set h5p activity grade
As a teacher
I need to be able to control h5p activity grading setting
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
| student3 | Student | 3 | student3@example.com |
| student4 | Student | 4 | student4@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 |
| student2 | C1 | student |
| student3 | C1 | student |
| student4 | C1 | student |
@javascript
Scenario: Verify that invalid grades given to students are not saved
Given the following "activities" exist:
| activity | name | course |
| h5pactivity | H5P point | C1 |
# Activity grade settings are not saved using generators so manual setting is necessary.
And I am on the "H5P point" "h5pactivity activity editing" page logged in as teacher1
And I set the following fields to these values:
| grade[modgrade_type] | Point |
| grade[modgrade_point] | 10 |
And I press "Save and return to course"
And I navigate to "View > Grader report" in the course gradebook
And I turn editing mode on
When I give the grade "50" to the user "Student 1" for the grade item "H5P point"
And I press "Save changes"
And I turn editing mode off
# Confirm that grades are not saved when grade entered is > maximum grade.
Then the following should exist in the "user-grades" table:
| -1- | -1- | -3- |
| Student 1 | student1@example.com | - |
| Student 2 | student2@example.com | - |
| Student 3 | student3@example.com | - |
| Student 4 | student4@example.com | - |
@javascript
Scenario: Verify that valid grades given to students are saved
Given the following "activities" exist:
| activity | name | course |
| h5pactivity | H5P point | C1 |
# Activity grade settings are not saved using generators so manual setting is necessary.
And I am on the "H5P point" "h5pactivity activity editing" page logged in as teacher1
And I set the following fields to these values:
| grade[modgrade_type] | Point |
| grade[modgrade_point] | 10 |
And I press "Save and return to course"
And I navigate to "View > Grader report" in the course gradebook
And I turn editing mode on
When I give the grade "10" to the user "Student 1" for the grade item "H5P point"
And I give the grade "5" to the user "Student 2" for the grade item "H5P point"
And I give the grade "0" to the user "Student 3" for the grade item "H5P point"
And I press "Save changes"
And I turn editing mode off
# Confirm that corresponding grades are stored for each student.
And the following should exist in the "user-grades" table:
| -1- | -1- | -3- |
| Student 1 | student1@example.com | 10 |
| Student 2 | student2@example.com | 5 |
| Student 3 | student3@example.com | 0 |
| Student 4 | student4@example.com | - |
@javascript
Scenario: Verify that scales given to students are saved
Given the following "activities" exist:
| activity | name | course |
| h5pactivity | H5P scale | C1 |
# Activity grade settings are not saved using generators so manual setting is necessary.
And I am on the "H5P scale" "h5pactivity activity editing" page logged in as teacher1
And I set the following fields to these values:
| grade[modgrade_type] | Scale |
| grade[modgrade_scale] | Default competence scale |
And I press "Save and return to course"
And I navigate to "View > Grader report" in the course gradebook
And I turn editing mode on
And I give the grade "Not yet competent" to the user "Student 1" for the grade item "H5P scale"
And I give the grade "Competent" to the user "Student 2" for the grade item "H5P scale"
And I give the grade "Competent" to the user "Student 4" for the grade item "H5P scale"
When I press "Save changes"
And I turn editing mode off
# Confirm that scale set for student is successfully saved.
Then the following should exist in the "user-grades" table:
| -1- | -1- | -3- |
| Student 1 | student1@example.com | Not yet competent |
| Student 2 | student2@example.com | Competent |
| Student 3 | student3@example.com | - |
| Student 4 | student4@example.com | Competent |
@@ -0,0 +1,106 @@
@core @core_my @javascript
Feature: Navigate and use preferences page
In order to navigate through preferences page
As a user
I need to be able to use preferences page
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Sam | Student | s1@example.com |
And the following "courses" exist:
| fullname | shortname | format |
| Course 1 | C1 | topics |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
And I log in as "admin"
Scenario Outline: Navigating through user menu Preferences
When I follow "Preferences" in the user menu
# Click each link in the 'Preferences' page.
And I click on "<userprefpage>" "link" in the "#page-content" "css_element"
# Confirm that each redirected page has 'Preferences' in the breadcrumbs.
And "Users" "link" should not exist in the ".breadcrumb" "css_element"
Then "Preferences" "link" should exist in the ".breadcrumb" "css_element"
# Additional confirmation that breadcrumbs is correct.
And "<userprefpage>" "text" should exist in the ".breadcrumb" "css_element"
# Confirm that user name and profile picture are displayed in header section.
And I should see "Admin User" in the ".page-header-headings" "css_element"
And ".page-header-image" "css_element" should exist in the "#page-header" "css_element"
Examples:
| userprefpage |
| Edit profile |
| Change password |
| Preferred language |
| Forum preferences |
| Editor preferences |
| Calendar preferences |
| Content bank preferences |
| Message preferences |
| Notification preferences |
| Manage badges |
| Badge preferences |
| Backpack settings |
| This user's role assignments |
| Permissions |
| Check permissions |
| Blog preferences |
| External blogs |
| Register an external blog |
Scenario Outline: Navigating through course participant preferences
Given I am on "Course 1" course homepage
And I navigate to course participants
And I follow "Sam Student"
When I click on "Preferences" "link" in the "#region-main-box" "css_element"
Then I should see "Sam Student" in the ".page-header-headings" "css_element"
And ".page-header-image" "css_element" should exist in the "#page-header" "css_element"
# Click each link in the 'Preferences' page.
And I click on "<courseprefpage>" "link" in the "#page-content" "css_element"
# Confirm that each redirected page has 'Users/{user}/Preferences' in the breadcrumbs.
Then "Users" "link" should exist in the ".breadcrumb" "css_element"
And "Sam Student" "link" should exist in the ".breadcrumb" "css_element"
And "Preferences" "link" should exist in the ".breadcrumb" "css_element"
# Additional confirmation that breadcrumbs is correct.
And "<courseprefpage>" "text" should exist in the ".breadcrumb" "css_element"
# Confirm that user name and profile picture are displayed in header section.
And I should see "Sam Student" in the ".page-header-headings" "css_element"
And ".page-header-image" "css_element" should exist in the "#page-header" "css_element"
Examples:
| courseprefpage |
| Edit profile |
| Preferred language |
| Forum preferences |
| Editor preferences |
| Calendar preferences |
| Content bank preferences |
| Message preferences |
| Notification preferences |
| This user's role assignments |
| Permissions |
| Check permissions |
Scenario: Navigation with Event monitoring enabled
Given I navigate to "Reports > Event monitoring rules" in site administration
And I click on "Enable" "link"
And I press "Add a new rule"
And I set the following fields to these values:
| Rule name | Testing1 |
| Area to monitor | Core |
| Event | Allow role override |
And I press "Save changes"
When I follow "Preferences" in the user menu
# Confirm that Event monitoring is visible and clickable.
Then I should see "Miscellaneous"
And I follow "Event monitoring"
# Confirm that user can subscribe to new rule.
And "Subscribe to rule \"Testing1\"" "link" should exist
And I am on "Course 1" course homepage
And I navigate to course participants
And I follow "Sam Student"
And I click on "Preferences" "link" in the "#region-main-box" "css_element"
# Confirm that admin cannot change student's event monitor subscription.
And I should not see "Event monitoring"
@@ -37,7 +37,7 @@ Feature: Test creating a Numerical question
Then I should see "Numerical-001"
@javascript
Scenario: Create a Numerical question with units
Scenario: Create a Numerical question with required units
When I am on the "Course 1" "core_question > course question bank" page logged in as teacher
And I add a "Numerical" question filling the form with:
| Question name | Numerical-002 |
@@ -70,3 +70,31 @@ Feature: Test creating a Numerical question
| id_unitgradingtypes | as a fraction (0-1) of the question grade |
| id_multichoicedisplay | a drop-down menu |
| id_unitsleft | on the right, for example 1.00cm or 1.00km |
Scenario: Create a Numerical question with optional units
Given I am on the "Course 1" "core_question > course question bank" page logged in as teacher
# Add the numerical question with optional units.
And I add a "Numerical" question filling the form with:
| Question name | Numerical Question (optional) |
| Question text | How many meter is 1m + 20cm + 50mm? |
| Default mark | 1 |
| General feedback | The correct answer is 1.25m |
| id_answer_0 | 1.25 |
| id_tolerance_0 | 0 |
| id_fraction_0 | 100% |
| id_answer_1 | 125 |
| id_tolerance_1 | 0 |
| id_fraction_1 | 0% |
| id_unitrole | Units are optional. |
| id_unitsleft | on the right, for example 1.00cm or 1.00km |
| id_unit_0 | m |
# Confirm that the numerical question with optional units is added successfully.
When I choose "Edit question" action for "Numerical Question (optional)" in the question bank
Then the following fields match these values:
| Question name | Numerical Question (optional) |
| Question text | How many meter is 1m + 20cm + 50mm? |
| Default mark | 1 |
| General feedback | The correct answer is 1.25m |
| id_unitrole | Units are optional. If a unit is entered, it is used to convert the response to Unit 1 before grading. |
| id_unitsleft | on the right, for example 1.00cm or 1.00km |
| id_unit_0 | m |
@@ -55,3 +55,32 @@ Feature: Test editing a Numerical question
Then the following fields match these values:
| id_answer_0 | 0.00000123456789 |
| id_tolerance_1 | 0.0000123456789 |
Scenario: Edit a Numerical question with optional units
Given I am on the "Numerical for editing" "core_question > edit" page logged in as teacher
# Edit the existing numerical question, changing the unit to optional.
And I set the following fields to these values:
| Question name | Numerical Question (optional) |
| Question text | How many meter is 1m + 20cm + 50mm? |
| Default mark | 1 |
| General feedback | The correct answer is 1.25m |
| id_answer_0 | 1.25 |
| id_tolerance_0 | 0 |
| id_fraction_0 | 100% |
| id_answer_1 | 125 |
| id_tolerance_1 | 0 |
| id_fraction_1 | 0% |
| id_unitrole | Units are optional. |
| id_unitsleft | on the right, for example 1.00cm or 1.00km |
| id_unit_0 | m |
And I press "submitbutton"
When I choose "Edit question" action for "Numerical Question (optional)" in the question bank
# Confirm that the numerical question with optional unit is updated accordingly.
Then the following fields match these values:
| Question name | Numerical Question (optional) |
| Question text | How many meter is 1m + 20cm + 50mm? |
| Default mark | 1 |
| General feedback | The correct answer is 1.25m |
| id_unitrole | Units are optional. If a unit is entered, it is used to convert the response to Unit 1 before grading. |
| id_unitsleft | on the right, for example 1.00cm or 1.00km |
| id_unit_0 | m |
@@ -45,3 +45,25 @@ Feature: Preview a Numerical question
And I press "Check"
And I should see "Very good."
And I should see "Mark 1#00 out of 1#00"
Scenario: Preview a Numerical question with optional units
Given I am on the "Numerical-001" "core_question > edit" page logged in as teacher
# Edit the existing numerical question, add in the optional units.
And I set the following fields to these values:
| Question name | Numerical Question (optional) |
| Question text | How many meter is 1m + 20cm + 50mm? |
| Default mark | 1 |
| General feedback | The correct answer is 1.25m |
| id_answer_0 | 1.25 |
| id_tolerance_0 | 0 |
| id_fraction_0 | 100% |
| id_answer_1 | 125 |
| id_tolerance_1 | 0 |
| id_fraction_1 | 0% |
| id_unitrole | Units are optional. |
| id_unitsleft | on the right, for example 1.00cm or 1.00km |
| id_unit_0 | m |
And I press "submitbutton"
When I choose "Preview" action for "Numerical Question (optional)" in the question bank
# Unit is optional, so the unit select box should not be exist.
Then "Select one unit" "select" should not exist
+3 -1
View File
@@ -191,11 +191,13 @@ class report_log_table_log extends table_sql {
} else if ($eventusername) {
if (empty($this->download)) {
$params = array('id' => $event->userid);
$params = ['id' => $event->userid];
if ($event->courseid) {
$params['course'] = $event->courseid;
}
$username = html_writer::link(new moodle_url('/user/view.php', $params), $eventusername);
} else {
$username = $eventusername;
}
} else {
$username = '-';
@@ -16,7 +16,6 @@ Feature: In a course administration page, navigate through report page, test for
| admin | C1 | editingteacher |
| student1 | C1 | student |
@javascript
Scenario: Selector should be available in the course participation page
Given I log in as "admin"
And I am on "Course 1" course homepage
@@ -20,51 +20,49 @@ Feature: In a participation report, admin can filter student actions
| student1 | C1 | student |
And the following "activity" exists:
| course | C1 |
| activity | book |
| name | Test book name |
| idnumber | book1 |
And the following "mod_book > chapter" exists:
| book | Test book name |
| title | Test chapter |
| content | Test chapter content |
| activity | forum |
| name | Test forum name |
| idnumber | forum1 |
@javascript
Scenario: Filter participation report when standard log reader is enabled later
Given I log in as "admin"
And I navigate to "Plugins > Logging > Manage log stores" in site administration
And I click on "Disable" "link" in the "Standard log" "table_row"
And I log out
And I am on the "Test book name" "book activity" page logged in as student1
And I am on the "Test forum name" "forum activity" page logged in as student1
And I log out
And I log in as "admin"
And I navigate to "Plugins > Logging > Manage log stores" in site administration
And I click on "Enable" "link" in the "Standard log" "table_row"
And I am on the "Test book name" "book activity" page logged in as student1
And I am on the "Test forum name" "forum activity" page logged in as student1
And I am on the "Course 1" course page logged in as admin
When I navigate to "Reports" in current page administration
And I click on "Course participation" "link"
And I set the field "instanceid" to "Test book name"
And I set the field "roleid" to "Student"
And I set the following fields to these values:
| Activity module | Test forum name |
| Show only | Student |
And I press "Go"
Then I should see "Yes (1)"
Then the following should exist in the "reporttable" table:
| -1- | All actions |
| Student 1 | Yes (1) |
@javascript
Scenario: Filter participation report when only standard log reader is enabled by default
Given I am on the "Test book name" "book activity" page logged in as student1
And I am on the "Course 1" course page logged in as admin
Given I am on the "Test forum name" "forum activity" page logged in as student1
When I am on the "Course 1" course page logged in as admin
And I navigate to "Reports" in current page administration
And I click on "Course participation" "link"
And I set the field "instanceid" to "Test book name"
And I set the field "roleid" to "Student"
And I set the following fields to these values:
| Activity module | Test forum name |
| Show only | Student |
And I press "Go"
Then I should see "Yes (1)"
Then the following should exist in the "reporttable" table:
| -1- | All actions |
| Student 1 | Yes (1) |
@javascript
Scenario Outline: Filter participation report by viewable roles
Given I am on the "Course 1" course page logged in as "teacher1"
When I navigate to "Reports" in current page administration
@@ -72,8 +70,8 @@ Feature: In a participation report, admin can filter student actions
# Teacher role cannot see Manager by default.
Then "Manager" "option" should not exist in the "Show only" "select"
And I set the following fields to these values:
| Activity module | Test book name |
| Show only | <role> |
| Activity module | Test forum name |
| Show only | <role> |
And I press "Go"
And I should see "<uservisible>" in the "reporttable" "table"
And I should not see "<usernonvisible>" in the "reporttable" "table"
@@ -1,5 +1,5 @@
@report @report_participation @javascript
Feature: Use the particiaption report to message groups of students
@report @report_participation
Feature: Use the participation report to message groups of students
In order to engage with students based on participation
As a teacher
I need to be able to message students who have not participated in an activity
@@ -28,6 +28,7 @@ Feature: Use the particiaption report to message groups of students
| idnumber | book1 |
And I am on the "Test book name" "book activity" page logged in as student1
@javascript
Scenario: Message all students from the participation report
Given I am on the "Course 1" course page logged in as teacher1
And I navigate to "Reports" in current page administration
@@ -42,6 +43,7 @@ Feature: Use the particiaption report to message groups of students
And I press "Send message to 3 people"
And I should see "Message sent to 3 people"
@javascript
Scenario: Message students who have not participated in book
Given I am on the "Course 1" course page logged in as teacher1
And I navigate to "Reports" in current page administration
@@ -59,7 +61,7 @@ Feature: Use the particiaption report to message groups of students
And I press "Send message to 2 people"
And I should see "Message sent to 2 people"
Scenario: Ensure no message options when messaging is disabled
Scenario: When messaging is disabled no message options should be displayed
Given the following config values are set as admin:
| messaging | 0 |
And I am on the "Course 1" course page logged in as teacher1
+2 -2
View File
@@ -29,9 +29,9 @@
defined('MOODLE_INTERNAL') || die();
$version = 2023042407.00; // 20230424 = branching date YYYYMMDD - do not modify!
$version = 2023042408.00; // 20230424 = branching date YYYYMMDD - do not modify!
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
$release = '4.2.7 (Build: 20240422)'; // Human-friendly version name
$release = '4.2.8 (Build: 20240610)'; // Human-friendly version name
$branch = '402'; // This version's branch.
$maturity = MATURITY_STABLE; // This version's maturity level.