Compare commits
95 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 73fc31dabf | |||
| 75b685892e | |||
| e1a465e308 | |||
| fef55ab67f | |||
| e34a6de8fc | |||
| 8b0c0899a2 | |||
| 53070e58e6 | |||
| 5f399c1fd6 | |||
| 3cebc3ae93 | |||
| f985532070 | |||
| 07ec036d94 | |||
| 4992ac10d4 | |||
| 699c8b2762 | |||
| 6e5f3f1bcb | |||
| bf37322446 | |||
| ede62f83d3 | |||
| 29a405d768 | |||
| 0556eee665 | |||
| fa44a1de53 | |||
| 25c2e8079d | |||
| ef8cf9fdce | |||
| 8733010253 | |||
| 1e86bbfdd6 | |||
| 15e392cec5 | |||
| 062d760482 | |||
| 835e2b76c3 | |||
| aa7b5a9632 | |||
| 0606dcfbce | |||
| 0f20b11843 | |||
| fe1caf61ba | |||
| d6eaf85e38 | |||
| a2e327094e | |||
| ae9228b834 | |||
| a2f1368e38 | |||
| 307ad2348f | |||
| 8e9bd4c55c | |||
| 95fea46186 | |||
| b666531258 | |||
| 3f20152cac | |||
| b883a7a21f | |||
| 75d8e9fb77 | |||
| 162627b088 | |||
| 3b374cdc8a | |||
| a85c9490b0 | |||
| 49bf20af0b | |||
| 03242ec12c | |||
| 48a3c783dd | |||
| acdcb722b0 | |||
| e692607aaa | |||
| 7f7f13bba0 | |||
| 920a8be0e6 | |||
| c1e2713764 | |||
| d3a8d50292 | |||
| d0137c54a2 | |||
| beb2fcb077 | |||
| 58ba3cf834 | |||
| f1c8f2281a | |||
| 03e8bddd1e | |||
| 2531818e3d | |||
| e2a32ef88f | |||
| e4a37ce710 | |||
| a94925736c | |||
| b10657129e | |||
| 2cee750fdf | |||
| 78a04159cd | |||
| eb87add2b2 | |||
| a952340811 | |||
| 21fe8a97e8 | |||
| f2093b46e8 | |||
| abea467757 | |||
| 2c4e86245b | |||
| ea80a7f049 | |||
| 5e7238e637 | |||
| 49e490d209 | |||
| 6009960edb | |||
| cdf2b01228 | |||
| c52babea18 | |||
| 9a528fbbb4 | |||
| beb5ba99e7 | |||
| a689db2063 | |||
| 151542757f | |||
| 87b30d4ac6 | |||
| 14772da46c | |||
| 58b9066111 | |||
| eac184c754 | |||
| dca015783d | |||
| b086bc5387 | |||
| b82a60b3cf | |||
| 5e7cde3baf | |||
| cbb94d0f87 | |||
| 5f3057cb7c | |||
| 71e551b03f | |||
| 69f3c42284 | |||
| 735f25dceb | |||
| 4725303f8c |
@@ -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 |
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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')) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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 (\) versions of backslashed items to deny list.
|
||||
$encodedslashdenylist = array_map(function($value) {
|
||||
$encoded = str_replace('\\', '\', $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"
|
||||
@@ -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"
|
||||
@@ -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"]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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'] = 'ウェブアドレス';
|
||||
|
||||
@@ -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';
|
||||
@@ -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 stworzyć ś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';
|
||||
|
||||
@@ -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})';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 = '';
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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',
|
||||
]);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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 |
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user