Compare commits

..

95 Commits

Author SHA1 Message Date
Jun Pataleta 73fc31dabf Moodle release 4.3.12 2025-04-13 09:52:00 +08:00
Huong Nguyen 75b685892e weekly release 4.3.11+ 2025-04-11 22:02:26 +07:00
Huong Nguyen e1a465e308 Merge branch 'install_403_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_403_STABLE 2025-04-11 22:02:25 +07:00
Sara Arjona fef55ab67f MDL-83994 course: Adapt PHPUnit to lower PHP versions 2025-04-11 19:25:11 +08:00
Huong Nguyen e34a6de8fc MDL-72704 message: Fix fetching for user has already contacted 2025-04-11 17:46:43 +08:00
Paul Holden 8b0c0899a2 MDL-84865 cohort: improve system report validation of parameters. 2025-04-11 12:46:56 +08:00
Jun Pataleta 53070e58e6 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:19 +08:00
Stevani Andolo 5f399c1fd6 MDL-72704 message: Fix fetching of conversation member info 2025-04-11 12:19:19 +08:00
raortegar 3cebc3ae93 MDL-85104 lib: Add additional check to local URLs params. 2025-04-11 10:22:42 +07:00
James C f985532070 MDL-83994 course: Respect course_can_delete_section() in AJAX 2025-04-11 13:06:39 +10:00
Paul Holden 07ec036d94 MDL-84473 repository_equella: safer unserializing of file references. 2025-04-11 09:52:23 +07:00
Paul Holden 4992ac10d4 MDL-84475 repository_dropbox: safer unserializing of file references. 2025-04-11 09:34:20 +07:00
Michael Hawkins 699c8b2762 MDL-85152 filter_tex: Update deny list and slash handling 2025-04-10 14:21:10 +08:00
Paul Holden 6e5f3f1bcb MDL-84478 tool_brickfield: verify sesskey prior to course analysis. 2025-04-10 13:07:24 +08:00
Paul Holden bf37322446 MDL-84499 block_rss_client: user access checks for viewing feed. 2025-04-10 12:32:48 +08:00
Paul Holden ede62f83d3 MDL-65356 mod_data: update entry editing/deletion to not leak sesskey. 2025-04-10 10:08:08 +07:00
Paul Holden 29a405d768 MDL-84750 course: require user be able to view course participants. 2025-04-10 09:30:34 +08:00
David Woloszyn 0556eee665 MDL-84351 tool_mfa: Fix guidance page file access
File uploaded to the guidance page were not accessible due to
null values being provided to $course and $cm params for the
tool_mfa_pluginfile.
2025-04-09 16:51:28 +07:00
raortegar fa44a1de53 MDL-84351 tool_mfa: Fix access to guide page when MFA login 2025-04-09 16:51:28 +07:00
David Woloszyn 25c2e8079d MDL-84351 tool_mfa: Check URL against allowed components for redirect 2025-04-09 16:51:28 +07:00
David Woloszyn ef8cf9fdce MDL-84351 url: Add method for getting slashargument 2025-04-09 16:51:27 +07:00
Paul Holden 8733010253 MDL-84479 tool_usertours: verify sesskey prior to tour duplication. 2025-04-08 16:16:22 +07:00
AMOS bot 1e86bbfdd6 Automatically generated installer lang files 2025-04-08 00:07:43 +00:00
Michael Hawkins 15e392cec5 MDL-85015 tool_mfa: Include extra param in email factor cancel check 2025-04-04 15:24:19 +00:00
David Woloszyn 062d760482 MDL-84784 tool_mfa: Remove enrol/index.php from no redirect 2025-04-04 15:24:19 +00:00
Huong Nguyen 835e2b76c3 weekly release 4.3.11+ 2025-04-04 22:20:18 +07:00
Jun Pataleta aa7b5a9632 Merge branch 'MDL-84907-403' of https://github.com/HuongNV13/moodle into MOODLE_403_STABLE 2025-04-04 11:30:58 +08:00
Shamim Rezaie 0606dcfbce Merge branch 'MDL-85000-403' of https://github.com/HuongNV13/moodle into MOODLE_403_STABLE 2025-04-03 06:14:13 +11:00
Sara Arjona 0f20b11843 weekly release 4.3.11+ 2025-03-28 12:48:28 +01:00
Sara Arjona fe1caf61ba Merge branch 'install_403_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_403_STABLE 2025-03-28 12:48:27 +01:00
Paul Holden d6eaf85e38 MDL-85048 core: define mapping for new America/Coyhaique zone.
Follows same process as other timezone additions in d5ec3594.
2025-03-28 08:58:46 +00:00
Huong Nguyen a2e327094e Merge branch 'MDL-85048-403' of https://github.com/lameze/moodle into MOODLE_403_STABLE 2025-03-28 15:01:25 +07:00
Simey Lameze ae9228b834 MDL-85048 phpunit: add america/coyhaique timezone 2025-03-28 15:00:26 +08:00
AMOS bot a2f1368e38 Automatically generated installer lang files 2025-03-28 00:08:36 +00:00
Huong Nguyen 307ad2348f MDL-85000 media_videojs: Enable Ogv.JS Tech for all browsers 2025-03-26 09:34:09 +07:00
Huong Nguyen 8e9bd4c55c weekly release 4.3.11+ 2025-03-26 08:56:08 +07:00
Huong Nguyen 95fea46186 Merge branch 'install_403_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_403_STABLE 2025-03-26 08:56:07 +07:00
Huong Nguyen b666531258 Merge branch 'MDL-83160-403' of https://github.com/mickhawkins/moodle into MOODLE_403_STABLE 2025-03-24 14:54:54 +07:00
AMOS bot 3f20152cac Automatically generated installer lang files 2025-03-22 00:07:44 +00:00
Huong Nguyen b883a7a21f weekly release 4.3.11+ 2025-03-21 22:04:15 +07:00
Huong Nguyen 75d8e9fb77 Merge branch 'install_403_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_403_STABLE 2025-03-21 22:04:14 +07:00
AMOS bot 162627b088 Automatically generated installer lang files 2025-03-21 00:07:45 +00:00
Huong Nguyen 3b374cdc8a MDL-84905 Behat: Fix I set the following administration settings failure 2025-03-19 09:17:33 +07:00
Huong Nguyen a85c9490b0 MDL-84907 backup: Fix invalid CTRL characters regex 2025-03-18 14:12:07 +07:00
Jun Pataleta 49bf20af0b Moodle release 4.3.11 2025-03-15 20:19:02 +08:00
Ilya Tregubov 03242ec12c MDL-81945 gradereport_singleview: Fix hidden grade handling.
Seems to become broken after IR refactoring.
2025-03-14 15:24:16 +00:00
Ilya Tregubov 48a3c783dd MDL-81945 gradereport_user: Don't reveal visibility by color.
Seems like dimmed_text class was added without proper check so
user can guess if item is hidden.
2025-03-14 15:24:16 +00:00
Ilya Tregubov acdcb722b0 MDL-81945 gradereport_user: Behat tests for user report. 2025-03-14 15:24:16 +00:00
Ilya Tregubov e692607aaa MDL-81945 gradereport_singleview: Behat tests for singleview report. 2025-03-14 15:24:16 +00:00
Ilya Tregubov 7f7f13bba0 MDL-81945 gradereport_grader: Behat tests for grader report 2025-03-14 15:24:16 +00:00
Ilya Tregubov 920a8be0e6 MDL-81945 core_grades: create Behat step for grades action menu. 2025-03-14 15:24:16 +00:00
Ilya Tregubov c1e2713764 MDL-81945 gradereport_singleview: Fix hidden grades handling.
If a user is lacking moodle/grade:viewhidden he should not be able
to view or modify grades.
2025-03-14 15:24:16 +00:00
Sara Arjona d3a8d50292 weekly release 4.3.10+ 2025-03-14 16:14:15 +01:00
Sara Arjona d0137c54a2 Merge branch 'install_403_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_403_STABLE 2025-03-14 16:14:12 +01:00
Michael Hawkins beb2fcb077 MDL-83160 core: Update security.txt expiry 2025-03-13 20:36:02 +08:00
Huong Nguyen 58ba3cf834 Merge branch 'MDL-84640-403' of https://github.com/andelacruz/moodle into MOODLE_403_STABLE 2025-03-12 16:32:40 +07:00
Angelia Dela Cruz f1c8f2281a MDL-84640 analytics: Behat for obtaining prediction models insights 2025-03-12 16:57:41 +08:00
Sara Arjona 03e8bddd1e Merge branch 'MDL-84801-403' of https://github.com/HuongNV13/moodle into MOODLE_403_STABLE 2025-03-11 17:26:37 +01:00
Huong Nguyen 2531818e3d MDL-84801 Behat: Fix Expand all fieldsets and advanced elements failure 2025-03-11 09:22:26 +07:00
AMOS bot e2a32ef88f Automatically generated installer lang files 2025-03-11 00:07:47 +00:00
AMOS bot e4a37ce710 Automatically generated installer lang files 2025-03-10 00:07:36 +00:00
Jun Pataleta a94925736c weekly release 4.3.10+ 2025-03-08 11:44:39 +08:00
Andrew Nicols b10657129e Merge branch 'MDL-84723-403' of https://github.com/lameze/moodle into MOODLE_403_STABLE 2025-03-06 11:51:21 +08:00
Simey Lameze 2cee750fdf MDL-84723 behat: final improvements and fixes to the test
This commit does few things:
- Remove unnecessary grade field to assign generator.
- Remove unnecessary comment lines.
- Update the warning message assertion to use the no-break space unicode character.
2025-03-06 11:24:16 +08:00
Angelia Dela Cruz 78a04159cd MDL-84723 gradingform: Behat for maximum grade mismatch in marking guide 2025-03-06 11:24:16 +08:00
Huong Nguyen eb87add2b2 Merge branch 'MDL-84425-403' of https://github.com/andelacruz/moodle into MOODLE_403_STABLE 2025-03-05 10:51:45 +07:00
Angelia Dela Cruz a952340811 MDL-84425 theme_boost: Behat for course section selector unavailable 2025-03-05 11:20:12 +08:00
Sara Arjona 21fe8a97e8 weekly release 4.3.10+ 2025-03-04 16:13:50 +01:00
Sara Arjona f2093b46e8 Merge branch 'install_403_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_403_STABLE 2025-03-04 16:13:49 +01:00
Huong Nguyen abea467757 Merge branch 'MDL-84595-403' of https://github.com/andrewnicols/moodle into MOODLE_403_STABLE 2025-03-04 10:55:44 +07:00
AMOS bot 2c4e86245b Automatically generated installer lang files 2025-03-02 00:08:09 +00:00
Sara Arjona ea80a7f049 weekly release 4.3.10+ 2025-02-27 16:54:57 +01:00
Simey Lameze 5e7238e637 MDL-84424 behat: add template_competencies generator and improve test 2025-02-27 07:55:31 +08:00
Angelia Dela Cruz 49e490d209 MDL-84424 tool_lp: admin can create learning plans from template 2025-02-27 07:54:33 +08:00
Sara Arjona 6009960edb Merge branch 'MDL-84609-403' of https://github.com/lameze/moodle into MOODLE_403_STABLE 2025-02-26 16:19:59 +01:00
Simey Lameze cdf2b01228 MDL-84609 behat: add required entities and fix failures
This commit also fix some typos and misaligned pipes around the
changed steps.

Signed-off-by: Simey Lameze <simey@moodle.com>
2025-02-25 11:52:14 +08:00
Angelia Dela Cruz c52babea18 MDL-84609 tool: Replaced tool_lp generators with standard generators 2025-02-25 11:52:14 +08:00
Huong Nguyen 9a528fbbb4 weekly release 4.3.10+ 2025-02-21 10:15:16 +07:00
Andrew Nicols beb5ba99e7 MDL-84595 tool_behat: Allow accessibility tests to run for a single Node 2025-02-20 22:28:24 +08:00
Jun Pataleta a689db2063 Merge branch 'MDL-84448-403' of https://github.com/lameze/moodle into MOODLE_403_STABLE 2025-02-20 12:10:06 +08:00
Simey Lameze 151542757f MDL-84448 behat: return null if theme has no config 2025-02-20 12:00:49 +08:00
Amanda Doughty 87b30d4ac6 MDL-84448 lib: Fixed behat context check for grandchild themes 2025-02-20 12:00:49 +08:00
Jun Pataleta 14772da46c Merge branch 'MDL-84592-403' of https://github.com/andrewnicols/moodle into MOODLE_403_STABLE 2025-02-20 11:45:44 +08:00
Jun Pataleta 58b9066111 Merge branch 'MDL-84590-403' of https://github.com/andrewnicols/moodle into MOODLE_403_STABLE 2025-02-20 10:50:01 +08:00
Andrew Nicols eac184c754 MDL-84592 tool_behat: Improve accessibility failure information 2025-02-20 10:26:38 +08:00
Huong Nguyen dca015783d Merge branch 'MDL-84498-403' of https://github.com/lameze/moodle into MOODLE_403_STABLE 2025-02-20 09:22:42 +07:00
Andrew Nicols b086bc5387 MDL-84590 tool_behat: Axe tests must use all wcag rules
Rules are not additive. That is to say that the wcag22a tag does not
include wcag21a or wcag2a. They all need to be specified.
2025-02-20 09:59:56 +08:00
Jason Den Dulk b82a60b3cf MDL-84498 lib: Fix reflection property regression. 2025-02-20 09:35:26 +08:00
Jun Pataleta 5e7cde3baf weekly release 4.3.10+ 2025-02-14 09:54:00 +08:00
Jun Pataleta cbb94d0f87 Merge branch 'install_403_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_403_STABLE 2025-02-14 09:53:58 +08:00
Jun Pataleta 5f3057cb7c Merge branch 'MDL-84102-403' of https://github.com/andelacruz/moodle into MOODLE_403_STABLE 2025-02-13 11:49:03 +08:00
Angelia Dela Cruz 71e551b03f MDL-84102 message: Behat for setting default notification preferences 2025-02-12 15:36:49 +08:00
Huong Nguyen 69f3c42284 Merge branch 'MDL-84414-403' of https://github.com/andelacruz/moodle into MOODLE_403_STABLE 2025-02-11 11:18:00 +07:00
Angelia Dela Cruz 735f25dceb MDL-84414 gradingform_guide: Behat test to set marking guide as ready 2025-02-11 11:15:19 +08:00
AMOS bot 4725303f8c Automatically generated installer lang files 2025-02-11 00:09:15 +00:00
86 changed files with 2113 additions and 368 deletions
+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");
}
}
@@ -0,0 +1,79 @@
@tool @tool_analytics
Feature: Manager can obtain prediction models insights
In order to view prediction models insights
As a manager
I should be able to obtain the prediction models insights
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| s1 | Student | One | s1@example.com |
| m1 | Manager | One | m1@example.com |
| m2 | Manager | Two | m2@example.com |
| t1 | Teacher | One | t1@example.com |
And the following "role assigns" exist:
| user | role | contextlevel | reference |
| m1 | manager | System | |
| m2 | manager | System | |
# To ensure that course start date is in the future, set course start date to "tomorrow".
And the following "courses" exist:
| fullname | shortname | startdate |
| Course 1 | C1 | ##tomorrow## |
| Course 2 | C2 | ##tomorrow## |
| Course 3 | C3 | ##tomorrow## |
And the following "course enrolments" exist:
| user | course | role |
| s1 | C1 | student |
| s1 | C2 | student |
| s1 | C3 | student |
| t1 | C3 | editingteacher |
# Disable "Analytics processes execution via command line only".
And the following config values are set as admin:
| onlycli | 0 | analytics |
@javascript
Scenario: Manager can obtain prediction models insights
Given I log in as "m1"
And I navigate to "Analytics > Analytics models" in site administration
And I click on "Actions" "link" in the "Courses at risk of not starting" "table_row"
And I click on "Execute scheduled analysis" "link" in the "Courses at risk of not starting" "table_row"
And I press "Continue"
When I select "All predictions" from the "contextid" singleselect
# Confirm that only courses without teachers, Course 1 and Course 2, are listed.
Then "Course 1" "text" should exist
And "Course 2" "text" should exist
And "Course 3" "text" should not exist
And I click on "Select Course 1 for bulk action" "checkbox"
And I press "Not applicable"
And I press "Confirm"
# After m1 marks Course 1 as Not applicable, only Course 2 should be listed in the predictions for manager 1.
And "Course 1" "text" should not exist
And "Course 2" "text" should exist
And I log in as "m2"
And I click on ".popover-region-notifications" "css_element"
# Notification of new insight exists.
And "Upcoming courses have no teachers or students" "text" should exist
And I click on "View full notification" "link" in the ".popover-region-notifications" "css_element"
And I click on "View insight" "link"
# Course 1, previously marked as Not applicable by manager 1, is still listed for manager 2.
And "Course 1" "text" should exist
And I click on "View details" "link" in the "Course 1" "table_row"
# Prediction details and indicators are displayed for the selected course.
And the following should exist in the "insights-list" table:
| Description |
| Course 1 |
And the following should exist in the "prediction-calculations" table:
| -1- | -2- |
| Teacher availability | No |
| Student enrolments | Yes |
# Date of prediction analysis execution.
And "##today##%A, %d %B %Y##" "text" should exist in the "Time predicted" "table_row"
And I click on "Select Course 1 for bulk action" "checkbox"
And I press "Accept"
And I press "Confirm"
# Verify that you end up on the page listing predictions for 'No teaching'.
And "Courses at risk of not starting" "text" should exist
And "The following courses due to start in the upcoming days are at risk of not starting because they don't have teachers or students enrolled." "text" should exist
And the following should not exist in the "insights-list" table:
| Description |
| Course 1 |
+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());
@@ -4,13 +4,13 @@ Feature: See the competencies for an activity on the course competencies page.
In order to see only the competencies for an activity in the course competencies page.
Background:
Given the following lp "frameworks" exist:
| shortname | idnumber |
| Test-Framework | ID-FW1 |
And the following lp "competencies" exist:
| shortname | framework |
| Test-Comp1 | ID-FW1 |
| Test-Comp2 | ID-FW1 |
Given the following "core_competency > frameworks" exist:
| shortname | idnumber |
| Test-Framework | ID-FW1 |
And the following "core_competency > competencies" exist:
| shortname | competencyframework |
| Test-Comp1 | ID-FW1 |
| Test-Comp2 | ID-FW1 |
Given the following "courses" exist:
| shortname | fullname | enablecompletion |
| C1 | Course 1 | 1 |
@@ -29,18 +29,18 @@ Feature: Manage competency frameworks
And I should see "Science Year-1"
Scenario: Read a framework
Given the following lp "frameworks" exist:
| shortname | idnumber |
| Science Year-2 | sc-y-2 |
Given the following "core_competency > frameworks" exist:
| shortname | idnumber |
| Science Year-2 | sc-y-2 |
And I navigate to "Competencies > Competency frameworks" in site administration
And I should see "Science Year-2"
When I click on "Science Year-2" "link"
Then I should see "Science Year-2"
Scenario: Edit a framework
Given the following lp "frameworks" exist:
| shortname | idnumber |
| Science Year-3 | sc-y-3 |
Given the following "core_competency > frameworks" exist:
| shortname | idnumber |
| Science Year-3 | sc-y-3 |
And I navigate to "Competencies > Competency frameworks" in site administration
And I should see "Science Year-3"
And I click on "Edit" of edit menu in the "Science Year-3" row
@@ -52,9 +52,9 @@ Feature: Manage competency frameworks
And I should see "sc-y-3"
Scenario: Delete a framework
Given the following lp "frameworks" exist:
| shortname | idnumber |
| Science Year-4 | sc-y-4 |
Given the following "core_competency > frameworks" exist:
| shortname | idnumber |
| Science Year-4 | sc-y-4 |
And I navigate to "Competencies > Competency frameworks" in site administration
And I should see "Science Year-4"
And I should see "sc-y-4"
@@ -70,24 +70,24 @@ Feature: Manage competency frameworks
And I should not see "sc-y-4"
Scenario: Edit a framework with competencies in user competency
Given the following lp "frameworks" exist:
| shortname | idnumber |
| Science Year-5 | sc-y-5 |
And the following lp "competencies" exist:
| shortname | framework |
| Comp1 | sc-y-5 |
| Comp2 | sc-y-5 |
And the following lp "plans" exist:
| name | user | description |
Given the following "core_competency > frameworks" exist:
| shortname | idnumber |
| Science Year-5 | sc-y-5 |
And the following "core_competency > competencies" exist:
| shortname | competencyframework | idnumber |
| Comp1 | sc-y-5 | Comp1 |
| Comp2 | sc-y-5 | Comp2 |
And the following "core_competency > plans" exist:
| name | user | description |
| Plan Science-5 | admin | Plan description |
And the following lp "plancompetencies" exist:
| plan | competency |
| Plan Science-5 | Comp1 |
| Plan Science-5 | Comp2 |
And the following lp "usercompetencies" exist:
| user | competency |
| admin | Comp1 |
| admin | Comp2 |
And the following "core_competency > plan_competency" exist:
| plan | competency |
| Plan Science-5 | Comp1 |
| Plan Science-5 | Comp2 |
And the following "core_competency > user_competency" exist:
| user | competency |
| admin | Comp1 |
| admin | Comp2 |
And I navigate to "Competencies > Competency frameworks" in site administration
And I should see "Science Year-5"
And I click on "Edit" of edit menu in the "Science Year-5" row
@@ -100,24 +100,24 @@ Feature: Manage competency frameworks
And I should see "sc-y-5"
Scenario: Edit a framework with competencies in user competency plan
Given the following lp "frameworks" exist:
| shortname | idnumber |
| Science Year-6 | sc-y-6 |
And the following lp "competencies" exist:
| shortname | framework |
| Comp1 | sc-y-6 |
| Comp2 | sc-y-6 |
And the following lp "plans" exist:
| name | user | description |
Given the following "core_competency > frameworks" exist:
| shortname | idnumber |
| Science Year-6 | sc-y-6 |
And the following "core_competency > competencies" exist:
| shortname | competencyframework | idnumber |
| Comp1 | sc-y-6 | Comp1 |
| Comp2 | sc-y-6 | Comp2 |
And the following "core_competency > plans" exist:
| name | user | description |
| Plan Science-6 | admin | Plan description |
And the following lp "plancompetencies" exist:
| plan | competency |
| Plan Science-6 | Comp1 |
| Plan Science-6 | Comp2 |
And the following lp "usercompetencyplans" exist:
| user | competency | plan |
| admin | Comp1 | Plan Science-6 |
| admin | Comp2 | Plan Science-6 |
And the following "core_competency > plan_competency" exist:
| plan | competency |
| Plan Science-6 | Comp1 |
| Plan Science-6 | Comp2 |
And the following "core_competency > user_competency_plans" exist:
| user | competency | plan |
| admin | Comp1 | Plan Science-6 |
| admin | Comp2 | Plan Science-6 |
And I navigate to "Competencies > Competency frameworks" in site administration
And I should see "Science Year-6"
And I click on "Edit" of edit menu in the "Science Year-6" row
@@ -130,12 +130,12 @@ Feature: Manage competency frameworks
And I should see "sc-y-6"
Scenario: Duplicate a competency framework
Given the following lp "frameworks" exist:
Given the following "core_competency > frameworks" exist:
| shortname | idnumber |
| CF1 | CF1 |
And the following lp "competencies" exist:
| shortname | framework |
| C1 | CF1 |
And the following "core_competency > competencies" exist:
| shortname | competencyframework |
| C1 | CF1 |
And I navigate to "Competencies > Competency frameworks" in site administration
# Duplicate the selected competency framework
When I click on "Duplicate" of edit menu in the "CF1" row
@@ -0,0 +1,69 @@
@tool @tool_lp @tool_lp_plan
Feature: Admin can create learning plans from template
In order to link and unlink a learning plan from a student
As an admin
I need to be able to create a learning plan from template
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | One | student1@email.com |
And the following "core_competency > frameworks" exist:
| shortname | idnumber |
| CF1 | CF1 |
| CF2 | CF2 |
And the following "core_competency > competencies" exist:
| shortname | competencyframework | idnumber |
| CF1 Competency 1 | CF1 | CF1C1 |
| CF2 Competency 1 | CF2 | CF2C1 |
And the following "core_competency > templates" exist:
| shortname | description |
| LPT1 | LPT 1 description |
And the following "core_competency > template_competencies" exist:
| template | competency |
| LPT1 | CF1C1 |
@javascript
Scenario: Admin can assign competencies from different frameworks to a learning plan
Given I log in as "admin"
And I navigate to "Competencies > Learning plan templates" in site administration
And I follow "LPT1"
When I press "Add competencies to learning plan template"
# Add another competency from a different framework in the learning plan template.
And I set the field with xpath "//select[@data-action='chooseframework']" to "CF2 CF2"
And I select "CF2 Competency 1" of the competency tree
And I click on "Add" "button" in the "Competency picker" "dialogue"
# Confirm that both competencies from different frameworks are successfully added to learning plan template.
Then "LPT1" "text" should exist
And "Learning plan template competencies" "text" should exist
And "CF1 Competency 1" "text" should exist
And "CF2 Competency 1" "text" should exist
@javascript
Scenario: Admin can link and unlink learning plans created from template
Given I log in as "admin"
And I navigate to "Competencies > Learning plan templates" in site administration
# Initially, the number of learning plans is 0.
And the following should exist in the "generaltable" table:
| Name | Learning plans |
| LPT1 | 0 |
And I click on ".template-userplans" "css_element" in the "LPT1" "table_row"
# Create a learning plan for selected student using template.
When I set the field "Select users" to "student1"
And I press "Create learning plans"
# Confirm that selected student is now in the list of learning plans.
Then "A learning plan was created" "text" should exist
And the following should exist in the "generaltable" table:
| Name | Email address |
| LPT1 | student1@email.com |
And I click on "LPT1" "link" in the "LPT1" "table_row"
# Template title and Unlink from learning plan template link exists.
And "Learning plan template" "text" should exist
And "Unlink from learning plan template" "link" should exist
# Unlink the template.
And I click on "Unlink from learning plan template" "link"
And I press "Unlink from learning plan template"
# Learning plan still exists but Template name and Unlink from learning plan template link no longer exist.
And "LPT1" "text" should exist
And "Learning plan template" "text" should not exist
And "Unlink from learning plan template" "link" should not exist
+26 -26
View File
@@ -22,8 +22,8 @@ Feature: Manage plearning plan
And I should see "Science plan"
Scenario: Create a learning plan based on template
Given the following lp "templates" exist:
| shortname | description |
Given the following "core_competency > templates" exist:
| shortname | description |
| Science template | science template description |
And I am on homepage
And I navigate to "Competencies > Learning plan templates" in site administration
@@ -36,8 +36,8 @@ Feature: Manage plearning plan
And I should see "Admin User" in the "Science template" "table_row"
Scenario: Create a learning plan from template cohort
Given the following lp "templates" exist:
| shortname | description |
Given the following "core_competency > templates" exist:
| shortname | description |
| Science template cohort | science template description |
And the following "users" exist:
| username | firstname | lastname | email |
@@ -62,8 +62,8 @@ Feature: Manage plearning plan
And I should see "Student 2"
Scenario: Read a learning plan
Given the following lp "plans" exist:
| name | user | description |
Given the following "core_competency > plans" exist:
| name | user | description |
| Science plan Year-1 | admin | science plan description |
And I follow "Learning plans"
And I should see "Science plan Year-1"
@@ -72,16 +72,16 @@ Feature: Manage plearning plan
And I should see "Learning plan competencies"
Scenario: Manage a learning plan competencies
Given the following lp "plans" exist:
| name | user | description |
Given the following "core_competency > plans" exist:
| name | user | description |
| Science plan Year-manage | admin | science plan description |
And the following lp "frameworks" exist:
And the following "core_competency > frameworks" exist:
| shortname | idnumber |
| Framework 1 | sc-y-2 |
And the following lp "competencies" exist:
| shortname | framework |
| comp1 | sc-y-2 |
| comp2 | sc-y-2 |
And the following "core_competency > competencies" exist:
| shortname | competencyframework |
| comp1 | sc-y-2 |
| comp2 | sc-y-2 |
And I follow "Learning plans"
And I should see "Science plan Year-manage"
And I follow "Science plan Year-manage"
@@ -98,8 +98,8 @@ Feature: Manage plearning plan
And "comp1" "table_row" should not exist
Scenario: Edit a learning plan
Given the following lp "plans" exist:
| name | user | description |
Given the following "core_competency > plans" exist:
| name | user | description |
| Science plan Year-2 | admin | science plan description |
| Science plan Year-3 | admin | science plan description |
And I follow "Learning plans"
@@ -113,8 +113,8 @@ Feature: Manage plearning plan
And I should see "Science plan Year-3 Edited"
Scenario: Delete a learning plan
Given the following lp "plans" exist:
| name | user | description |
Given the following "core_competency > plans" exist:
| name | user | description |
| Science plan Year-4 | admin | science plan description |
And I follow "Learning plans"
And I should see "Science plan Year-4"
@@ -130,16 +130,16 @@ Feature: Manage plearning plan
Then I should not see "Science plan Year-4"
Scenario: See a learning plan from a course
Given the following lp "plans" exist:
| name | user | description |
Given the following "core_competency > plans" exist:
| name | user | description |
| Science plan Year-manage | admin | science plan description |
And the following lp "frameworks" exist:
| shortname | idnumber |
| Framework 1 | sc-y-2 |
And the following lp "competencies" exist:
| shortname | framework |
| comp1 | sc-y-2 |
| comp2 | sc-y-2 |
And the following "core_competency > frameworks" exist:
| shortname | idnumber |
| Framework 1 | sc-y-2 |
And the following "core_competency > competencies" exist:
| shortname | competencyframework |
| comp1 | sc-y-2 |
| comp2 | sc-y-2 |
And I follow "Learning plans"
And I should see "Science plan Year-manage"
And I follow "Science plan Year-manage"
+28 -28
View File
@@ -31,19 +31,19 @@ Feature: Manage plan workflow
| user1 | usermanageowndraftplan | System | |
| user2 | usermanageownplan | System | |
| manager1 | manageplan | System | |
And the following lp "frameworks" exist:
| shortname | idnumber |
| Test-Framework | ID-FW1 |
And the following lp "competencies" exist:
| shortname | framework |
| Test-Comp1 | ID-FW1 |
| Test-Comp2 | ID-FW1 |
And the following lp "plans" exist:
| name | user | description |
| Test-Plan1 | user1 | Description of plan for user 1 |
| Test-Plan2 | user2 | Description of plan for user 2 |
And the following lp "plancompetencies" exist:
| plan | competency |
And the following "core_competency > frameworks" exist:
| shortname | idnumber |
| Test-Framework | ID-FW1 |
And the following "core_competency > competencies" exist:
| shortname | competencyframework | idnumber |
| Test-Comp1 | ID-FW1 | Test-Comp1 |
| Test-Comp2 | ID-FW1 | Test-Comp2 |
And the following "core_competency > plans" exist:
| name | user | description | reviewer | status |
| Test-Plan1 | user1 | Description of plan for user 1 | user1 | draft |
| Test-Plan2 | user2 | Description of plan for user 2 | user2 | draft |
And the following "core_competency > plan_competency" exist:
| plan | competency |
| Test-Plan1 | Test-Comp1 |
| Test-Plan1 | Test-Comp2 |
| Test-Plan2 | Test-Comp1 |
@@ -89,18 +89,18 @@ Feature: Manage plan workflow
And I log out
Scenario: Manager can see learning plan with status waiting for review
Given the following lp "plans" exist:
| name | user | description | status |
Given the following "core_competency > plans" exist:
| name | user | description | status |
| Test-Plan3 | user2 | Description of plan 3 for user 1 | waiting for review |
| Test-Plan4 | user1 | Description of plan 3 for user 1 | draft |
| Test-Plan4 | user1 | Description of plan 3 for user 1 | draft |
When I log in as "manager1"
Then I should see "Test-Plan3"
And I should not see "Test-Plan4"
And I log out
Scenario: Manager can start review of learning plan with status waiting for review
Given the following lp "plans" exist:
| name | user | description | status |
Given the following "core_competency > plans" exist:
| name | user | description | status |
| Test-Plan3 | user1 | Description of plan 3 for user 1 | waiting for review |
And I log in as "manager1"
And I follow "Test-Plan3"
@@ -111,8 +111,8 @@ Feature: Manage plan workflow
And I log out
Scenario: Manager can reject a learning plan with status in review
Given the following lp "plans" exist:
| name | user | description | status | reviewer |
Given the following "core_competency > plans" exist:
| name | user | description | status | reviewer |
| Test-Plan3 | user1 | Description of plan 3 for user 1 | in review | manager1 |
And I log in as "manager1"
And I follow "Test-Plan3"
@@ -124,8 +124,8 @@ Feature: Manage plan workflow
And I log out
Scenario: Manager can accept a learning plan with status in review
Given the following lp "plans" exist:
| name | user | description | status | reviewer |
Given the following "core_competency > plans" exist:
| name | user | description | status | reviewer |
| Test-Plan3 | user1 | Description of plan 3 for user 1 | in review | manager1 |
And I log in as "manager1"
And I follow "Test-Plan3"
@@ -137,8 +137,8 @@ Feature: Manage plan workflow
And I log out
Scenario: Manager send back to draft an active learning plan
Given the following lp "plans" exist:
| name | user | description | status | reviewer |
Given the following "core_competency > plans" exist:
| name | user | description | status | reviewer |
| Test-Plan3 | user1 | Description of plan 3 for user 1 | active | manager1 |
| Test-Plan4 | user1 | Description of plan 4 for user 1 | active | manager1 |
And I am on the "user1" "user > profile" page logged in as "manager1"
@@ -153,8 +153,8 @@ Feature: Manage plan workflow
And I log out
Scenario: Manager change an active learning plan to completed
Given the following lp "plans" exist:
| name | user | description | status | reviewer |
Given the following "core_competency > plans" exist:
| name | user | description | status | reviewer |
| Test-Plan3 | user1 | Description of plan 3 for user 1 | active | manager1 |
| Test-Plan4 | user1 | Description of plan 4 for user 1 | active | manager1 |
And I am on the "user1" "user > profile" page logged in as "manager1"
@@ -172,8 +172,8 @@ Feature: Manage plan workflow
And I log out
Scenario: Manager reopen a complete learning plan
Given the following lp "plans" exist:
| name | user | description | status | reviewer |
Given the following "core_competency > plans" exist:
| name | user | description | status | reviewer |
| Test-Plan3 | user1 | Description of plan 3 for user 1 | complete | manager1 |
| Test-Plan4 | user1 | Description of plan 4 for user 1 | complete | manager1 |
And I am on the "user1" "user > profile" page logged in as "manager1"
@@ -21,8 +21,8 @@ Feature: Manage plearning plan templates
And I should see "Science template"
Scenario: Read a learning plan template
Given the following lp "templates" exist:
| shortname | description |
Given the following "core_competency > templates" exist:
| shortname | description |
| Science template Year-2 | science template description |
And I navigate to "Competencies > Learning plan templates" in site administration
And I should see "Science template Year-2"
@@ -31,8 +31,8 @@ Feature: Manage plearning plan templates
And I should see "Learning plan template competencies"
Scenario: Edit a learning plan template
Given the following lp "templates" exist:
| shortname | description |
Given the following "core_competency > templates" exist:
| shortname | description |
| Science template Year-3 | science template description |
And I navigate to "Competencies > Learning plan templates" in site administration
And I should see "Science template Year-3"
@@ -44,8 +44,8 @@ Feature: Manage plearning plan templates
And I should see "Science template Year-3 Edited"
Scenario: Delete a learning plan template
Given the following lp "templates" exist:
| shortname | description |
Given the following "core_competency > templates" exist:
| shortname | description |
| Science template Year-4 | science template description |
And I navigate to "Competencies > Learning plan templates" in site administration
And I should see "Science template Year-4"
@@ -5,22 +5,22 @@ Feature: Manage competencies linked to evidence of prior learning
I need to link and unlink competencies from evidence of prior learning
Background:
Given the following lp "frameworks" exist:
| shortname | idnumber |
| Test-Framework | ID-FW1 |
And the following lp "competencies" exist:
| shortname | framework |
| Test-Comp1 | ID-FW1 |
| Test-Comp2 | ID-FW1 |
And the following lp "plans" exist:
| name | user | description |
Given the following "core_competency > frameworks" exist:
| shortname | idnumber |
| Test-Framework | ID-FW1 |
And the following "core_competency > competencies" exist:
| shortname | competencyframework | idnumber |
| Test-Comp1 | ID-FW1 | Test-Comp1 |
| Test-Comp2 | ID-FW1 | Test-Comp2 |
And the following "core_competency > plans" exist:
| name | user | description |
| Test-Plan | admin | Plan description |
And the following lp "plancompetencies" exist:
| plan | competency |
And the following "core_competency > plan_competency" exist:
| plan | competency |
| Test-Plan | Test-Comp1 |
| Test-Plan | Test-Comp2 |
And the following lp "userevidence" exist:
| name | description | user |
And the following "core_competency > user_evidence" exist:
| name | description | user |
| Test-Evidence | Description evidence | admin |
When I log in as "admin"
And I follow "Profile" in the user menu
@@ -51,12 +51,12 @@ Feature: Manage competencies linked to evidence of prior learning
Then "Test-Comp2" "table_row" should exist
Scenario: Unlink competency from evidence of prior learning
Given the following lp "userevidencecompetencies" exist:
| userevidence | competency |
Given the following "core_competency > user_evidence_competency" exist:
| userevidence | competency |
| Test-Evidence | Test-Comp1 |
| Test-Evidence | Test-Comp2 |
Given the following lp "usercompetencies" exist:
| user | competency |
And the following "core_competency > user_competency" exist:
| user | competency |
| admin | Test-Comp1 |
| admin | Test-Comp2 |
And I follow "Evidence of prior learning"
@@ -24,8 +24,8 @@ Feature: Manage evidence of prior learning
And I should see "Evidence-1"
Scenario: Read an evidence of prior learning
Given the following lp "userevidence" exist:
| name | description | user |
Given the following "core_competency > user_evidence" exist:
| name | description | user |
| Evidence-2 | Evidence-2 description | user1 |
And I follow "Evidence of prior learning"
And I should see "List of evidence"
@@ -34,8 +34,8 @@ Feature: Manage evidence of prior learning
Then I should see "Evidence-2"
Scenario: Edit an evidence of prior learning
Given the following lp "userevidence" exist:
| name | description | user |
Given the following "core_competency > user_evidence" exist:
| name | description | user |
| Evidence-3 | Evidence-3 description | user1 |
And I follow "Evidence of prior learning"
And I should see "List of evidence"
@@ -47,8 +47,8 @@ Feature: Manage evidence of prior learning
And I should see "Evidence-3 Edited"
Scenario: Delete an evidence of prior learning
Given the following lp "userevidence" exist:
| name | description | user |
Given the following "core_competency > user_evidence" exist:
| name | description | user |
| Evidence-4 | Evidence-4 description | user1 |
And I follow "Evidence of prior learning"
And I should see "List of evidence"
@@ -62,10 +62,10 @@ Feature: Manage evidence of prior learning
Scenario: List evidences of prior learning
Given the following "users" exist:
| username | firstname | lastname | email |
|user2 | User | 2 | user2@example.com |
And the following lp "userevidence" exist:
| name | description | user |
| username | firstname | lastname | email |
| user2 | User | 2 | user2@example.com |
And the following "core_competency > user_evidence" exist:
| name | description | user |
| Evidence-5 | Evidence-5 description | user1 |
| Evidence-6 | Evidence-6 description | user2 |
And I follow "Evidence of prior learning"
@@ -40,9 +40,9 @@ Feature: View competencies
| competency | relatedcompetency |
| desserts | cakes |
And the following "core_competency > plans" exist:
| name | description | competencies | user |
| Cookery | Cookery is important | salads, desserts, cakes | student1 |
| Literacy | Literacy is important | reading, writing | student1 |
| name | description | competencies | user | status |
| Cookery | Cookery is important | salads, desserts, cakes | student1 | active |
| Literacy | Literacy is important | reading, writing | student1 | active |
And the following "core_competency > course_competencies" exist:
| course | competency |
| C1 | salads |
+43 -12
View File
@@ -41,6 +41,18 @@ class manager {
/** @var int */
const REDIR_LOOP_THRESHOLD = 5;
/** @var array These components and related fileareas will not redirect. */
const ALLOWED_COMPONENTS = [
'core_admin' => [
'logocompact',
'logo',
'favicon',
],
'tool_mfa' => [
'guidance',
],
];
/**
* Displays a debug table with current factor information.
*
@@ -424,6 +436,37 @@ class manager {
return self::NO_REDIRECT;
}
// Ensure we have a moodle_url object if a string is provided.
if (is_string($url)) {
$url = new \moodle_url($url);
}
// Check for pluginfile.php urls.
$pluginfileurl = new \moodle_url('/pluginfile.php');
if ($url->compare($pluginfileurl)) {
// Get the slash arguments.
$args = explode('/', ltrim($url->get_slashargument(), '/'));
// Remove the contextid because we do not need it for this check.
array_shift($args);
// Get the component and filearea.
$component = clean_param(array_shift($args), PARAM_COMPONENT);
$filearea = clean_param(array_shift($args), PARAM_AREA);
// Check allowed components.
if (!array_key_exists($component, static::ALLOWED_COMPONENTS)) {
return self::REDIRECT;
}
// Check allowed fileareas.
if (!in_array($filearea, static::ALLOWED_COMPONENTS[$component])) {
return self::REDIRECT;
}
return self::NO_REDIRECT;
}
// Remove all params before comparison.
$url->remove_all_params();
@@ -442,12 +485,6 @@ class manager {
}
}
// Dont redirect logo images from pluginfile.php (for example: logo in header).
$logourl = new \moodle_url('/pluginfile.php/1/core_admin/logocompact/');
if ($url->compare($logourl)) {
return self::NO_REDIRECT;
}
// Admin not setup.
if (!empty($CFG->adminsetuppending)) {
return self::NO_REDIRECT;
@@ -469,12 +506,6 @@ class manager {
return self::NO_REDIRECT;
}
// Enrolment.
$enrol = new \moodle_url('/enrol/index.php');
if ($enrol->compare($url, URL_MATCH_BASE)) {
return self::NO_REDIRECT;
}
// Guest access.
if (isguestuser()) {
return self::NO_REDIRECT;
@@ -41,7 +41,8 @@ class factor_email_renderer extends plugin_renderer_base {
$authurl = new \moodle_url('/admin/tool/mfa/factor/email/email.php',
['instance' => $instance->id, 'pass' => 1, 'secret' => $instance->secret]);
$authurlstring = \html_writer::link($authurl, get_string('email:link', 'factor_email'));
$blockurl = new \moodle_url('/admin/tool/mfa/factor/email/email.php', ['instance' => $instanceid]);
$blockurl = new \moodle_url('/admin/tool/mfa/factor/email/email.php',
['instance' => $instance->id, 'secret' => $instance->secret]);
$blockurlstring = \html_writer::link($blockurl, get_string('email:stoploginlink', 'factor_email'));
$geoinfo = iplookup_find_location($instance->createdfromip);
+8 -3
View File
@@ -42,7 +42,8 @@ $PAGE->set_cacheable(false);
$instance = $DB->get_record('tool_mfa', ['id' => $instanceid]);
$factor = \tool_mfa\plugininfo\factor::get_factor('email');
// If pass is set, require login to force $SESSION and user, and pass for that session.
// If pass is set, do checks and pass for this session.
// Require login to force $SESSION and user, and pass for that session.
if (!empty($instance) && $pass != 0 && $secret != 0) {
require_login();
if ($factor->get_state() === \tool_mfa\plugininfo\factor::STATE_LOCKED) {
@@ -69,8 +70,12 @@ $form = new \factor_email\form\email($url);
if ($form->is_cancelled()) {
redirect(new moodle_url('/'));
} else if ($fromform = $form->get_data()) {
if (empty($instance)) {
}
// If submitted without the pass param, is a cancel request - do checks and revoke email factor.
if ($fromform = $form->get_data()) {
// Only allow revoke attempts from requests with a valid instance and secret.
if (empty($instance) || empty($secret) || $instance->secret != $secret) {
$message = get_string('error:badcode', 'factor_email');
} else {
$user = $DB->get_record('user', ['id' => $instance->userid]);
@@ -33,7 +33,7 @@ $string['email:loginlink'] = 'Or, if you\'re on the same device, use this {$a}.'
$string['email:message'] = 'Here\'s your verification code for {$a->sitename} ({$a->siteurl}).';
$string['email:originatingip'] = 'This login request was made from \'{$a}\'';
$string['email:revokelink'] = 'If this wasn\'t you, you can {$a}.';
$string['email:revokesuccess'] = 'This code has been successfully revoked. All sessions for {$a} have been ended.
$string['email:revokesuccess'] = 'This code has been successfully revoked. All sessions for this user have been ended.
Email will not be usable as a factor until account security has been verified.';
$string['email:subject'] = 'Here\'s your verification code';
$string['email:stoploginlink'] = 'stop this login attempt';
+1 -1
View File
@@ -29,7 +29,7 @@ require_once(__DIR__ . '/../../../config.php');
$PAGE->set_context(\context_system::instance());
$PAGE->set_url(new moodle_url('/admin/tool/mfa/guide.php'));
$PAGE->set_title(get_string('guidance', 'tool_mfa'));
$PAGE->set_pagelayout('standard');
$PAGE->set_pagelayout('secure');
// If guidance page isn't enabled, just redir back to home.
if (!get_config('tool_mfa', 'guidance')) {
+3 -3
View File
@@ -127,8 +127,8 @@ function tool_mfa_bulk_user_actions(): array {
/**
* Serves any files for the guidance page.
*
* @param stdClass $course
* @param stdClass $cm
* @param stdClass|null $course
* @param stdClass|null $cm
* @param context $context
* @param string $filearea
* @param array $args
@@ -136,7 +136,7 @@ function tool_mfa_bulk_user_actions(): array {
* @param array $options
* @return bool
*/
function tool_mfa_pluginfile(stdClass $course, stdClass $cm, context $context, string $filearea,
function tool_mfa_pluginfile(stdClass|null $course, stdClass|null $cm, context $context, string $filearea,
array $args, bool $forcedownload, array $options = []): bool {
// Hardcode to only send guidance files from the top level.
$fs = get_file_storage();
+1 -1
View File
@@ -23,6 +23,6 @@
{
}
}}
<a href='{{config.wwwroot}}/admin/tool/mfa/guide.php'>
<a href='{{config.wwwroot}}/admin/tool/mfa/guide.php' target='_blank'>
{{#str}} guidance, tool_mfa {{/str}}
</a>
+21 -5
View File
@@ -241,11 +241,6 @@ final class manager_test extends \advanced_testcase {
$this->assertEquals(\tool_mfa\manager::NO_REDIRECT, \tool_mfa\manager::should_require_mfa($badurl, false));
$this->setUser($user);
// Enrolment.
$enrolurl = new \moodle_url('/enrol/index.php');
$this->assertEquals(\tool_mfa\manager::REDIRECT, \tool_mfa\manager::should_require_mfa($badurl, false));
$this->assertEquals(\tool_mfa\manager::NO_REDIRECT, \tool_mfa\manager::should_require_mfa($enrolurl, false));
// Guest User.
$this->assertEquals(\tool_mfa\manager::REDIRECT, \tool_mfa\manager::should_require_mfa($badurl, false));
$this->setGuestUser();
@@ -266,6 +261,27 @@ final class manager_test extends \advanced_testcase {
\core\session\manager::loginas($user2->id, $syscontext, false);
$this->assertEquals(\tool_mfa\manager::NO_REDIRECT, \tool_mfa\manager::should_require_mfa($badurl, false));
$this->setUser($user);
// Access logocompact via pluginfile.
$logourl = new \moodle_url('/pluginfile.php/1/core_admin/logocompact/');
$this->assertEquals(\tool_mfa\manager::NO_REDIRECT, \tool_mfa\manager::should_require_mfa($logourl, false));
// Access logo via pluginfile.
$logourl = new \moodle_url('/pluginfile.php/1/core_admin/logo/');
$this->assertEquals(\tool_mfa\manager::NO_REDIRECT, \tool_mfa\manager::should_require_mfa($logourl, false));
// Access favicon via pluginfile.
$logourl = new \moodle_url('/pluginfile.php/1/core_admin/favicon/');
$this->assertEquals(\tool_mfa\manager::NO_REDIRECT, \tool_mfa\manager::should_require_mfa($logourl, false));
// Access guidance files.
$guideurl = new \moodle_url('/pluginfile.php/1/tool_mfa/guidance/0/capybara.png');
$this->assertEquals(\tool_mfa\manager::NO_REDIRECT, \tool_mfa\manager::should_require_mfa($guideurl, false));
// Access private area.
$user3 = $this->getDataGenerator()->create_user();
$privateurl = new \moodle_url("/pluginfile.php/{$user3->id}/user/private/privatefile.png");
$this->assertEquals(\tool_mfa\manager::REDIRECT, \tool_mfa\manager::should_require_mfa($privateurl, false));
}
/**
+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
@@ -5262,8 +5262,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);
}
@@ -14,13 +14,13 @@ Feature: Award badges based on competency completion
And the following "course enrolments" exist:
| user | course | role |
| user1 | C1 | student |
And the following lp "frameworks" exist:
| shortname | idnumber |
| Framework 1 | sc-y-2 |
And the following lp "competencies" exist:
| shortname | framework |
| comp1 | sc-y-2 |
| comp2 | sc-y-2 |
And the following "core_competency > frameworks" exist:
| shortname | idnumber |
| Framework 1 | sc-y-2 |
And the following "core_competency > competencies" exist:
| shortname | competencyframework |
| comp1 | sc-y-2 |
| comp2 | sc-y-2 |
And I log in as "admin"
@javascript
+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)) {
@@ -57,7 +57,13 @@ class behat_core_competency_generator extends behat_generator_base {
'singular' => 'plan',
'datagenerator' => 'plan',
'required' => ['name'],
'switchids' => ['user' => 'userid'],
'switchids' => ['user' => 'userid', 'status' => 'status'],
],
'plan_competencies' => [
'singular' => 'plan_competency',
'datagenerator' => 'plan_competency',
'required' => ['plan', 'competency'],
'switchids' => ['competency' => 'competencyid', 'plan' => 'planid'],
],
'related_competencies' => [
'singular' => 'related_competency',
@@ -83,14 +89,38 @@ class behat_core_competency_generator extends behat_generator_base {
'required' => ['plan', 'competency', 'user'],
'switchids' => ['plan' => 'planid', 'competency' => 'competencyid', 'user' => 'userid'],
],
'user_evidence' => [
'singular' => 'user_evidence',
'datagenerator' => 'user_evidence',
'required' => ['user', 'name'],
'switchids' => ['user' => 'userid'],
],
'user_evidence_competency' => [
'singular' => 'user_evidence_competency',
'datagenerator' => 'user_evidence_competency',
'required' => ['userevidence', 'competency'],
'switchids' => ['userevidence' => 'userevidenceid', 'competency' => 'competencyid'],
],
'templates' => [
'singular' => 'template',
'datagenerator' => 'template',
'required' => ['shortname'],
'switchids' => ['context' => 'contextid'],
],
'template_competencies' => [
'singular' => 'template_competency',
'datagenerator' => 'template_competency',
'required' => ['template', 'competency'],
'switchids' => ['template' => 'templateid', 'competency' => 'competencyid'],
],
];
}
/**
* Get the competecy framework id using an idnumber.
* Get the competency framework id using an idnumber.
*
* @param string $idnumber
* @return int The competecy framework id
* @return int The competency framework id
*/
protected function get_competencyframework_id(string $idnumber): int {
global $DB;
@@ -103,10 +133,10 @@ class behat_core_competency_generator extends behat_generator_base {
}
/**
* Get the competecy id using an idnumber.
* Get the competency id using an idnumber.
*
* @param string $idnumber
* @return int The competecy id
* @return int The competency id
*/
protected function get_competency_id(string $idnumber): int {
global $DB;
@@ -135,15 +165,31 @@ class behat_core_competency_generator extends behat_generator_base {
}
/**
* Get the related competecy id using an idnumber.
* Get the related competency id using an idnumber.
*
* @param string $idnumber
* @return int The related competecy id
* @return int The related competency id
*/
protected function get_relatedcompetency_id(string $idnumber): int {
return $this->get_competency_id($idnumber);
}
/**
* Get the template id by shortname.
*
* @param string $shortname The template name.
* @return int
*/
protected function get_template_id(string $shortname): int {
global $DB;
if (!$id = $DB->get_field('competency_template', 'id', ['shortname' => $shortname])) {
throw new Exception('The specified template with name "' . $shortname . '" could not be found.');
}
return $id;
}
/**
* Add a plan.
*
@@ -208,6 +254,8 @@ class behat_core_competency_generator extends behat_generator_base {
* @return array Processed data.
*/
protected function preprocess_plan(array $data): array {
global $DB, $USER;
if (isset($data['competencies'])) {
$competencies = array_map('trim', str_getcsv($data['competencies']));
$data['competencyids'] = array_map([$this, 'get_competency_id'], $competencies);
@@ -215,11 +263,20 @@ class behat_core_competency_generator extends behat_generator_base {
unset($data['competencies']);
}
global $USER;
if (isset($data['reviewer'])) {
if (is_number($data['reviewer'])) {
$data['reviewerid'] = $data['reviewer'];
} else {
if (!$userid = $DB->get_field('user', 'id', ['username' => $data['reviewer']])) {
throw new Exception('The specified user "' . $data['reviewer'] . '" could not be found.');
}
$data['reviewerid'] = $userid;
}
unset($data['reviewer']);
}
return $data + [
'userid' => $USER->id,
'status' => plan::STATUS_ACTIVE,
];
}
@@ -258,4 +315,81 @@ class behat_core_competency_generator extends behat_generator_base {
protected function get_data_generator(): core_competency_generator {
return $this->componentdatagenerator;
}
/**
* Get the user evidence id using a name.
*
* @param string $name User evidence name.
* @return int The user evidence id
*/
protected function get_userevidence_id(string $name): int {
global $DB;
if (!$id = $DB->get_field('competency_userevidence', 'id', ['name' => $name])) {
throw new Exception('The specified user evidence with name "' . $name . '" could not be found.');
}
return $id;
}
/**
* Get the template competency id using a name.
*
* @param string $name Template competency name.
* @return int The template competency id
*/
protected function get_templatecompetency_id(string $name): int {
global $DB;
if (!$id = $DB->get_field('competency_template', 'id', ['name' => $name])) {
throw new Exception('The specified template competency with name "' . $name . '" could not be found.');
}
return $id;
}
/**
* Get the context id using a contextid.
*
* @param string $contextid Context id.
* @return int The context id
*/
protected function get_context_id(string $contextid): int {
global $DB;
if (!$id = $DB->get_field('context', 'id', ['id' => $contextid])) {
throw new Exception('The specified context with id "' . $contextid . '" could not be found.');
}
return $id;
}
/**
* Get the status id by status name.
*
* @param string $name Status name.
* @return int
*/
protected function get_status_id(string $name): int {
switch ($name) {
case 'draft':
$status = plan::STATUS_DRAFT;
break;
case 'in review':
$status = plan::STATUS_IN_REVIEW;
break;
case 'waiting for review':
$status = plan::STATUS_WAITING_FOR_REVIEW;
break;
case 'complete':
$status = plan::STATUS_COMPLETE;
break;
default:
$status = plan::STATUS_ACTIVE;
break;
}
return $status;
}
}
+12 -6
View File
@@ -4273,22 +4273,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
@@ -356,6 +356,9 @@ class stateactions {
// We need to get the latest modinfo on each iteration because the section numbers change.
$modinfo = get_fast_modinfo($course);
$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' => 4,
'format' => 'topics',
'formatoptions' => [],
'expectedsectionum' => 1,
],
'format theunittestdelete' => [
'sectionum' => 4,
'format' => 'theunittestdelete',
'formatoptions' => [],
'expectedsectionum' => 5,
],
'format theunittestdelete can delete' => [
'sectionum' => 4,
'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;
}
}
+6
View File
@@ -3838,6 +3838,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';
@@ -18,7 +18,7 @@ Feature: Teacher can edit a marking guide state
| activity | course | name | advancedgradingmethod_submissions |
| assign | C1 | Assign 1 | guide |
Scenario: Marking guide state can be changed to draft
Scenario Outline: Marking guide state can be changed
Given 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:
@@ -28,12 +28,12 @@ Feature: Teacher can edit a marking guide state
| Criterion name | Description for students | Description for markers | Maximum score |
| Grade Criteria 1 | Grade 1 description for students | Grade 1 description for markers | 70 |
| Grade Criteria 2 | Grade 2 description for students | Grade 2 description for markers | 30 |
And I press "Save marking guide and make it ready"
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."
And I should not see "Assign 1 marking guide Draft"
And I should see "Assign 1 marking guide Ready for use"
And I click on "Edit the current form definition" "link"
When I press "Save as draft"
Then I should 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."
And I should see "Assign 1 marking guide Draft"
And I should not see "Assign 1 marking guide Ready for use"
When I press "<buttonname>"
Then I <drafttextvisibility> 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."
And I <drafttextvisibility> see "Assign 1 marking guide Draft"
And I <readytextvisibility> see "Assign 1 marking guide Ready for use"
Examples:
| buttonname | drafttextvisibility | readytextvisibility |
| Save marking guide and make it ready | should not | should |
| Save as draft | should | should not |
@@ -0,0 +1,43 @@
@gradingform @gradingform_guide
Feature: Marking guide can handle maximum grade mismatches
In order to handle maximum grade mismatches
As a teacher
I should be able to set the maximum grade
Background:
Given the following "user" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | 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 |
Scenario Outline: Marking guide maximum grade handling
Given 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 | Assign 1 marking guide |
| Description | Marking guide description |
And I define the following marking guide:
| Criterion name | Description for students | Description for markers | Maximum score |
| Grade Criteria 1 | Grade 1 description for students | Grade 1 description for markers | <maxscore> |
| Grade Criteria 2 | Grade 2 description for students | Grade 2 description for markers | 30 |
When I press "Save marking guide and make it ready"
Then I should see "Assign 1 marking guide Ready for use"
# Please note: We need to add the no-break space unicode character to the warning message otherwise it will fail.
And I should see "WARNING: Your marking guide has a maximum grade of <totalmaxscore> points but the maximum grade set in your activity is 100 The maximum score set in your marking guide will be scaled to the maximum grade in the module."
And I should see "Intermediate scores will be converted respectively and rounded to the nearest available grade."
Examples:
# <totalmaxscore> value is derived from <maxscore> + maximum score assigned to criteria 2 (30).
# Case 1: total > max score of 100.
# Case 2: total < max score of 100.
| maxscore | totalmaxscore |
| 90 | 120 |
| 50 | 80 |
@@ -0,0 +1,102 @@
@gradereport @gradereport_grader
Feature: We don't show hidden grades for users without the 'moodle/grade:viewhidden' capability on grader report
In order to show grader report in secure way
As a teacher without the 'moodle/grade:viewhidden' capability
I should not see hidden grades in the grader report
Background:
Given the following "courses" exist:
| fullname | shortname | format |
| Course 1 | C1 | topics |
And 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 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "activities" exist:
| activity | course | section | name | intro | assignsubmission_onlinetext_enabled | submissiondrafts |
| assign | C1 | 1 | Test assignment name 1 | Submit your online text | 1 | 0 |
| assign | C1 | 1 | Test assignment name 2 | submit your online text | 1 | 0 |
| assign | C1 | 1 | Test assignment name 3 | submit your online text | 1 | 0 |
# Hidden manual grade item.
And the following "grade items" exist:
| itemname | grademin | grademax | course | hidden |
| Manual grade | 20 | 40 | C1 | 1 |
And the following "grade grades" exist:
| gradeitem | user | grade |
| Test assignment name 1 | student1 | 80 |
| Test assignment name 1 | student2 | 70 |
| Test assignment name 2 | student1 | 90 |
| Test assignment name 2 | student2 | 60 |
| Test assignment name 3 | student1 | 10 |
| Test assignment name 3 | student2 | 50 |
| Manual grade | student1 | 30 |
| Manual grade | student2 | 40 |
And I log in as "admin"
And I am on "Course 1" course homepage with editing mode on
# Hide assignment 2 activity.
And I open "Test assignment name 2" actions menu
And I choose "Availability > Hide on course page" in the open action menu
And I navigate to "View > Grader report" in the course gradebook
# Hide grade.
And I click on grade menu "Test assignment name 1" for user "student1"
And I choose "Hide" in the open action menu
# Hide assignment 3 grade item.
And I set the following settings for grade item "Test assignment name 3" of type "gradeitem" on "grader" page:
| Hidden | 1 |
@javascript
Scenario: View grader report containing hidden activities or grade items or grades
Given I log in as "teacher1"
And I am on "Course 1" course homepage
And I navigate to "View > Grader report" in the course gradebook
And the following should exist in the "user-grades" table:
| -1- | -2- | -3- | -4- | -5- | -6- | -7- |
| Student 1 | student1@example.com | 80 | 90 | 10 | 30 | 210 |
| Student 2 | student2@example.com | 70 | 60 | 50 | 40 | 220 |
And I turn editing mode on
And the field "Student 1 Test assignment name 1 grade" matches value "80"
And the field "Student 1 Test assignment name 2 grade" matches value "90"
And the field "Student 1 Test assignment name 3 grade" matches value "10"
And the field "Student 1 Manual grade" matches value "30"
And the field "Student 1 Course total grade" matches value "210"
And "Hidden" "icon" should exist in the "Student 1" "table_row"
And the field "Student 2 Test assignment name 1 grade" matches value "70"
And the field "Student 2 Test assignment name 2 grade" matches value "60"
And the field "Student 2 Test assignment name 3 grade" matches value "50"
And the field "Student 2 Manual grade" matches value "40"
And the field "Student 2 Course total grade" matches value "220"
And "Hidden" "icon" should exist in the "Student 2" "table_row"
# Remove the capability to see hidden grades.
When the following "role capability" exists:
| role | editingteacher |
| moodle/grade:viewhidden | prohibit |
And I navigate to "View > Grader report" in the course gradebook
And I turn editing mode off
And the following should exist in the "user-grades" table:
| -1- | -2- | -3- | -4- | -5- | -6- | -7- |
| Student 1 | student1@example.com | - | - | - | - | - |
| Student 2 | student2@example.com | 70 | - | - | - | 70 |
And "Hidden" "icon" should not exist in the "Student 1" "table_row"
And "Hidden" "icon" should not exist in the "Student 2" "table_row"
And I turn editing mode on
And the following should exist in the "user-grades" table:
| -1- | -2- | -3- | -4- | -5- | -6- |
| Student 1 | student1@example.com | - | - | - | - |
And the following should exist in the "user-grades" table:
| -1- | -2- | -4- | -5- | -6- |
| Student 2 | student2@example.com | - | - | - |
# Check how totals should behave!!!!!!!!!!!
And the field "Student 1 Course total grade" matches value ""
And the field "Student 2 Test assignment name 1 grade" matches value "70"
And the field "Student 2 Course total grade" matches value "70"
And "Hidden" "icon" should not exist in the "Student 1" "table_row"
Then "Hidden" "icon" should not exist in the "Student 2" "table_row"
@@ -24,6 +24,7 @@
namespace gradereport_singleview\local\screen;
use context_course;
use grade_report;
use gradereport_singleview\local\ui\range;
use gradereport_singleview\local\ui\bulk_insert;
@@ -217,8 +218,13 @@ class grade extends tablelike implements selectable_items, filterable_items {
$grade = $this->fetch_grade_or_default($this->item, $item->id);
$gradestatus = '';
// Show hidden icon if the grade is hidden and the user has permission to view hidden grades.
$showhiddenicon = $grade->is_hidden() &&
has_capability('moodle/grade:viewhidden', context_course::instance($this->courseid));
$context = [
'hidden' => $grade->is_hidden(),
'hidden' => $showhiddenicon,
'locked' => $grade->is_locked(),
];
@@ -24,6 +24,7 @@
namespace gradereport_singleview\local\screen;
use context_course;
use grade_seq;
use gradereport_singleview;
use moodle_url;
@@ -171,8 +172,12 @@ class user extends tablelike implements selectable_items {
$grade = $this->fetch_grade_or_default($item, $this->item->id);
$gradestatus = '';
// Show hidden icon if the grade is hidden and the user has permission to view hidden grades.
$showhiddenicon = ($grade->is_hidden() || $item->is_hidden()) &&
has_capability('moodle/grade:viewhidden', context_course::instance($item->courseid));
$context = [
'hidden' => $grade->is_hidden(),
'hidden' => $showhiddenicon,
'locked' => $grade->is_locked(),
];
@@ -26,6 +26,7 @@ namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
use context_course;
use grade_grade;
/**
@@ -83,6 +84,10 @@ class exclude extends grade_attribute_format implements be_checked, be_disabled,
* @return element
*/
public function determine_format(): element {
if (($this->grade->is_hidden() || $this->grade->grade_item->is_hidden()) &&
!has_capability('moodle/grade:viewhidden', context_course::instance($this->grade->grade_item->courseid))) {
return new empty_element();
}
return new checkbox_attribute(
$this->get_name(),
$this->get_label(),
@@ -24,6 +24,8 @@
namespace gradereport_singleview\local\ui;
use context_course;
defined('MOODLE_INTERNAL') || die;
/**
@@ -104,6 +106,10 @@ class feedback extends grade_attribute_format implements unique_value, be_disabl
* @return element
*/
public function determine_format(): element {
if (($this->grade->is_hidden() || $this->grade->grade_item->is_hidden()) &&
!has_capability('moodle/grade:viewhidden', context_course::instance($this->grade->grade_item->courseid))) {
return new empty_element();
}
return new text_attribute(
$this->get_name(),
$this->get_value(),
@@ -26,6 +26,7 @@ namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
use context_course;
use stdClass;
/**
* UI element representing the finalgrade column.
@@ -115,6 +116,11 @@ class finalgrade extends grade_attribute_format implements unique_value, be_disa
public function determine_format(): element {
global $CFG;
if (($this->grade->is_hidden() || $this->grade->grade_item->is_hidden()) &&
!has_capability('moodle/grade:viewhidden', context_course::instance($this->grade->grade_item->courseid))) {
return new empty_element();
}
if ($this->grade->grade_item->load_scale()) {
$scale = $this->grade->grade_item->load_scale();
@@ -24,6 +24,8 @@
namespace gradereport_singleview\local\ui;
use context_course;
defined('MOODLE_INTERNAL') || die;
/**
@@ -94,7 +96,11 @@ class override extends grade_attribute_format implements be_checked, be_disabled
* @return element
*/
public function determine_format(): element {
if (!$this->grade->grade_item->is_overridable_item()) {
// If the grade is hidden and the user does not have permission to view hidden grades,
// then we don't show the override checkbox.
$allowhiddenoverride = (!$this->grade->is_hidden() && !$this->grade->grade_item->is_hidden()) ||
has_capability('moodle/grade:viewhidden', context_course::instance($this->grade->grade_item->courseid));
if (!$allowhiddenoverride || !$this->grade->grade_item->is_overridable_item()) {
return new empty_element();
}
return new checkbox_attribute(
@@ -0,0 +1,490 @@
@gradereport @gradereport_singleview
Feature: We don't show hidden grades for users without the 'moodle/grade:viewhidden' capability on singleview report
In order to show singleview report in secure way
As a teacher without the 'moodle/grade:viewhidden' capability
I should not see hidden grades in the singleview report
Background:
Given the following "courses" exist:
| fullname | shortname | format |
| Course 1 | C1 | topics |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student1 | 1 | student1@example.com |
| student2 | Student2 | 2 | student2@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "activities" exist:
| activity | course | section | name | intro | assignsubmission_onlinetext_enabled | submissiondrafts |
| assign | C1 | 1 | Test assignment name 1 | Submit your online text | 1 | 0 |
| assign | C1 | 1 | Test assignment name 2 | submit your online text | 1 | 0 |
| assign | C1 | 1 | Test assignment name 3 | submit your online text | 1 | 0 |
# Hidden manual grade item.
And the following "grade items" exist:
| itemname | grademin | grademax | course | hidden |
| Manual grade | 20 | 40 | C1 | 1 |
And the following "grade grades" exist:
| gradeitem | user | grade |
| Test assignment name 1 | student1 | 80 |
| Test assignment name 1 | student2 | 70 |
| Test assignment name 2 | student1 | 90 |
| Test assignment name 2 | student2 | 60 |
| Test assignment name 3 | student1 | 10 |
| Test assignment name 3 | student2 | 50 |
| Manual grade | student1 | 30 |
| Manual grade | student2 | 40 |
And I log in as "admin"
And I am on "Course 1" course homepage with editing mode on
# Hide assignment 2 activity.
And I open "Test assignment name 2" actions menu
And I choose "Availability > Hide on course page" in the open action menu
And I navigate to "View > Grader report" in the course gradebook
# Hide grade.
And I click on grade menu "Test assignment name 1" for user "student1"
And I choose "Hide" in the open action menu
# Hide assignment 3 grade item.
And I set the following settings for grade item "Test assignment name 3" of type "gradeitem" on "grader" page:
| Hidden | 1 |
@javascript
Scenario: View singleview report containing hidden activities or grade items or grades with editing on and required capabilities
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
When I navigate to "View > Single view" in the course gradebook
And I click on "Users" "link" in the ".page-toggler" "css_element"
And I set the field "Search users" to "Student1"
And I click on "Student1" "list_item"
And the field "Grade for Test assignment name 1" matches value "80"
And the field "Grade for Test assignment name 2" matches value "90"
And the field "Grade for Test assignment name 3" matches value "10"
And the field "Grade for Manual grade" matches value "30"
And the field "Grade for Course total" matches value "210"
And "Hidden" "icon" should exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should exist in the "Test assignment name 2" "table_row"
And "Hidden" "icon" should exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should exist in the "Manual grade" "table_row"
And "Hidden" "icon" should not exist in the "Course total" "table_row"
And the field "Feedback for Test assignment name 1" matches value ""
And the field "Feedback for Test assignment name 2" matches value ""
And the field "Feedback for Test assignment name 3" matches value ""
And the field "Feedback for Manual grade" matches value ""
And the field "Feedback for Course total" matches value ""
And "Override for Test assignment name 1" "checkbox" should exist in the "Test assignment name 1" "table_row"
And "Override for Test assignment name 2" "checkbox" should exist in the "Test assignment name 2" "table_row"
And "Override for Test assignment name 3" "checkbox" should exist in the "Test assignment name 3" "table_row"
And "Override for Manual grade" "checkbox" should not exist in the "Manual grade" "table_row"
And "Override for Course total" "checkbox" should exist in the "Course total" "table_row"
And "Exclude for Test assignment name 1" "checkbox" should exist in the "Test assignment name 1" "table_row"
And "Exclude for Test assignment name 2" "checkbox" should exist in the "Test assignment name 2" "table_row"
And "Exclude for Test assignment name 3" "checkbox" should exist in the "Test assignment name 3" "table_row"
And "Exclude for Manual grade" "checkbox" should exist in the "Manual grade" "table_row"
And "Exclude for Course total" "checkbox" should exist in the "Course total" "table_row"
And I set the field "Search users" to "Student2"
And I click on "Student2" "list_item"
And the field "Grade for Test assignment name 1" matches value "70"
And the field "Grade for Test assignment name 2" matches value "60"
And the field "Grade for Test assignment name 3" matches value "50"
And the field "Grade for Manual grade" matches value "40"
And the field "Grade for Course total" matches value "220"
And "Hidden" "icon" should not exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should exist in the "Test assignment name 2" "table_row"
And "Hidden" "icon" should exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should exist in the "Manual grade" "table_row"
And "Hidden" "icon" should not exist in the "Course total" "table_row"
And the field "Feedback for Test assignment name 1" matches value ""
And the field "Feedback for Test assignment name 2" matches value ""
And the field "Feedback for Test assignment name 3" matches value ""
And the field "Feedback for Manual grade" matches value ""
And the field "Feedback for Course total" matches value ""
And "Override for Test assignment name 1" "checkbox" should exist in the "Test assignment name 1" "table_row"
And "Override for Test assignment name 2" "checkbox" should exist in the "Test assignment name 2" "table_row"
And "Override for Test assignment name 3" "checkbox" should exist in the "Test assignment name 3" "table_row"
And "Override for Manual grade" "checkbox" should not exist in the "Manual grade" "table_row"
And "Override for Course total" "checkbox" should exist in the "Course total" "table_row"
And "Exclude for Test assignment name 1" "checkbox" should exist in the "Test assignment name 1" "table_row"
And "Exclude for Test assignment name 2" "checkbox" should exist in the "Test assignment name 2" "table_row"
And "Exclude for Test assignment name 3" "checkbox" should exist in the "Test assignment name 3" "table_row"
And "Exclude for Manual grade" "checkbox" should exist in the "Manual grade" "table_row"
And "Exclude for Course total" "checkbox" should exist in the "Course total" "table_row"
And I click on "Grade items" "link" in the ".page-toggler" "css_element"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Test assignment name 1"
And I click on "Test assignment name 1" in the "grade" search widget
And the field "Grade for Student1 1" matches value "80"
And the field "Grade for Student2 2" matches value "70"
And "Hidden" "icon" should exist in the "Student1 1" "table_row"
And "Hidden" "icon" should not exist in the "Student2 2" "table_row"
And the field "Feedback for Student1 1" matches value ""
And the field "Feedback for Student2 2" matches value ""
And "Override for Student1 1" "checkbox" should exist in the "Student1 1" "table_row"
And "Override for Student2 2" "checkbox" should exist in the "Student2 2" "table_row"
And "Exclude for Student1 1" "checkbox" should exist in the "Student1 1" "table_row"
And "Exclude for Student2 2" "checkbox" should exist in the "Student2 2" "table_row"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Test assignment name 2"
And I click on "Test assignment name 2" in the "grade" search widget
And the field "Grade for Student1 1" matches value "90"
And the field "Grade for Student2 2" matches value "60"
And "Hidden" "icon" should exist in the "Student1 1" "table_row"
And "Hidden" "icon" should exist in the "Student2 2" "table_row"
And the field "Feedback for Student1 1" matches value ""
And the field "Feedback for Student2 2" matches value ""
And "Override for Student1 1" "checkbox" should exist in the "Student1 1" "table_row"
And "Override for Student2 2" "checkbox" should exist in the "Student2 2" "table_row"
And "Exclude for Student1 1" "checkbox" should exist in the "Student1 1" "table_row"
And "Exclude for Student2 2" "checkbox" should exist in the "Student2 2" "table_row"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Test assignment name 3"
And I click on "Test assignment name 3" in the "grade" search widget
And the field "Grade for Student1 1" matches value "10"
And the field "Grade for Student2 2" matches value "50"
And "Hidden" "icon" should exist in the "Student1 1" "table_row"
And "Hidden" "icon" should exist in the "Student2 2" "table_row"
And the field "Feedback for Student1 1" matches value ""
And the field "Feedback for Student2 2" matches value ""
And "Exclude for Student1 1" "checkbox" should exist in the "Student1 1" "table_row"
And "Exclude for Student2 2" "checkbox" should exist in the "Student2 2" "table_row"
And "Override for Student1 1" "checkbox" should exist in the "Student1 1" "table_row"
And "Override for Student2 2" "checkbox" should exist in the "Student2 2" "table_row"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Manual grade"
And I click on "Manual grade" in the "grade" search widget
And the field "Grade for Student1 1" matches value "30"
And the field "Grade for Student2 2" matches value "40"
And "Hidden" "icon" should exist in the "Student1 1" "table_row"
And "Hidden" "icon" should exist in the "Student2 2" "table_row"
And the field "Feedback for Student1 1" matches value ""
And the field "Feedback for Student2 2" matches value ""
And "Override for Student1 1" "checkbox" should not exist in the "Student1 1" "table_row"
And "Override for Student2 2" "checkbox" should not exist in the "Student2 2" "table_row"
And "Exclude for Student1 1" "checkbox" should exist in the "Student1 1" "table_row"
And "Exclude for Student2 2" "checkbox" should exist in the "Student2 2" "table_row"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Course total"
And I click on "Course total" in the "grade" search widget
And the field "Grade for Student1 1" matches value "210"
And the field "Grade for Student2 2" matches value "220"
And "Hidden" "icon" should not exist in the "Student1 1" "table_row"
And "Hidden" "icon" should not exist in the "Student2 2" "table_row"
And the field "Feedback for Student1 1" matches value ""
And the field "Feedback for Student2 2" matches value ""
And "Override for Student1 1" "checkbox" should exist in the "Student1 1" "table_row"
And "Override for Student2 2" "checkbox" should exist in the "Student2 2" "table_row"
And "Exclude for Student1 1" "checkbox" should exist in the "Student1 1" "table_row"
Then "Exclude for Student2 2" "checkbox" should exist in the "Student2 2" "table_row"
@javascript
Scenario: View singleview report containing hidden activities or grade items or grades with editing off and required capabilities
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode off
And I navigate to "View > Single view" in the course gradebook
And I click on "Grade items" "link" in the ".page-toggler" "css_element"
And I click on ".gradesearchwidget" "css_element"
And I click on "Course total" in the "grade" search widget
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Student1 1 | 210 |
| Student2 2 | 220 |
And "Hidden" "icon" should not exist in the "Student1 1" "table_row"
And "Hidden" "icon" should not exist in the "Student2 2" "table_row"
When I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Manual grade"
And I click on "Manual grade" in the "grade" search widget
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Student1 1 | 30 |
| Student2 2 | 40 |
And "Hidden" "icon" should exist in the "Student1 1" "table_row"
And "Hidden" "icon" should exist in the "Student2 2" "table_row"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Test assignment name 3"
And I click on "Test assignment name 3" in the "grade" search widget
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Student1 1 | 10 |
| Student2 2 | 50 |
And "Hidden" "icon" should exist in the "Student1 1" "table_row"
And "Hidden" "icon" should exist in the "Student2 2" "table_row"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Test assignment name 2"
And I click on "Test assignment name 2" in the "grade" search widget
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Student1 1 | 90 |
| Student2 2 | 60 |
And "Hidden" "icon" should exist in the "Student1 1" "table_row"
And "Hidden" "icon" should exist in the "Student2 2" "table_row"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Test assignment name 1"
And I click on "Test assignment name 1" in the "grade" search widget
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Student1 1 | 80 |
| Student2 2 | 70 |
And "Hidden" "icon" should exist in the "Student1 1" "table_row"
And "Hidden" "icon" should not exist in the "Student2 2" "table_row"
And I click on "Users" "link" in the ".page-toggler" "css_element"
And I set the field "Search users" to "Student1"
And I click on "Student1" "list_item"
And the following should exist in the "generaltable" table:
| Grade item | Grade |
| Test assignment name 1 | 80 |
| Test assignment name 2 | 90 |
| Test assignment name 3 | 10 |
| Manual grade | 30 |
| Course total | 210 |
And "Hidden" "icon" should exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should exist in the "Test assignment name 2" "table_row"
And "Hidden" "icon" should exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should exist in the "Manual grade" "table_row"
And "Hidden" "icon" should not exist in the "Course total" "table_row"
And I set the field "Search users" to "Student2"
And I click on "Student2" "list_item"
And the following should exist in the "generaltable" table:
| Grade item | Grade |
| Test assignment name 1 | 70 |
| Test assignment name 2 | 60 |
| Test assignment name 3 | 50 |
| Manual grade | 40 |
| Course total | 220 |
And "Hidden" "icon" should not exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should exist in the "Test assignment name 2" "table_row"
And "Hidden" "icon" should exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should exist in the "Manual grade" "table_row"
Then "Hidden" "icon" should not exist in the "Course total" "table_row"
@javascript
Scenario: View singleview report containing hidden activities or grade items or grades with editing off without required capabilities
Given I log in as "teacher1"
And the following "role capability" exists:
| role | editingteacher |
| moodle/grade:viewhidden | prohibit |
And I am on "Course 1" course homepage with editing mode off
And I navigate to "View > Single view" in the course gradebook
When I click on "Users" "link" in the ".page-toggler" "css_element"
And I set the field "Search users" to "Student2"
And I click on "Student2" "list_item"
And the following should exist in the "generaltable" table:
# Total is weird!!!!!!!!!!!!!!.
| Grade item | Grade |
| Test assignment name 1 | 70 |
| Test assignment name 2 | |
| Test assignment name 3 | |
| Manual grade | |
| Course total | 220 |
And "Hidden" "icon" should not exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 2" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should not exist in the "Manual grade" "table_row"
And "Hidden" "icon" should not exist in the "Course total" "table_row"
And I set the field "Search users" to "Student1"
And I click on "Student1" "list_item"
And the following should exist in the "generaltable" table:
| Grade item | Grade |
| Test assignment name 1 | |
| Test assignment name 2 | |
| Test assignment name 3 | |
| Manual grade | |
| Course total | 210 |
And "Hidden" "icon" should not exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 2" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should not exist in the "Manual grade" "table_row"
And "Hidden" "icon" should not exist in the "Course total" "table_row"
And I click on "Grade items" "link" in the ".page-toggler" "css_element"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Test assignment name 1"
And I click on "Test assignment name 1" in the "grade" search widget
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Student1 1 | |
| Student2 2 | 70 |
And "Hidden" "icon" should not exist in the "Student1 1" "table_row"
And "Hidden" "icon" should not exist in the "Student2 2" "table_row"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Test assignment name 2"
And I click on "Test assignment name 2" in the "grade" search widget
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Student1 1 | |
| Student2 2 | |
And "Hidden" "icon" should not exist in the "Student1 1" "table_row"
And "Hidden" "icon" should not exist in the "Student2 2" "table_row"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Test assignment name 3"
And I click on "Test assignment name 3" in the "grade" search widget
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Student1 1 | |
| Student2 2 | |
And "Hidden" "icon" should not exist in the "Student1 1" "table_row"
And "Hidden" "icon" should not exist in the "Student2 2" "table_row"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Manual grade"
And I click on "Manual grade" in the "grade" search widget
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Student1 1 | |
| Student2 2 | |
And "Hidden" "icon" should not exist in the "Student1 1" "table_row"
And "Hidden" "icon" should not exist in the "Student2 2" "table_row"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Course total"
And I click on "Course total" in the "grade" search widget
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Student1 1 | 210 |
| Student2 2 | 220 |
And "Hidden" "icon" should not exist in the "Student1 1" "table_row"
Then "Hidden" "icon" should not exist in the "Student2 2" "table_row"
@javascript
Scenario: View singleview report containing hidden activities or grade items or grades with editing on without required capabilities
Given I log in as "teacher1"
And the following "role capability" exists:
| role | editingteacher |
| moodle/grade:viewhidden | prohibit |
And I am on "Course 1" course homepage with editing mode on
And I navigate to "View > Single view" in the course gradebook
And I click on "Grade items" "link" in the ".page-toggler" "css_element"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Course total"
And I click on "Course total" in the "grade" search widget
And the field "Grade for Student1 1" matches value "210"
And the field "Grade for Student2 2" matches value "220"
And "Hidden" "icon" should not exist in the "Student1 1" "table_row"
And "Hidden" "icon" should not exist in the "Student2 2" "table_row"
And the field "Feedback for Student1 1" matches value ""
And the field "Feedback for Student2 2" matches value ""
And "Override for Student1 1" "checkbox" should exist in the "Student1 1" "table_row"
And "Override for Student2 2" "checkbox" should exist in the "Student2 2" "table_row"
And "Exclude for Student1 1" "checkbox" should exist in the "Student1 1" "table_row"
And "Exclude for Student2 2" "checkbox" should exist in the "Student2 2" "table_row"
When I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Manual grade"
And I click on "Manual grade" in the "grade" search widget
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Student1 1 | |
| Student2 2 | |
And "Hidden" "icon" should not exist in the "Student1 1" "table_row"
And "Hidden" "icon" should not exist in the "Student2 2" "table_row"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Test assignment name 3"
And I click on "Test assignment name 3" in the "grade" search widget
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Student1 1 | |
| Student2 2 | |
And "Hidden" "icon" should not exist in the "Student1 1" "table_row"
And "Hidden" "icon" should not exist in the "Student2 2" "table_row"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Test assignment name 2"
And I click on "Test assignment name 2" in the "grade" search widget
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Student1 1 | |
| Student2 2 | |
And "Hidden" "icon" should not exist in the "Student1 1" "table_row"
And "Hidden" "icon" should not exist in the "Student2 2" "table_row"
And I click on ".gradesearchwidget" "css_element"
And I set the field "Search items" to "Test assignment name 1"
And I click on "Test assignment name 1" in the "grade" search widget
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Student1 1 | |
And "Hidden" "icon" should not exist in the "Student1 1" "table_row"
And the field "Grade for Student2 2" matches value "70"
And "Hidden" "icon" should not exist in the "Student2 2" "table_row"
And the field "Feedback for Student2 2" matches value ""
And "Override for Student2 2" "checkbox" should exist in the "Student2 2" "table_row"
And "Exclude for Student2 2" "checkbox" should exist in the "Student2 2" "table_row"
And I click on "Users" "link" in the ".page-toggler" "css_element"
And I set the field "Search users" to "Student1"
And I click on "Student1" "list_item"
And the following should exist in the "generaltable" table:
| Grade item | Grade | Feedback |
| Test assignment name 1 | | |
| Test assignment name 2 | | |
| Test assignment name 3 | | |
| Manual grade | | |
And "Hidden" "icon" should not exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 2" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should not exist in the "Manual grade" "table_row"
And "Hidden" "icon" should not exist in the "Course total" "table_row"
And "Override for Test assignment name 1" "checkbox" should not exist in the "Test assignment name 1" "table_row"
And "Exclude for Test assignment name 1" "checkbox" should not exist in the "Test assignment name 1" "table_row"
And "Override for Test assignment name 2" "checkbox" should not exist in the "Test assignment name 2" "table_row"
And "Exclude for Test assignment name 2" "checkbox" should not exist in the "Test assignment name 2" "table_row"
And "Override for Test assignment name 3" "checkbox" should not exist in the "Test assignment name 3" "table_row"
And "Exclude for Test assignment name 3" "checkbox" should not exist in the "Test assignment name 3" "table_row"
And "Override for Manual grade" "checkbox" should not exist in the "Manual grade" "table_row"
And "Exclude for Manual grade" "checkbox" should not exist in the "Manual grade" "table_row"
And the field "Grade for Course total" matches value "210"
And the field "Feedback for Course total" matches value ""
And "Override for Course total" "checkbox" should exist in the "Course total" "table_row"
And "Exclude for Course total" "checkbox" should exist in the "Course total" "table_row"
And I set the field "Search users" to "Student2"
And I click on "Student2" "list_item"
And the following should exist in the "generaltable" table:
| Grade item | Grade | Feedback |
| Test assignment name 2 | | |
| Test assignment name 3 | | |
| Manual grade | | |
And "Hidden" "icon" should not exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 2" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should not exist in the "Manual grade" "table_row"
And "Hidden" "icon" should not exist in the "Course total" "table_row"
And the field "Grade for Test assignment name 1" matches value "70"
And the field "Feedback for Test assignment name 1" matches value ""
And "Override for Test assignment name 1" "checkbox" should exist in the "Test assignment name 1" "table_row"
And "Exclude for Test assignment name 1" "checkbox" should exist in the "Test assignment name 1" "table_row"
And "Override for Test assignment name 2" "checkbox" should not exist in the "Test assignment name 2" "table_row"
And "Exclude for Test assignment name 2" "checkbox" should not exist in the "Test assignment name 2" "table_row"
And "Override for Test assignment name 3" "checkbox" should not exist in the "Test assignment name 3" "table_row"
And "Exclude for Test assignment name 3" "checkbox" should not exist in the "Test assignment name 3" "table_row"
And "Override for Manual grade" "checkbox" should not exist in the "Manual grade" "table_row"
And "Exclude for Manual grade" "checkbox" should not exist in the "Manual grade" "table_row"
And the field "Grade for Course total" matches value "220"
And the field "Feedback for Course total" matches value ""
And "Override for Course total" "checkbox" should exist in the "Course total" "table_row"
Then "Exclude for Course total" "checkbox" should exist in the "Course total" "table_row"
+12 -6
View File
@@ -477,7 +477,7 @@ class user extends grade_report {
$gradegrade->load_grade_item();
// Hidden Items.
if ($gradegrade->grade_item->is_hidden()) {
if ($gradegrade->grade_item->is_hidden() && $this->canviewhidden) {
$hidden = ' dimmed_text';
}
@@ -543,7 +543,7 @@ class user extends grade_report {
$itemicon = \html_writer::div($this->gtree->get_element_icon($element), 'mr-1');
$elementtype = $this->gtree->get_element_type_string($element);
$itemtype = \html_writer::span($elementtype, 'd-block text-uppercase small dimmed_text',
$itemtype = \html_writer::span($elementtype, 'd-block text-uppercase small ' . $hidden,
['title' => $elementtype]);
if ($type == 'categoryitem' || $type == 'courseitem') {
@@ -647,10 +647,11 @@ class user extends grade_report {
);
$gradeitemdata['gradehiddenbydate'] = true;
} else if ($gradegrade->is_hidden()) {
$data['grade']['class'] = $class.' dimmed_text';
$data['grade']['class'] = $class;
$data['grade']['content'] = '-';
if ($this->canviewhidden) {
$data['grade']['class'] .= ' dimmed_text';
$gradeitemdata['graderaw'] = $gradeval;
$data['grade']['content'] = grade_format_gradevalue($gradeval,
$gradegrade->grade_item,
@@ -708,9 +709,10 @@ class user extends grade_report {
$data['percentage']['class'] = $class.' gradingerror';
$data['percentage']['content'] = get_string('error');
} else if ($gradegrade->is_hidden()) {
$data['percentage']['class'] = $class.' dimmed_text';
$data['percentage']['class'] = $class;
$data['percentage']['content'] = '-';
if ($this->canviewhidden) {
$data['percentage']['class'] .= ' dimmed_text';
$data['percentage']['content'] = grade_format_gradevalue(
$gradeval,
$gradegrade->grade_item,
@@ -737,8 +739,9 @@ class user extends grade_report {
$data['lettergrade']['class'] = $class.' gradingerror';
$data['lettergrade']['content'] = get_string('error');
} else if ($gradegrade->is_hidden()) {
$data['lettergrade']['class'] = $class.' dimmed_text';
$data['lettergrade']['class'] = $class;
if (!$this->canviewhidden) {
$data['lettergrade']['class'] .= ' dimmed_text';
$data['lettergrade']['content'] = '-';
} else {
$data['lettergrade']['content'] = grade_format_gradevalue(
@@ -768,8 +771,11 @@ class user extends grade_report {
$data['rank']['class'] = $class.' gradingerror';
$data['rank']['content'] = get_string('error');
} else if ($gradegrade->is_hidden()) {
$data['rank']['class'] = $class.' dimmed_text';
$data['rank']['class'] = $class;
$data['rank']['content'] = '-';
if ($this->canviewhidden) {
$data['rank']['class'] .= ' dimmed_text';
}
} else if (is_null($gradeval)) {
// No grade, o rank.
$data['rank']['class'] = $class;
@@ -0,0 +1,209 @@
@gradereport @gradereport_user
Feature: We don't show hidden grades for users without the 'moodle/grade:viewhidden' capability on user report
In order to show user report in secure way
As a teacher without the 'moodle/grade:viewhidden' capability
I should not see hidden grades in the user report
Background:
Given the following "courses" exist:
| fullname | shortname | format |
| Course 1 | C1 | topics |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student1 | 1 | student1@example.com |
| student2 | Student2 | 2 | student2@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "activities" exist:
| activity | course | section | name | intro | assignsubmission_onlinetext_enabled | submissiondrafts |
| assign | C1 | 1 | Test assignment name 1 | Submit your online text | 1 | 0 |
| assign | C1 | 1 | Test assignment name 2 | submit your online text | 1 | 0 |
| assign | C1 | 1 | Test assignment name 3 | submit your online text | 1 | 0 |
# Hidden manual grade item.
And the following "grade items" exist:
| itemname | grademin | grademax | course | hidden |
| Manual grade | 20 | 40 | C1 | 1 |
And the following "grade grades" exist:
| gradeitem | user | grade |
| Test assignment name 1 | student1 | 80 |
| Test assignment name 1 | student2 | 70 |
| Test assignment name 2 | student1 | 90 |
| Test assignment name 2 | student2 | 60 |
| Test assignment name 3 | student1 | 10 |
| Test assignment name 3 | student2 | 50 |
| Manual grade | student1 | 30 |
| Manual grade | student2 | 40 |
And I log in as "admin"
# Need to show hidden items in order to check hidden grades. Also lets show totals if they contain hidden items.
And the following config values are set as admin:
| grade_report_user_showhiddenitems | 2 |
| grade_report_user_showtotalsifcontainhidden | 2 |
And I am on "Course 1" course homepage with editing mode on
# Hide assignment 2 activity.
And I open "Test assignment name 2" actions menu
And I choose "Availability > Hide on course page" in the open action menu
And I navigate to "View > Grader report" in the course gradebook
# Hide grade.
And I click on grade menu "Test assignment name 1" for user "student1"
And I choose "Hide" in the open action menu
# Hide assignment 3 grade item.
And I set the following settings for grade item "Test assignment name 3" of type "gradeitem" on "grader" page:
| Hidden | 1 |
@javascript
Scenario: View user report containing hidden activities or grade items or grades with capabilities with student view
Given I am on the "Course 1" "grades > User report > View" page logged in as "teacher1"
When I set the field "Search users" to "Student1"
And I click on "Student1 1" "list_item"
And I wait until the page is ready
# Student View.
And the following should exist in the "user-grade" table:
| Grade item | Grade |
| Test assignment name 1 | - |
| Test assignment name 3 | - |
| Manual grade | - |
| Course total | 210 |
And the following should not exist in the "user-grade" table:
| Grade item |
| Test assignment name 2 |
And "Hidden" "icon" should not exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should not exist in the "Manual grade" "table_row"
And "Hidden" "icon" should not exist in the "Course total" "table_row"
And I set the field "Search users" to "Student2"
And I click on "Student2 2" "list_item"
And I wait until the page is ready
And the following should exist in the "user-grade" table:
| Grade item | Grade |
| Test assignment name 1 | 70 |
| Test assignment name 3 | - |
| Manual grade | - |
| Course total | 220 |
And the following should not exist in the "user-grade" table:
| Grade item |
| Test assignment name 2 |
And "Hidden" "icon" should not exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should not exist in the "Manual grade" "table_row"
Then "Hidden" "icon" should not exist in the "Course total" "table_row"
@javascript
Scenario: View user report containing hidden activities or grade items or grades with capabilities with teacher view
Given I am on the "Course 1" "grades > User report > View" page logged in as "teacher1"
When I set the field "Search users" to "Student2"
And I click on "Student2 2" "list_item"
And I wait until the page is ready
And I set the field "View report as" to "Myself"
And the following should exist in the "user-grade" table:
| Grade item | Grade |
| Test assignment name 1 | 70 |
| Test assignment name 2 | 60 |
| Test assignment name 3 | 50 |
| Manual grade | 40 |
| Course total | 220 |
And "Hidden" "icon" should not exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should exist in the "Test assignment name 2" "table_row"
And "Hidden" "icon" should exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should exist in the "Manual grade" "table_row"
And "Hidden" "icon" should not exist in the "Course total" "table_row"
And I set the field "Search users" to "Student1"
And I click on "Student1 1" "list_item"
And I wait until the page is ready
And the following should exist in the "user-grade" table:
| Grade item | Grade |
| Test assignment name 1 | 80 |
| Test assignment name 2 | 90 |
| Test assignment name 3 | 10 |
| Manual grade | 30 |
| Course total | 210 |
And "Hidden" "icon" should exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should exist in the "Test assignment name 2" "table_row"
And "Hidden" "icon" should exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should exist in the "Manual grade" "table_row"
Then "Hidden" "icon" should not exist in the "Course total" "table_row"
@javascript
Scenario: View user report containing hidden activities or grade items or grades without capabilities with teacher view
Given I am on the "Course 1" "grades > User report > View" page logged in as "teacher1"
And the following "role capability" exists:
| role | editingteacher |
| moodle/grade:viewhidden | prohibit |
When I set the field "Search users" to "Student1"
And I click on "Student1 1" "list_item"
And I wait until the page is ready
And I set the field "View report as" to "Myself"
And the following should exist in the "user-grade" table:
| Grade item | Grade |
| Test assignment name 1 | - |
| Test assignment name 2 | - |
| Test assignment name 3 | - |
| Manual grade | - |
| Course total | 210 |
And "Hidden" "icon" should not exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 2" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should not exist in the "Manual grade" "table_row"
And "Hidden" "icon" should not exist in the "Course total" "table_row"
And I set the field "Search users" to "Student2"
And I click on "Student2 2" "list_item"
And I wait until the page is ready
And the following should exist in the "user-grade" table:
| Grade item | Grade |
| Test assignment name 1 | 70 |
| Test assignment name 2 | - |
| Test assignment name 3 | - |
| Manual grade | - |
| Course total | 220 |
And "Hidden" "icon" should not exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 2" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should not exist in the "Manual grade" "table_row"
Then "Hidden" "icon" should not exist in the "Course total" "table_row"
@javascript
Scenario: View user report containing hidden activities or grade items or grades without capabilities with student view
Given I am on the "Course 1" "grades > User report > View" page logged in as "teacher1"
And the following "role capability" exists:
| role | editingteacher |
| moodle/grade:viewhidden | prohibit |
When I set the field "Search users" to "Student2"
And I click on "Student2 2" "list_item"
And I wait until the page is ready
And the following should exist in the "user-grade" table:
| Grade item | Grade |
| Test assignment name 1 | 70 |
| Test assignment name 3 | - |
| Manual grade | - |
| Course total | 220 |
And the following should not exist in the "user-grade" table:
| Grade item |
| Test assignment name 2 |
And "Hidden" "icon" should not exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should not exist in the "Manual grade" "table_row"
And "Hidden" "icon" should not exist in the "Course total" "table_row"
And I set the field "Search users" to "Student1"
And I click on "Student1 1" "list_item"
And I wait until the page is ready
And the following should exist in the "user-grade" table:
| Grade item | Grade |
| Test assignment name 1 | - |
| Test assignment name 3 | - |
| Manual grade | - |
| Course total | 210 |
And the following should not exist in the "user-grade" table:
| Grade item |
| Test assignment name 2 |
And "Hidden" "icon" should not exist in the "Test assignment name 1" "table_row"
And "Hidden" "icon" should not exist in the "Test assignment name 3" "table_row"
And "Hidden" "icon" should not exist in the "Manual grade" "table_row"
Then "Hidden" "icon" should not exist in the "Course total" "table_row"
+41
View File
@@ -145,6 +145,24 @@ class behat_grades extends behat_base {
}
}
/**
* Gets the grade id from its grade item name and userid.
*
* @param int $itemid Item ID
* @param int $userid User ID
* @return int
* @throws Exception
*/
protected function get_grade_id(int $itemid, int $userid): int {
global $DB;
if ($id = $DB->get_field('grade_grades', 'id', ['itemid' => $itemid, 'userid' => $userid])) {
return $id;
}
throw new Exception('The specified grade with id "' . $itemid . ' and userid' . $userid . '" does not exist');
}
/**
* Gets the grade item id from its name.
*
@@ -269,4 +287,27 @@ class behat_grades extends behat_base {
$this->execute_js_on_node($node, '{{ELEMENT}}.scrollIntoView({ block: "center", inline: "center" })');
$this->execute("behat_general::i_click_on", [$this->escape($xpath), "xpath_element"]);
}
/**
* Clicks on given grade menu.
*
* @Given /^I click on grade menu "([^"]*)" for user "([^"]*)"$/
* @param string $itemname Item name
* @param string $username User name
* @throws Exception
*/
public function i_click_on_grade_menu(string $itemname, string $username) {
$this->execute("behat_navigation::i_close_block_drawer_if_open");
$userid = $this->get_user_id_by_identifier($username);
$itemid = $this->get_grade_item_id($itemname);
$gradeid = $this->get_grade_id($itemid, $userid);
$xpath = "//table[@id='user-grades']";
$xpath .= "//*[@data-type='grade'][@data-id='" . $gradeid . "']";
$node = $this->get_selected_node("xpath_element", $this->escape($xpath));
$this->execute_js_on_node($node, '{{ELEMENT}}.scrollIntoView({ block: "center", inline: "center" })');
$this->execute("behat_general::i_click_on", [$this->escape($xpath), "xpath_element"]);
}
}
+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';
+14 -18
View File
@@ -62,36 +62,32 @@ $string['memorylimithelp'] = '<p>現在、サーバのPHPメモリ制限が {$a}
<p>しかし、この設定が<b>すべての</b>PHPページの動作を妨げる場合もあります。ページ閲覧中にエラーが表示される場合、.htaccessファイルを削除してください。</p></li>
</ol>';
$string['paths'] = 'パス';
$string['pathserrcreatedataroot'] = 'データディレクトリ ({$a->dataroot}) インストーラで作成できません。';
$string['pathserrcreatedataroot'] = 'データディレクトリ ({$a->dataroot}) インストーラで作成できません。';
$string['pathshead'] = 'パスを確認する';
$string['pathsrodataroot'] = 'datarootディレクトリに書き込み権がありません。';
$string['pathsroparentdataroot'] = '親ディレクトリ ({$a->parent}) に書き込み権がありません。データディレクトリ ({$a->dataroot}) インストーラで作成できません。';
$string['pathssubadmindir'] = 'まれに、コントロールパネルまたはその他の管理ツールにアクセスするためのURLとして/adminディレクトリを使用しているウェブホストがあります。残念ですが、これはMoodle管理ページの標準的なロケーションと衝突します。あなたはインストール時にadminディレクトリをリネームすることができます。ここに新しいディレクトリ名を入力してください。例: <br /> <br /><b>moodleadmin</b><br /> <br />
これでMoodleでのadminへのリンクを変更します。';
$string['pathssubdataroot'] = '<p>ユーザによってアップロードされたファイルコンテンツすべてをMoodleが保存するディレクトリです。</p>
$string['pathsroparentdataroot'] = '親ディレクトリ ({$a->parent}) に書き込み権がありません。データディレクトリ ({$a->dataroot}) インストーラで作成できません。';
$string['pathssubadmindir'] = 'ごく少数のウェブホストですが、コントロールパネルまたはその他の管理ツールにアクセスするためのURLとして/adminディレクトリを使用しています。残念ですが、これはMoodle管理ページの標準的なロケーションと衝突します。あなたはインストール時にadminディレクトリをリネームできます。ここに新しいディレクトリ名を入力してください。例: <b>moodleadmin</b> これでMoodleでのadminへのリンクを変更します。';
$string['pathssubdataroot'] = '<p>ユーザがアップロードしたファイルコンテンツすべてをMoodleが保存するディレクトリです。</p>
<p>このディレクトリはウェブサーバユーザ (通常「nobody」または「apache」) から読み込みおよび書き込みできる必要があります。</p>
<p>ウェブからは直接アクセスできないようにしてください。</p>
<p>現在ディレクトリが存在しない場合、インストレーションプロセスは作成を試みます。</p';
$string['pathssubdirroot'] = '<p>Moodleコードを含むディレクトリに関するフルパスです。</p>';
$string['pathssubwwwroot'] = '<p>Moodleにアクセスできるフルウェブアドレスです。例えばユーザがブラウザのアドレスバーに入力してMoodleにアクセスするためのアドレスです。</p>
$string['pathssubdirroot'] = '<p>Moodleコードを含むディレクトリへのフルパスです。</p>';
$string['pathssubwwwroot'] = '<p>Moodleにアクセスするための完全なウェブアドレスです。例えばユーザがブラウザのアドレスバーに入力してMoodleにアクセスするためのアドレスです。</p>
<p>複数アドレスを使用したMoodleへのアクセスはできません。あなたのサイトに複数アドレスからアクセスできる場合、最も簡単なアドレスを選択して、すべてのアドレスにパーマネントリダイレクトを設定してください。</p>
<p>あなたのサイトにインターネットおよび内部ネットワーク (イントラネットと呼ばれます) からアクセスできる場合、ここではパブリックアドレスを使用してください。</p>
<p>現在のアドレスが正しくない場合、あなたのブラウザのURLを変更して、異なる値でインストレーションを再開してください。</p>';
$string['pathsunsecuredataroot'] = 'dataroot ロケーションが安全ではありません。';
$string['pathswrongadmindir'] = 'adminディレクトリがありません。';
<p>あなたのサイトにインターネットおよび内部ネットワーク (イントラネットと呼ばれることもあります) からアクセスできる場合、ここではパブリックアドレスを使用してください。</p>
<p>現在のアドレスが正しくない場合、あなたのブラウザのアドレスバーでURLを変更して、インストールをやり直してください。</p>';
$string['pathsunsecuredataroot'] = 'datarootの場所が安全ではありません。';
$string['pathswrongadmindir'] = 'adminディレクトリが存在しません。';
$string['phpextension'] = '{$a} PHP拡張モジュール';
$string['phpversion'] = 'PHPバージョン';
$string['phpversionhelp'] = '<p>Moodleには少なくとも5.6.5または7.1のPHPバージョンが必要です (7.0.x にはエンジンの制限があります)。</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';
+5 -4
View File
@@ -80,10 +80,11 @@ $string['phpversionhelp'] = '<p>Moodle wymaga wersji PHP co najmniej 5.6.5 lub 7
<p>Aktualnie używasz wersji {$a}.</p>
<p>Musisz zaktualizować PHP lub przenieść na host z nowszą wersją PHP.</p>';
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
$string['welcomep20'] = 'Widzisz tę stronę, ponieważ z powodzeniem zainstalowałeś i uruchomiłeś <strong>{$a->packname} {$a->packversion}</strong> na swoim komputerze.';
$string['welcomep30'] = 'Ten instalator <strong>{$a->installername}</strong> zawiera aplikacje, by stworz środowisko, w którym będzie działać <strong>Moodle</strong>, czyli';
$string['welcomep20'] = 'Widzisz tę stronę, ponieważ pomyślnie zainstalowałeś i uruchomiłeś pakiet <strong>{$a->packname} {$a->packversion</strong> na swoim komputerze. Gratulacje!';
$string['welcomep30'] = 'Ta wersja <strong>{$a->installername}</strong> zawiera aplikacje służące do tworzenia środowiska, w którym będzie działać <strong>Moodle</strong>, a mianowicie:';
$string['welcomep40'] = 'Pakiet zawiera <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
$string['welcomep50'] = 'Wszystkie aplikacje w tym pakiecie maja własne, oddzielne licencje. Kompletny pakiet <strong>{$a->installername}</strong> jest <a href="http://www.opensource.org/docs/definition_plain.html">open source</a> i jest dystrybuowany na <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a> licencji.';
$string['welcomep60'] = 'Nastepujące strony przeprowadzą cię przez instalację <strong>Moodle</strong> na twoim komputerze. Możesz zaakceptować ustawienia domyślne, lub opcjonalnie, dostosować je do swoich potrzeb.';
$string['welcomep50'] = 'Użycie wszystkich aplikacji w tym pakiecie podlega ich odpowiednim licencjom. Kompletny pakiet <strong>{$a->installername}</strong> jest <a href="https://www.opensource.org/docs/definition_plain.html">open source</a> i jest dystrybuowany na licencji <a href="https://www.gnu.org/copyleft/gpl.html">GPL</a>.';
$string['welcomep60'] = 'Na poniższych stronach znajdziesz kilka łatwych do wykonania kroków, aby skonfigurować i skonfigurować <strong>Moodle</strong> na swoim komputerze. Możesz zaakceptować domyślne
ustawienia lub, opcjonalnie, zmienić je, aby odpowiadały Twoim potrzebom.';
$string['welcomep70'] = 'Kliknij przycisk "Dalej" żeby kontynuować instalację <strong>Moodle</strong>.';
$string['wwwroot'] = 'Adres w sieci';
+1 -1
View File
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
$string['clianswerno'] = 'n';
$string['cliansweryes'] = 'y';
$string['cliincorrectvalueerror'] = '错误,将{$a->option}的值设为{$a->value}是不正确的';
$string['cliincorrectvalueerror'] = '错误,将"{$a->option}"的值设为"{$a->value}"是不正确的';
$string['cliincorrectvalueretry'] = '不正确的值,请重试';
$string['clitypevalue'] = '输入值';
$string['clitypevaluedefault'] = '输入值,按回车使用缺省值({$a})';
+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';
@@ -148,6 +148,23 @@ class behat_context_helper {
return $overrideclassname;
}
try {
$themeconfig = theme_config::load($suitename);
} catch (Exception $e) {
// This theme has no theme config.
return null;
}
// The theme will use all core contexts, except the one overridden by theme or its parent.
if (isset($themeconfig->parents)) {
foreach ($themeconfig->parents as $parent) {
$overrideclassname = "behat_theme_{$parent}_{$classname}";
if (self::$environment->hasContextClass($overrideclassname)) {
return $overrideclassname;
}
}
}
if (self::$environment->hasContextClass($classname)) {
return $classname;
}
+15
View File
@@ -1676,4 +1676,19 @@ EOF;
return $result ?: null;
}
/**
* Prepare an xpath for insertion into Selenium JavaScript.
*
* @param string $xpath
* @return string
*/
protected function prepare_xpath_for_javascript(string $xpath): string {
$newlines = [
"\r\n",
"\r",
"\n",
];
return str_replace($newlines, ' ', $xpath);
}
}
@@ -88,6 +88,7 @@ class WebDriver extends UpstreamDriver {
} catch (DriverException $e) {
error_log($e->getMessage());
$rcp = new \ReflectionProperty(parent::class, 'webDriver');
$rcp->setAccessible(true);
$rcp->setValue($this, null);
}
}
+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',
+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);
}
}
+1 -1
View File
@@ -6,6 +6,6 @@ define("core_form/collapsesections",["exports","jquery","core/pending"],(functio
* @copyright 2021 Bas Brands
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 4.0
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_pending=_interopRequireDefault(_pending);const SELECTORS_FORM=".mform",SELECTORS_FORMHEADER=".fheader",SELECTORS_FORMCONTAINER="fieldset > .fcontainer",CLASSES_SHOW="show",CLASSES_COLLAPSED="collapsed",CLASSES_HIDDEN="d-none";_exports.init=collapsesections=>{const pendingPromise=new _pending.default("core_form/collapsesections"),collapsemenu=document.querySelector(collapsesections),formParent=collapsemenu.closest(SELECTORS_FORM),formContainers=formParent.querySelectorAll(SELECTORS_FORMCONTAINER);collapsemenu.addEventListener("keydown",(e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),collapsemenu.click())}));let formcontainercount=0,expandedcount=0;formContainers.forEach((container=>{container.parentElement.classList.contains(CLASSES_HIDDEN)||formcontainercount++,container.classList.contains(CLASSES_SHOW)&&expandedcount++})),formcontainercount===expandedcount&&(collapsemenu.classList.remove(CLASSES_COLLAPSED),collapsemenu.setAttribute("aria-expanded",!0)),collapsemenu.addEventListener("click",(()=>{let action="hide";collapsemenu.classList.contains(CLASSES_COLLAPSED)&&(action="show"),formContainers.forEach((container=>(0,_jquery.default)(container).collapse(action)))}));const collapseElementIds=[...formParent.querySelectorAll(SELECTORS_FORMHEADER)].map(((element,index)=>(element.id=element.id||"collapseElement-".concat(index),element.id)));collapsemenu.setAttribute("aria-controls",collapseElementIds.join(" ")),(0,_jquery.default)(SELECTORS_FORMCONTAINER).on("hidden.bs.collapse",(()=>{[...formContainers].every((container=>!container.classList.contains(CLASSES_SHOW)))&&(collapsemenu.classList.add(CLASSES_COLLAPSED),collapsemenu.setAttribute("aria-expanded",!1))})),(0,_jquery.default)(SELECTORS_FORMCONTAINER).on("shown.bs.collapse",(()=>{[...formContainers].every((container=>container.classList.contains(CLASSES_SHOW)))&&(collapsemenu.classList.remove(CLASSES_COLLAPSED),collapsemenu.setAttribute("aria-expanded",!0))})),pendingPromise.resolve()}}));
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_pending=_interopRequireDefault(_pending);const SELECTORS_FORM=".mform",SELECTORS_FORMHEADER=".fheader",SELECTORS_FORMCONTAINER="fieldset > .fcontainer",CLASSES_SHOW="show",CLASSES_COLLAPSED="collapsed",CLASSES_HIDDEN="d-none";_exports.init=collapsesections=>{const pendingPromise=new _pending.default("core_form/collapsesections"),collapsemenu=document.querySelector(collapsesections),formParent=collapsemenu.closest(SELECTORS_FORM),formContainers=formParent.querySelectorAll(SELECTORS_FORMCONTAINER);collapsemenu.addEventListener("keydown",(e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),collapsemenu.click())}));let formcontainercount=0,expandedcount=0;formContainers.forEach((container=>{container.parentElement.classList.contains(CLASSES_HIDDEN)||formcontainercount++,container.classList.contains(CLASSES_SHOW)&&expandedcount++})),formcontainercount===expandedcount&&(collapsemenu.classList.remove(CLASSES_COLLAPSED),collapsemenu.setAttribute("aria-expanded",!0)),collapsemenu.addEventListener("click",(()=>{let action="hide";if(collapsemenu.classList.contains(CLASSES_COLLAPSED)&&(action="show"),formContainers.length){const pendingPromiseToggle=new _pending.default("core_form/collapsesections:toggle-"+action);formContainers.forEach(((container,index,array)=>{(0,_jquery.default)(container).collapse(action),index===array.length-1&&pendingPromiseToggle.resolve()}))}}));const collapseElementIds=[...formParent.querySelectorAll(SELECTORS_FORMHEADER)].map(((element,index)=>(element.id=element.id||"collapseElement-".concat(index),element.id)));collapsemenu.setAttribute("aria-controls",collapseElementIds.join(" ")),(0,_jquery.default)(SELECTORS_FORMCONTAINER).on("hidden.bs.collapse",(()=>{[...formContainers].every((container=>!container.classList.contains(CLASSES_SHOW)))&&(collapsemenu.classList.add(CLASSES_COLLAPSED),collapsemenu.setAttribute("aria-expanded",!1))})),(0,_jquery.default)(SELECTORS_FORMCONTAINER).on("shown.bs.collapse",(()=>{[...formContainers].every((container=>container.classList.contains(CLASSES_SHOW)))&&(collapsemenu.classList.remove(CLASSES_COLLAPSED),collapsemenu.setAttribute("aria-expanded",!0))})),pendingPromise.resolve()}}));
//# sourceMappingURL=collapsesections.min.js.map
File diff suppressed because one or more lines are too long
+9 -1
View File
@@ -82,7 +82,15 @@ export const init = collapsesections => {
action = 'show';
}
formContainers.forEach(container => $(container).collapse(action));
if (formContainers.length) {
const pendingPromiseToggle = new Pending('core_form/collapsesections:toggle-' + action);
formContainers.forEach((container, index, array) => {
$(container).collapse(action);
if (index === array.length - 1) {
pendingPromiseToggle.resolve();
}
});
}
});
// Ensure collapse menu button adds aria-controls attribute referring to each collapsible element.
+3 -3
View File
@@ -1090,10 +1090,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 = '';
}
+71 -16
View File
@@ -14,16 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Steps definitions to open and close action menus.
*
* @package core
* @category test
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use Behat\Mink\Exception\{DriverException, ExpectationException};
use Behat\Mink\Exception\DriverException;
use Behat\Mink\Exception\ExpectationException;
use Behat\Mink\Element\NodeElement;
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
@@ -38,7 +31,6 @@ require_once(__DIR__ . '/../../behat/behat_base.php');
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_accessibility extends behat_base {
/**
* Run the axe-core accessibility tests.
*
@@ -62,6 +54,37 @@ class behat_accessibility extends behat_base {
);
}
/**
* Run the axe-core accessibility tests for a page region.
*
* There are standard tags to ensure WCAG 2.1 A, WCAG 2.1 AA, and Section 508 compliance.
* It is also possible to specify any desired optional tags.
*
* See {@link https://github.com/dequelabs/axe-core/blob/v4.10.0/doc/rule-descriptions.md} for the list of available tags
*
* @Then the :element :selector should meet accessibility standards
* @Then the :element :selector should meet accessibility standards with :extratags extra tests
* @Then the :element :selector should meet :standardtags accessibility standards
* @param string $element The element to run the tests on
* @param string $selector The selector to use to find the element
* @param string $standardtags Comma-separated list of standard tags to run
* @param string $extratags Comma-separated list of tags to run in addition to the standard tags
*/
public function run_axe_validation_for_tags_within_element(
string $element,
string $selector,
string $standardtags = '',
string $extratags = '',
): void {
$node = $this->get_selected_node($selector, $element);
$this->run_axe_for_tags(
// Turn the comma-separated string into an array of trimmed values, filtering out empty values.
array_filter(array_map('trim', explode(',', $standardtags))),
array_filter(array_map('trim', explode(',', $extratags))),
$node,
);
}
/**
* Run the Axe tests.
*
@@ -70,8 +93,13 @@ class behat_accessibility extends behat_base {
*
* @param array $standardtags The list of standard tags to run
* @param array $extratags The list of tags, in addition to the standard tags, to run
* @param null|NodeElement $containerelement The element to run the tests on
*/
protected function run_axe_for_tags(array $standardtags = [], array $extratags = []): void {
protected function run_axe_for_tags(
array $standardtags = [],
array $extratags = [],
?NodeElement $containerelement = null,
): void {
if (!behat_config_manager::get_behat_run_config_value('axe')) {
return;
}
@@ -86,13 +114,33 @@ class behat_accessibility extends behat_base {
$axeurl = (new \moodle_url('/lib/behat/axe/axe.min.js'))->out(false);
$axeconfig = $this->get_axe_config_for_tags($standardtags, $extratags);
$xpath = '';
if ($containerelement) {
$xpath = $this->prepare_xpath_for_javascript($containerelement->getXpath());
}
$runaxe = <<<EOF
(axeurl => {
const runTests = () => {
const axeTag = document.querySelector('script[data-purpose="axe"]');
axeTag.dataset.results = null;
axe.run({$axeconfig})
const getRun = () => {
const xpath = "{$xpath}";
if (xpath.length) {
const targetElements = [];
const results = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);
let targetElement = results.iterateNext();
while (targetElement) {
targetElements.push(targetElement);
targetElement = results.iterateNext();
}
return axe.run(targetElements, {$axeconfig});
}
return axe.run({$axeconfig});
};
getRun()
.then(results => {
axeTag.dataset.results = JSON.stringify({
violations: results.violations,
@@ -167,12 +215,15 @@ EOF;
}
$violationdata .= sprintf(
" %.03d violations of '%s' (severity: %s)\n%s\n",
" %.03d violations of rule '%s' found (severity: %s)\n",
count($violation->nodes),
$violation->description,
$violation->id,
$violation->impact,
$nodedata
);
$violationdata .= " {$violation->help}\n";
$violationdata .= " {$violation->description}\n";
$violationdata .= " {$violation->helpUrl}\n";
$violationdata .= $nodedata;
}
throw new ExpectationException($violationdata, $this->getSession());
@@ -191,9 +242,13 @@ EOF;
if (empty($standardtags)) {
$standardtags = [
// Meet WCAG 2.2 Level A success criteria.
'wcag2a',
'wcag21a',
'wcag22a',
// Meet WCAG 2.2 Level AA success criteria.
'wcag2aa',
'wcag21aa',
'wcag22aa',
// Meet Section 508 requirements.
+16 -15
View File
@@ -167,7 +167,7 @@ class behat_forms extends behat_base {
// Different try & catch as we can have expanded fieldsets with advanced fields on them.
try {
$this->wait_for_pending_js();
// Expand all fields xpath.
$showmorexpath = "//a[normalize-space(.)='" . get_string('showmore', 'form') . "']" .
"[contains(concat(' ', normalize-space(@class), ' '), ' moreless-toggler')]";
@@ -177,22 +177,23 @@ class behat_forms extends behat_base {
return;
}
if ($this->getSession()->getDriver() instanceof \DMore\ChromeDriver\ChromeDriver) {
// Chrome Driver produces unique xpaths for each element.
foreach ($showmores as $showmore) {
$showmore->click();
$js = <<<EOF
require(['core/pending'], function(Pending) {
const query = document.evaluate("{$showmorexpath}", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
if (query.snapshotLength > 0) {
const pendingPromise = new Pending('showmore:expand');
for (let i = 0, length = query.snapshotLength; i < length; ++i) {
query.snapshotItem(i).click();
if (i === length - 1) {
pendingPromise.resolve();
}
}
}
} else {
// Funny thing about this, with findAll() we specify a pattern and each element matching the pattern
// is added to the array with of xpaths with a [0], [1]... sufix, but when we click on an element it
// does not matches the specified xpath anymore (now is a "Show less..." link) so [1] becomes [0],
// that's why we always click on the first XPath match, will be always the next one.
$iterations = count($showmores);
for ($i = 0; $i < $iterations; $i++) {
$showmores[0]->click();
}
}
});
EOF;
$this->execute_script($js);
$this->wait_for_pending_js();
} catch (ElementNotFoundException $e) {
// We continue with the test.
}
+6 -7
View File
@@ -977,13 +977,12 @@ class behat_general extends behat_base {
list($preselector, $prelocator) = $this->transform_selector($preselectortype, $preelement);
list($postselector, $postlocator) = $this->transform_selector($postselectortype, $postelement);
$newlines = [
"\r\n",
"\r",
"\n",
];
$prexpath = str_replace($newlines, ' ', $this->find($preselector, $prelocator, false, $containernode)->getXpath());
$postxpath = str_replace($newlines, ' ', $this->find($postselector, $postlocator, false, $containernode)->getXpath());
$prexpath = $this->prepare_xpath_for_javascript(
$this->find($preselector, $prelocator, false, $containernode)->getXpath()
);
$postxpath = $this->prepare_xpath_for_javascript(
$this->find($postselector, $postlocator, false, $containernode)->getXpath()
);
if ($this->running_javascript()) {
// The xpath to do this was running really slowly on certain Chrome versions so we are using
+13
View File
@@ -336,6 +336,19 @@ final class moodle_url_test extends \advanced_testcase {
$this->assertMatchesRegularExpression($expected, $url->out(true));
}
/**
* Test the get_slashargument method.
*/
public function test_get_slashargument(): void {
$this->resetAfterTest();
$url = new \moodle_url('/pluginfile.php/14/user/private/capybara.png');
$this->assertEquals('/14/user/private/capybara.png', $url->get_slashargument());
$url = new \moodle_url('/image/capybara.png');
$this->assertEmpty($url->get_slashargument());
}
/**
* Data provider for make_pluginfile_url tests.
*
+5
View File
@@ -1,6 +1,11 @@
This files describes API changes in core libraries and APIs,
information provided here is intended especially for developers.
=== 4.3.12 ===
* 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
* The public method `get_slashargument` has been added to the `moodle_url` class.
=== 4.3.8 ===
* The `navigation_cache` class now uses the Moodle Universal Cache (MUC) to store the navigation cache data instead of storing
+11
View File
@@ -1024,6 +1024,17 @@ class moodle_url {
public function get_port() {
return $this->port;
}
/**
* Returns the 'slashargument' portion of a URL. For example, if the URL is
* http://www.example.org.com/pluginfile.php/1/core_admin/logocompact/ then this will
* return '1/core_admin/logocompact/'.
*
* @return string Slash argument as string.
*/
public function get_slashargument(): string {
return $this->slashargument;
}
}
/**
+11 -4
View File
@@ -308,10 +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() || core_useragent::is_chrome())) {
// Chrome has stopped supporting OGV in the latest version. Refer: https://caniuse.com/ogv.
// We need to enable Ogv.JS Tech plugin for Safari, iOS and Chrome.
/* 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
@@ -3395,7 +3395,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)) {
@@ -3415,9 +3415,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']
);
@@ -39,3 +39,35 @@ Feature: To be able to see and save user message preferences as admin
Then "[data-processor-name='popup']" "css_element" should exist
And "[data-processor-name='email']" "css_element" should not exist
And "[data-processor-name='airnotifier']" "css_element" should exist
@javascript
Scenario: An admin can set the default notification preferences
Given I log in as "admin"
And I navigate to "Messaging > Notification settings" in site administration
And I set the field "email" to "1"
And I press "Save changes"
And I set the following fields to these values:
| mod_assign_assign_notification_enabled[popup] | 1 |
| mod_assign_assign_notification_enabled[email] | 0 |
| mod_feedback_submission_enabled[popup] | 0 |
| mod_feedback_submission_locked[popup] | 1 |
| mod_feedback_submission_enabled[email] | 1 |
| mod_feedback_submission_locked[email] | 1 |
| mod_feedback_message_disable | 0 |
And I press "Save changes"
And I log in as "student1"
And I follow "Preferences" in the user menu
When I click on "Notification preferences" "link" in the "#page-content" "css_element"
# Assignment web notification is enabled, email is disabled.
Then the field "message_provider_mod_assign_assign_notification_popup" matches value "1"
And the field "message_provider_mod_assign_assign_notification_email" matches value "0"
# Corresponding checkboxes can be updated.
And I click on "message_provider_mod_assign_assign_notification_popup" "checkbox"
And I click on "message_provider_mod_assign_assign_notification_email" "checkbox"
And the field "message_provider_mod_assign_assign_notification_popup" matches value "0"
And the field "message_provider_mod_assign_assign_notification_email" matches value "1"
# Feedback submission notifications are locked off for web and locked on for email.
And "Feedback notifications" row "Web" column of "preference-table" table should contain "Locked off"
And "Feedback notifications" row "Requires configuration Email" column of "preference-table" table should contain "Locked on"
# Feedback reminder is disabled.
And I should not see "Feedback reminder"
-2
View File
@@ -799,7 +799,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))
]);
@@ -812,7 +811,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
@@ -407,7 +407,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)' => [
@@ -723,7 +723,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');
}
@@ -324,8 +326,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);
}
@@ -373,7 +377,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
@@ -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;
@@ -5,13 +5,13 @@ Feature: See the competencies for an activity
I need to see the competencies linked to one activity in the breakdown report.
Background:
Given the following lp "frameworks" exist:
| shortname | idnumber |
| Test-Framework | ID-FW1 |
And the following lp "competencies" exist:
| shortname | framework |
| Test-Comp1 | ID-FW1 |
| Test-Comp2 | ID-FW1 |
Given the following "core_competency > frameworks" exist:
| shortname | idnumber |
| Test-Framework | ID-FW1 |
And the following "core_competency > competencies" exist:
| shortname | competencyframework |
| Test-Comp1 | ID-FW1 |
| Test-Comp2 | ID-FW1 |
Given the following "courses" exist:
| shortname | fullname |
| C1 | Course 1 |
+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
@@ -0,0 +1,24 @@
@theme_boost
Feature: Course sections selector is not available on boost theme
In order to view course sections selector
As admin
I need to use the classic theme
# Please note: This is a sister test to "Use the course section selector in classic theme" to confirm
# that the "Jump to" menu does not exist on the section page.
Scenario: Course sections selector is not available on boost theme regardless of the course format
Given the following "courses" exist:
| fullname | shortname | format | coursedisplay |
| Course 1 | C1 | topics | 1 |
# Add activities in different sections to test that only the selected section's activity is visible.
And the following "activities" exist:
| activity | course | name | section |
| forum | C1 | Forum 1 | 1 |
| assign | C1 | Assign 1 | 2 |
And I am on the "Course 1" course page logged in as admin
When I click on "Topic 1" "link" in the "#section-1" "css_element"
# Confirm that only the selected section's activity is visible.
Then I should see "Forum 1"
And I should not see "Assign 1"
# Confirm that the "Jump to" menu does not exist on the view section page.
And "jump" "select" should not exist
@@ -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 = 2023100910.00; // 20231009 = branching date YYYYMMDD - do not modify!
$version = 2023100912.00; // 20231009 = branching date YYYYMMDD - do not modify!
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
$release = '4.3.10 (Build: 20250210)'; // Human-friendly version name
$release = '4.3.12 (Build: 20250414)'; // Human-friendly version name
$branch = '403'; // This version's branch.
$maturity = MATURITY_STABLE; // This version's maturity level.