Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0a21811ed0 | |||
| 5102a3a04f | |||
| ae77cfd5af | |||
| 6da13bc87a | |||
| a8f4ba9af1 | |||
| b9ab9d82e9 | |||
| dc19dd6134 | |||
| 986a6f6f40 | |||
| cbf9dfbd8d | |||
| 6527cbc492 | |||
| 5068469f6f | |||
| 3f9cc1fc76 | |||
| 3dd5ef4cc0 | |||
| 2b7a9da30f | |||
| 9f1f8e584f | |||
| 68edc3c42a | |||
| 3d4b232a4a | |||
| 63df6e1ee2 | |||
| 30adbbf796 | |||
| 995ceb32d0 | |||
| 2c69199ca1 | |||
| 0ca014907b | |||
| 2afafa0431 | |||
| 9fb9779780 | |||
| e08da48fbd | |||
| d6bd634cc5 | |||
| 3fb79f7a6a | |||
| 94bdaa6b4e | |||
| aa1f55f1bc | |||
| f2a52e06e5 | |||
| 2bafc91952 | |||
| aa3a2a43ea | |||
| a7c9576c20 | |||
| 321c443838 | |||
| c34bd1aa46 | |||
| 2fe23b9295 | |||
| c9a309ed6a | |||
| a247fd8d93 | |||
| 5774e9e081 | |||
| fdaeb5c7c3 | |||
| ff2de0df79 | |||
| 2cde9d6aff | |||
| 15d50814c6 | |||
| aa59c122df | |||
| 0443be1bca | |||
| de589ca25d | |||
| c2dd676a4e | |||
| beb13397f8 | |||
| 13a902faea | |||
| 7487cbd043 | |||
| f884dee7e8 | |||
| f008960321 | |||
| e6a4dde54b | |||
| 2a2fd7ad38 | |||
| bb4bcb120d | |||
| 1e03249349 | |||
| d2cd91224f | |||
| 4abe3027d8 |
@@ -48,7 +48,7 @@ Feature: Verify that all form fields values can be get and set
|
||||
And the "Unenrol users" select box should not contain "Baker"
|
||||
And the "Unenrol users" select box should not contain "President, Baker"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I follow "Test this one"
|
||||
And I am on the "Test this one" "wiki activity" page
|
||||
And I press "Create page"
|
||||
# Text (textarea & editor) & Select (multi-select) - Checking "I set the following fields to these values".
|
||||
When I set the following fields to these values:
|
||||
@@ -61,8 +61,7 @@ Feature: Verify that all form fields values can be get and set
|
||||
# Checkbox - Checking "I set the field" and "The field matches value" ticked.
|
||||
And I set the field "Force format" to "1"
|
||||
And I press "Save and return to course"
|
||||
And I should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I am on the "Test this one" "wiki activity" page
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
And the field "Force format" matches value "1"
|
||||
@@ -70,8 +69,7 @@ Feature: Verify that all form fields values can be get and set
|
||||
# Checkbox - Checking "I set the field" and "The field matches value" unticked.
|
||||
And I set the field "Force format" to ""
|
||||
And I press "Save and return to course"
|
||||
And I should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I am on the "Test this one" "wiki activity" page
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
And the field "Force format" matches value ""
|
||||
@@ -80,8 +78,7 @@ Feature: Verify that all form fields values can be get and set
|
||||
And I set the following fields to these values:
|
||||
| Force format | 1 |
|
||||
And I press "Save and return to course"
|
||||
And I should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I am on the "Test this one" "wiki activity" page
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
And the following fields match these values:
|
||||
@@ -92,8 +89,7 @@ Feature: Verify that all form fields values can be get and set
|
||||
And I set the following fields to these values:
|
||||
| Force format | |
|
||||
And I press "Save and return to course"
|
||||
And I should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I am on the "Test this one" "wiki activity" page
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
And the following fields match these values:
|
||||
@@ -126,14 +122,13 @@ Feature: Verify that all form fields values can be get and set
|
||||
| section | 1 |
|
||||
| allowupdate | 1 |
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test choice name"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test choice name" "choice activity editing" page
|
||||
And I set the field "Option 1" to "one"
|
||||
And I set the field "Option 2" to "two"
|
||||
And I set the field "Option 3" to "three"
|
||||
And I press "Save and return to course"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test choice name"
|
||||
And I am on the "Test choice name" "choice activity" page
|
||||
And I set the field "one" to "1"
|
||||
And I press "Save my choice"
|
||||
And the field "one" matches value "1"
|
||||
@@ -183,31 +178,23 @@ Feature: Verify that all form fields values can be get and set
|
||||
And I press "Cancel"
|
||||
# Advanced checkbox requires real browser to allow uncheck to work. MDL-58681. MDL-55386.
|
||||
# Advanced checkbox - Checking "I set the field" and "The field matches value" ticked.
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test this one"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test choice name" "choice activity editing" page
|
||||
And I set the field "Display description on course page" to "1"
|
||||
And I press "Save and return to course"
|
||||
And I should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test choice name" "choice activity editing" page
|
||||
And the field "Display description on course page" matches value "1"
|
||||
And the field "Display description on course page" does not match value ""
|
||||
# Advanced checkbox - Checking "I set the field" and "The field matches value" unticked.
|
||||
And I set the field "Display description on course page" to ""
|
||||
And I press "Save and return to course"
|
||||
And I should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test choice name" "choice activity editing" page
|
||||
And the field "Display description on course page" matches value ""
|
||||
And the field "Display description on course page" does not match value "1"
|
||||
# Advanced checkbox - Checking "I set the following fields to these values:" and "The following fields match these values" ticked.
|
||||
And I set the following fields to these values:
|
||||
| Display description on course page | 1 |
|
||||
And I press "Save and return to course"
|
||||
And I should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test choice name" "choice activity editing" page
|
||||
And the following fields match these values:
|
||||
| Display description on course page | 1 |
|
||||
And the following fields do not match these values:
|
||||
@@ -216,9 +203,7 @@ Feature: Verify that all form fields values can be get and set
|
||||
And I set the following fields to these values:
|
||||
| Display description on course page | |
|
||||
And I press "Save and return to course"
|
||||
And I should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test choice name" "choice activity editing" page
|
||||
And the following fields match these values:
|
||||
| Display description on course page | |
|
||||
And the following fields do not match these values:
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
@tool @tool_behat
|
||||
Feature: Use core page resolvers for the I am on the page steps
|
||||
In order to write tests correctly
|
||||
As a developer
|
||||
I need to have steps which take me straight to a page
|
||||
|
||||
Scenario Outline: When I am on an instance
|
||||
Given the following "categories" exist:
|
||||
| name | category | idnumber |
|
||||
| Cat 1 | 0 | CAT1 |
|
||||
And the following "course" exists:
|
||||
| fullname | Economics 101 |
|
||||
| shortname | ECON101 |
|
||||
| idnumber | 2021-econ101 |
|
||||
And the following "activity" exists:
|
||||
| course | ECON101 |
|
||||
| activity | forum |
|
||||
| name | Fundamentals of Economics |
|
||||
| idnumber | fundamentalsofeconomics |
|
||||
And I log in as "admin"
|
||||
When I am on the <identifier> <type> page
|
||||
Then I should see "<shouldsee>"
|
||||
|
||||
Examples:
|
||||
| description | identifier | type | shouldsee |
|
||||
| Course Category idnumber | CAT1 | category | Add a new course |
|
||||
| Course Category name | "Cat 1" | Category | Add a new course |
|
||||
| Course Full name | "Economics 101" | course | Fundamentals of Economics |
|
||||
| Course Short name | ECON101 | COURSE | Fundamentals of Economics |
|
||||
| Course idnumber | "2021-econ101" | Course | Fundamentals of Economics |
|
||||
| Forum idnumber | fundamentalsofeconomics | Activity | Add a new discussion |
|
||||
| Generic activity editing | fundamentalsofeconomics | "Activity editing" | Updating: Forum |
|
||||
| Forum name | "Fundamentals of Economics" | "Forum activity" | Add a new discussion |
|
||||
| Forum name editing | "Fundamentals of Economics" | "Forum activity editing" | Updating: Forum |
|
||||
| Forum name permissions | "Fundamentals of Economics" | "Forum activity permissions" | Permissions in Forum: Fun |
|
||||
| Forum name roles | "Fundamentals of Economics" | "Forum activity roles" | Assign roles in Forum: Fun |
|
||||
|
||||
Scenario Outline: When I am on an instance logged in as
|
||||
Given the following "categories" exist:
|
||||
| name | category | idnumber |
|
||||
| Cat 1 | 0 | CAT1 |
|
||||
And the following "course" exists:
|
||||
| fullname | Economics 101 |
|
||||
| shortname | ECON101 |
|
||||
| idnumber | 2021-econ101 |
|
||||
And the following "activity" exists:
|
||||
| course | ECON101 |
|
||||
| activity | forum |
|
||||
| name | Fundamentals of Economics |
|
||||
| idnumber | fundamentalsofeconomics |
|
||||
When I am on the <identifier> <type> page logged in as admin
|
||||
Then I should see "<shouldsee>"
|
||||
|
||||
Examples:
|
||||
| description | identifier | type | shouldsee |
|
||||
| Course Category idnumber | CAT1 | category | Add a new course |
|
||||
| Course Category name | "Cat 1" | Category | Add a new course |
|
||||
| Course Full name | "Economics 101" | course | Fundamentals of Economics |
|
||||
| Course Short name | ECON101 | COURSE | Fundamentals of Economics |
|
||||
| Course idnumber | "2021-econ101" | Course | Fundamentals of Economics |
|
||||
| Forum idnumber | fundamentalsofeconomics | Activity | Add a new discussion |
|
||||
| Generic activity editing | fundamentalsofeconomics | "Activity editing" | Updating: Forum |
|
||||
| Forum name | "Fundamentals of Economics" | "Forum activity" | Add a new discussion |
|
||||
| Forum name editing | "Fundamentals of Economics" | "Forum activity editing" | Updating: Forum |
|
||||
| Forum name permissions | "Fundamentals of Economics" | "Forum activity permissions" | Permissions in Forum: Fun |
|
||||
| Forum name roles | "Fundamentals of Economics" | "Forum activity roles" | Assign roles in Forum: Fun |
|
||||
|
||||
Scenario Outline: When I am on a named page
|
||||
Given I log in as "admin"
|
||||
When I am on the <identifier> page
|
||||
Then I should see "<shouldsee>"
|
||||
|
||||
Examples:
|
||||
| description | identifier | shouldsee |
|
||||
| Admin page | "Admin notifications" | Check for available updates |
|
||||
| Home page | Homepage | Recently accessed courses |
|
||||
|
||||
Scenario Outline: When I am on a named page logged in as
|
||||
When I am on the <identifier> page logged in as admin
|
||||
Then I should see "<shouldsee>"
|
||||
|
||||
Examples:
|
||||
| description | identifier | shouldsee |
|
||||
| Admin page | "Admin notifications" | Check for available updates |
|
||||
| Home page | Homepage | Recently accessed courses |
|
||||
@@ -24,9 +24,7 @@ Feature: See the competencies for an activity on the course competencies page.
|
||||
| activity | name | intro | course | idnumber | completion | completionview |
|
||||
| page | PageName1 | PageDesc1 | C1 | PAGE1 | 1 | 1 |
|
||||
| page | PageName2 | PageDesc2 | C1 | PAGE2 | 1 | 1 |
|
||||
And I log in as "admin"
|
||||
And I am on site homepage
|
||||
And I follow "Course 1"
|
||||
And I am on the "Course 1" course page logged in as admin
|
||||
And I follow "Competencies"
|
||||
And I press "Add competencies to course"
|
||||
And "Competency picker" "dialogue" should be visible
|
||||
@@ -36,9 +34,7 @@ Feature: See the competencies for an activity on the course competencies page.
|
||||
And "Competency picker" "dialogue" should be visible
|
||||
And I select "Test-Comp2" of the competency tree
|
||||
And I click on "Add" "button" in the "Competency picker" "dialogue"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName1 "page activity editing" page
|
||||
And I follow "Expand all"
|
||||
And I set the field "Course competencies" to "Test-Comp1"
|
||||
And I press "Save and return to course"
|
||||
@@ -46,10 +42,8 @@ Feature: See the competencies for an activity on the course competencies page.
|
||||
|
||||
@javascript
|
||||
Scenario: Go to the competency course competencies page.
|
||||
When I log in as "student1"
|
||||
And I am on site homepage
|
||||
And I follow "Course 1"
|
||||
And I follow "Competencies"
|
||||
Given I am on the "Course 1" course page logged in as student1
|
||||
When I follow "Competencies"
|
||||
Then I should see "Test-Comp1"
|
||||
And I should see "Test-Comp2"
|
||||
And I set the field "Filter competencies by resource or activity" to "PageName1"
|
||||
@@ -64,12 +58,5 @@ Feature: See the competencies for an activity on the course competencies page.
|
||||
|
||||
@javascript
|
||||
Scenario: None course competencies page.
|
||||
When I log in as "student1"
|
||||
And I am on site homepage
|
||||
And I follow "Course 1"
|
||||
And I follow "PageName1"
|
||||
Then I should see "Test page content"
|
||||
And I am on site homepage
|
||||
And I follow "Course 1"
|
||||
And I follow "PageName1"
|
||||
When I am on the PageName1 "page activity" page logged in as student1
|
||||
Then I should see "Test page content"
|
||||
|
||||
@@ -42,9 +42,7 @@ Feature: Backup user data
|
||||
| Feedback for the response 'True'. | So you think it is true |
|
||||
| Feedback for the response 'False'. | So you think it is false |
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Quiz 1"
|
||||
When I am on the "Quiz 1" "quiz activity" page logged in as student1
|
||||
And I press "Attempt quiz now"
|
||||
And I click on "True" "radio" in the "First question" "question"
|
||||
And I click on "False" "radio" in the "Second question" "question"
|
||||
|
||||
@@ -48,10 +48,10 @@ Feature: Confirm that conditions on completion no longer cause a bug
|
||||
And I click on "Activity completion" "button" in the "Add restriction..." "dialogue"
|
||||
And I set the field with xpath "//div[contains(concat(' ', normalize-space(@class), ' '), ' availability-item ')][preceding-sibling::div]//select[@name='cm']" to "Page2"
|
||||
And I press "Save and return to course"
|
||||
And I should see "Not available unless:" in the ".activity.glossary" "css_element"
|
||||
Then I should see "Not available unless:" in the ".activity.glossary" "css_element"
|
||||
And I should see "The activity Page1 is marked complete" in the ".activity.glossary" "css_element"
|
||||
And I should see "The activity Page2 is marked complete" in the ".activity.glossary" "css_element"
|
||||
And I follow "TestGlossary"
|
||||
|
||||
# Behat will automatically check there is no error on this page.
|
||||
Then I should see "TestGlossary"
|
||||
And I am on the TestGlossary "glossary activity" page
|
||||
And I should see "TestGlossary"
|
||||
|
||||
@@ -86,13 +86,12 @@ Feature: availability_grade
|
||||
And I set the field "Minimum grade percentage (inclusive)" to "10"
|
||||
And I press "Save and return to course"
|
||||
|
||||
And I log out
|
||||
|
||||
# Log in as student without a grade yet.
|
||||
When I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
When I am on the "A1" "assign activity" page logged in as student1
|
||||
|
||||
# Do the assignment.
|
||||
And I follow "A1"
|
||||
And I click on "Add submission" "button"
|
||||
And I set the field "Online text" to "Q"
|
||||
And I click on "Save changes" "button"
|
||||
@@ -103,25 +102,22 @@ Feature: availability_grade
|
||||
And I should not see "P3" in the "region-main" "region"
|
||||
And I should not see "P4" in the "region-main" "region"
|
||||
And I should see "A1" in the "region-main" "region"
|
||||
And I log out
|
||||
|
||||
# Log back in as teacher.
|
||||
When I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
When I am on the "A1" "assign activity" page logged in as teacher1
|
||||
|
||||
# Give the assignment 40%.
|
||||
And I follow "A1"
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
# Pick the grade link in the row that has s@example.com in it.
|
||||
And I click on "Grade" "link" in the "s@example.com" "table_row"
|
||||
And I set the field "Grade out of 100" to "40"
|
||||
And I click on "Save changes" "button"
|
||||
And I click on "Edit settings" "link"
|
||||
And I log out
|
||||
|
||||
# Log back in as student.
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I am on the "Course 1" course page logged in as student1
|
||||
|
||||
# Check pages are visible.
|
||||
Then I should see "P2" in the "region-main" "region"
|
||||
|
||||
@@ -96,8 +96,7 @@ Feature: availability_profile
|
||||
And I click on "Save and return to course" "button"
|
||||
|
||||
# Edit it again and check the setting still works.
|
||||
When I follow "P1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
When I am on the P1 "page activity editing" page
|
||||
And I expand all fieldsets
|
||||
Then the field "User profile field" matches value "Super field"
|
||||
And the field "Value to compare against" matches value "Bananaman"
|
||||
|
||||
@@ -51,19 +51,19 @@ Feature: Award badges based on activity completion
|
||||
And I log out
|
||||
|
||||
Scenario: Student earns a badge using activity completion, but does not get passing grade
|
||||
When I log in as "student1"
|
||||
Given I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And the "Receive a grade" completion condition of "Test quiz name" is displayed as "failed"
|
||||
And the "Receive a pass grade or complete all available attempts" completion condition of "Test quiz name" is displayed as "todo"
|
||||
And I follow "Test quiz name"
|
||||
When I am on the "Test quiz name" "quiz activity" page
|
||||
And I press "Re-attempt quiz"
|
||||
And I set the field "False" to "1"
|
||||
And I press "Finish attempt ..."
|
||||
And I press "Submit all and finish"
|
||||
And I click on "Submit all and finish" "button" in the "Confirmation" "dialogue"
|
||||
And I log out
|
||||
Then I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
And I navigate to "Badges > Manage badges" in current page administration
|
||||
And I follow "Course Badge"
|
||||
And I should see "Recipients (1)"
|
||||
Then I should see "Recipients (1)"
|
||||
|
||||
@@ -27,9 +27,7 @@ Feature: Course overview block show users their progress on courses
|
||||
Then I should not see "0%" in the "Course overview" "block"
|
||||
|
||||
Scenario: User complete activity and verify his progress
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I follow "Test choice 1"
|
||||
Given I am on the "Test choice 1" "choice activity" page logged in as teacher1
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I set the following fields to these values:
|
||||
| Completion tracking | Show activity as complete when conditions are met |
|
||||
@@ -40,8 +38,8 @@ Feature: Course overview block show users their progress on courses
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
Then I should see "Course 1" in the "Course overview" "block"
|
||||
And I should see "0%" in the "Course overview" "block"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test choice 1"
|
||||
|
||||
And I am on the "Test choice 1" "choice activity" page
|
||||
And I follow "Dashboard" in the user menu
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
And I should see "100%" in the "Course overview" "block"
|
||||
|
||||
@@ -19,7 +19,7 @@ Feature: The private files block allows users to store files privately in moodle
|
||||
| page | C1 | page1 | Test page name | Test page description |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I follow "Test page name"
|
||||
And I am on the "Test page name" "page activity" page
|
||||
And I add the "Private files" block
|
||||
And I should see "No files available" in the "Private files" "block"
|
||||
When I follow "Manage private files..."
|
||||
|
||||
+8
-12
@@ -16,24 +16,20 @@ Feature: The recently accessed items block allows users to easily access their m
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| student1 | C2 | student |
|
||||
And I log in as "admin"
|
||||
And I am on "Course 1" course homepage
|
||||
And I turn editing mode on
|
||||
And I add a "Forum" to section "1" and I fill the form with:
|
||||
| Forum name | Test forum name |
|
||||
| Forum type | Standard forum for general use |
|
||||
| Description | Test forum description |
|
||||
And I log out
|
||||
And the following "activity" exists:
|
||||
| course | C1 |
|
||||
| activity | forum |
|
||||
| idnumber | Test forum name |
|
||||
| name | Test forum name |
|
||||
And I log in as "student1"
|
||||
When I press "Customise this page"
|
||||
And I press "Customise this page"
|
||||
And I add the "Recently accessed items" block
|
||||
|
||||
Scenario: User has not accessed any item
|
||||
Then I should see "No recent items" in the "Recently accessed items" "block"
|
||||
|
||||
Scenario: User has accessed some items
|
||||
Given I am on "Course 1" course homepage
|
||||
When I follow "Test forum name"
|
||||
Given I change window size to "large"
|
||||
When I am on the "Test forum name" "forum activity" page
|
||||
And I follow "Dashboard" in the user menu
|
||||
And I change window size to "large"
|
||||
Then I should see "Test forum name" in the "Recently accessed items" "block"
|
||||
|
||||
@@ -36,12 +36,10 @@ Feature: Restrict activity availability through date conditions
|
||||
| x[year] | 2037 |
|
||||
And I press "Save and return to course"
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
When I am on the "Course 1" course page logged in as student1
|
||||
Then I should see "Available from 31 December 2037"
|
||||
And "Test assignment 1" "link" should not exist in the "page" "region"
|
||||
And "Test assignment 1" activity should be dimmed
|
||||
And "Test assignment 1" "link" should not exist
|
||||
And I log out
|
||||
|
||||
@javascript
|
||||
Scenario: Show activity hidden to students when available until date is in past
|
||||
@@ -60,6 +58,5 @@ Feature: Restrict activity availability through date conditions
|
||||
And I click on ".availability-item .availability-eye img" "css_element"
|
||||
And I press "Save and return to course"
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
Then I should not see "Test assignment 2"
|
||||
When I am on the "Course 1" course page logged in as student1
|
||||
Then I should not see "Test assignment 2" in the "page" "region"
|
||||
|
||||
@@ -17,13 +17,16 @@ Feature: Restrict activity availability through grade conditions
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And I log in as "teacher1"
|
||||
And the following "activity" exists:
|
||||
| course | C1 |
|
||||
| activity | assign |
|
||||
| idnumber | Grade assignment |
|
||||
| name | Grade assignment |
|
||||
| assignsubmission_onlinetext_enabled | 1 |
|
||||
| assignsubmission_file_enabled | 0 |
|
||||
| submissiondrafts | 0 |
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Assignment" to section "1" and I fill the form with:
|
||||
| Assignment name | Grade assignment |
|
||||
| Description | Grade this assignment to revoke restriction on restricted assignment |
|
||||
| assignsubmission_onlinetext_enabled | 1 |
|
||||
| assignsubmission_file_enabled | 0 |
|
||||
# Adding the page like this because id_availableform_enabled needs to be clicked to trigger the action.
|
||||
And I add a "Page" to section "2"
|
||||
And I expand all fieldsets
|
||||
@@ -31,15 +34,15 @@ Feature: Restrict activity availability through grade conditions
|
||||
And I click on "Grade" "button" in the "Add restriction..." "dialogue"
|
||||
And I click on "min" "checkbox"
|
||||
And I set the following fields to these values:
|
||||
| Name | Test page name |
|
||||
| Description | Restricted page, till grades in Grade assignment is at least 20% |
|
||||
| Page content | Test page contents |
|
||||
| id | Grade assignment |
|
||||
| minval | 20 |
|
||||
| Name | Test page name |
|
||||
| Description | Restricted page, till grades in Grade assignment is at least 20% |
|
||||
| Page content | Test page contents |
|
||||
| id | Grade assignment |
|
||||
| minval | 20 |
|
||||
And I press "Save and return to course"
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
|
||||
When I am on the "Course 1" course page logged in as student1
|
||||
Then I should see "Not available unless: You achieve a required score in Grade assignment"
|
||||
And "Test page name" activity should be dimmed
|
||||
And "Test page name" "link" should not exist
|
||||
@@ -50,9 +53,8 @@ Feature: Restrict activity availability through grade conditions
|
||||
And I press "Save changes"
|
||||
And I should see "Submitted for grading"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Grade assignment"
|
||||
|
||||
And I am on the "Grade assignment" "assign activity" page logged in as teacher1
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
And I click on "Grade" "link" in the "Student First" "table_row"
|
||||
And I set the following fields to these values:
|
||||
@@ -60,7 +62,7 @@ Feature: Restrict activity availability through grade conditions
|
||||
And I press "Save changes"
|
||||
And I follow "Edit settings"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
|
||||
And I am on the "Course 1" course page logged in as student1
|
||||
And "Test page name" activity should be visible
|
||||
And I should not see "Not available unless: You achieve a required score in Grade assignment"
|
||||
|
||||
@@ -76,16 +76,14 @@ Feature: Restrict sections availability through completion or grade conditions
|
||||
And I am on "Course 1" course homepage
|
||||
Then I should see "Not available unless: You achieve a required score in Grade assignment"
|
||||
And "Test page name" activity should be hidden
|
||||
And I follow "Grade assignment"
|
||||
And I am on the "Grade assignment" "assign activity" page
|
||||
And I press "Add submission"
|
||||
And I set the following fields to these values:
|
||||
| Online text | I'm the student submission |
|
||||
And I press "Save changes"
|
||||
And I should see "Submitted for grading"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Grade assignment"
|
||||
And I am on the "Grade assignment" "assign activity" page logged in as teacher1
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
And I click on "Grade" "link" in the "Student First" "table_row"
|
||||
And I set the following fields to these values:
|
||||
@@ -93,7 +91,6 @@ Feature: Restrict sections availability through completion or grade conditions
|
||||
And I press "Save changes"
|
||||
And I follow "Edit settings"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I am on the "Course 1" Course page logged in as student1
|
||||
And "Test page name" activity should be visible
|
||||
And I should not see "Not available unless: You achieve a required score in Grade assignment"
|
||||
|
||||
@@ -11,25 +11,23 @@ Feature: Edit completion settings of an activity
|
||||
And the following "activities" exist:
|
||||
| activity | course | idnumber | intro | name | completion | completionview |
|
||||
| page | C1 | p1 | x | TestPage | 2 | 1 |
|
||||
And I log in as "admin"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
|
||||
Scenario: Completion is not locked when the activity has not yet been viewed
|
||||
Given I click on "Edit settings" "link" in the "TestPage" activity
|
||||
Given I am on the TestPage "Page Activity editing" page logged in as admin
|
||||
When I expand all fieldsets
|
||||
Then I should see "Completion tracking"
|
||||
And I should not see "Completion options locked"
|
||||
|
||||
Scenario: Completion is locked after the activity has been viewed
|
||||
Given I follow "TestPage"
|
||||
When I follow "Edit settings"
|
||||
Given I am on the TestPage "Page Activity" page logged in as admin
|
||||
When I am on the TestPage "Page Activity editing" page
|
||||
And I expand all fieldsets
|
||||
Then I should see "Completion options locked"
|
||||
|
||||
@javascript
|
||||
Scenario: Pressing the unlock button allows the user to edit completion settings
|
||||
Given I follow "TestPage"
|
||||
When I navigate to "Edit settings" in current page administration
|
||||
Given I am on the TestPage "Page Activity" page logged in as admin
|
||||
When I am on the TestPage "Page Activity editing" page
|
||||
And I expand all fieldsets
|
||||
And I press "Unlock completion options"
|
||||
Then I should see "Completion options unlocked"
|
||||
@@ -41,8 +39,8 @@ Feature: Edit completion settings of an activity
|
||||
|
||||
@javascript
|
||||
Scenario: Even when completion is locked, the user can still set the date
|
||||
Given I follow "TestPage"
|
||||
When I navigate to "Edit settings" in current page administration
|
||||
Given I am on the TestPage "Page Activity" page logged in as admin
|
||||
And I am on the TestPage "Page Activity editing" page
|
||||
And I expand all fieldsets
|
||||
When I click on "id_completionexpected_enabled" "checkbox"
|
||||
And I set the field "id_completionexpected_year" to "2013"
|
||||
|
||||
@@ -45,9 +45,7 @@ Feature: Activity navigation involving activities with access restrictions
|
||||
|
||||
@javascript
|
||||
Scenario: Activity navigation involving activities with access restrictions
|
||||
Given I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
When I follow "Page 1"
|
||||
Given I am on the "Page 1" "page activity" page logged in as student1
|
||||
Then I should see "Page 2" in the "#next-activity-link" "css_element"
|
||||
# Activity that has access restriction should not show up in the dropdown.
|
||||
And the "Jump to..." select box should not contain "Page 3"
|
||||
@@ -56,7 +54,7 @@ Feature: Activity navigation involving activities with access restrictions
|
||||
And I should see "Page 2" in the "#prev-activity-link" "css_element"
|
||||
And the "Jump to..." select box should not contain "Page 3"
|
||||
# Navigate to Page 2.
|
||||
And I follow "Page 2"
|
||||
And I click on "Page 2" "link" in the "page-content" "region"
|
||||
# Since Page 2 has now been viewed and deemed completed, Page 3 can now be accessed.
|
||||
And I should see "Page 3" in the "#next-activity-link" "css_element"
|
||||
And the "Jump to..." select box should contain "Page 3"
|
||||
|
||||
@@ -221,8 +221,9 @@ class behat_course extends behat_base {
|
||||
* @param int $section
|
||||
*/
|
||||
public function i_add_to_section($activity, $section) {
|
||||
$this->require_javascript('Please use the \'the following "activity" exists:\' data generator instead.');
|
||||
|
||||
if ($this->getSession()->getPage()->find('css', 'body#page-site-index') && (int)$section <= 1) {
|
||||
if ($this->getSession()->getPage()->find('css', 'body#page-site-index') && (int) $section <= 1) {
|
||||
// We are on the frontpage.
|
||||
if ($section) {
|
||||
// Section 1 represents the contents on the frontpage.
|
||||
@@ -237,41 +238,24 @@ class behat_course extends behat_base {
|
||||
$sectionxpath = "//li[@id='section-" . $section . "']";
|
||||
}
|
||||
|
||||
// Clicks add activity or resource section link.
|
||||
$sectionnode = $this->find('xpath', $sectionxpath);
|
||||
$this->execute('behat_general::i_click_on_in_the', [
|
||||
get_string('addresourceoractivity', 'moodle'),
|
||||
'button',
|
||||
$sectionnode,
|
||||
'NodeElement',
|
||||
]);
|
||||
|
||||
// Clicks the selected activity if it exists.
|
||||
$activityliteral = behat_context_helper::escape(ucfirst($activity));
|
||||
$activityxpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' modchooser ')]" .
|
||||
"/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' optioninfo ')]" .
|
||||
"/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' optionname ')]" .
|
||||
"[normalize-space(.)=$activityliteral]" .
|
||||
"/parent::a";
|
||||
|
||||
if ($this->running_javascript()) {
|
||||
|
||||
// Clicks add activity or resource section link.
|
||||
$sectionxpath = $sectionxpath . "/descendant::div" .
|
||||
"[contains(concat(' ', normalize-space(@class) , ' '), ' section-modchooser ')]/button";
|
||||
|
||||
$this->execute('behat_general::i_click_on', [$sectionxpath, 'xpath']);
|
||||
|
||||
// Clicks the selected activity if it exists.
|
||||
$activityxpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' modchooser ')]" .
|
||||
"/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' optioninfo ')]" .
|
||||
"/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' optionname ')]" .
|
||||
"[normalize-space(.)=$activityliteral]" .
|
||||
"/parent::a";
|
||||
|
||||
$this->execute('behat_general::i_click_on', [$activityxpath, 'xpath']);
|
||||
|
||||
} else {
|
||||
// Without Javascript.
|
||||
|
||||
// Selecting the option from the select box which contains the option.
|
||||
$selectxpath = $sectionxpath . "/descendant::div" .
|
||||
"[contains(concat(' ', normalize-space(@class), ' '), ' section_add_menus ')]" .
|
||||
"/descendant::select[option[normalize-space(.)=$activityliteral]]";
|
||||
$selectnode = $this->find('xpath', $selectxpath);
|
||||
$selectnode->selectOption($activity);
|
||||
|
||||
// Go button.
|
||||
$gobuttonxpath = $selectxpath . "/ancestor::form/descendant::input[@type='submit']";
|
||||
$gobutton = $this->find('xpath', $gobuttonxpath);
|
||||
$gobutton->click();
|
||||
}
|
||||
|
||||
$this->execute('behat_general::i_click_on', [$activityxpath, 'xpath']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1346,44 +1330,6 @@ class behat_course extends behat_base {
|
||||
return $this->getSession()->getPage()->findButton(get_string('turneditingoff')) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the category with the given idnumber.
|
||||
*
|
||||
* Please note that this function requires the category to exist. If it does not exist an ExpectationException is thrown.
|
||||
*
|
||||
* @param string $idnumber
|
||||
* @return string
|
||||
* @throws ExpectationException
|
||||
*/
|
||||
protected function get_category_id($idnumber) {
|
||||
global $DB;
|
||||
try {
|
||||
return $DB->get_field('course_categories', 'id', array('idnumber' => $idnumber), MUST_EXIST);
|
||||
} catch (dml_missing_record_exception $ex) {
|
||||
throw new ExpectationException(sprintf("There is no category in the database with the idnumber '%s'", $idnumber),
|
||||
$this->getSession());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the course with the given idnumber.
|
||||
*
|
||||
* Please note that this function requires the category to exist. If it does not exist an ExpectationException is thrown.
|
||||
*
|
||||
* @param string $idnumber
|
||||
* @return string
|
||||
* @throws ExpectationException
|
||||
*/
|
||||
protected function get_course_id($idnumber) {
|
||||
global $DB;
|
||||
try {
|
||||
return $DB->get_field('course', 'id', array('idnumber' => $idnumber), MUST_EXIST);
|
||||
} catch (dml_missing_record_exception $ex) {
|
||||
throw new ExpectationException(sprintf("There is no course in the database with the idnumber '%s'", $idnumber),
|
||||
$this->getSession());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the category node from within the listing on the management page.
|
||||
*
|
||||
|
||||
@@ -20,15 +20,12 @@ Feature: Managers can create courses
|
||||
When I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add the "Latest announcements" block
|
||||
Then "Latest announcements" "block" should exist
|
||||
And I follow "Announcements"
|
||||
And I am on the Announcements "forum activity" page
|
||||
And "Add a new topic" "link" should exist
|
||||
And "Subscription mode > Forced subscription" "link" should not exist in current page administration
|
||||
And "Subscription mode > Forced subscription" "text" should exist in current page administration
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Announcements"
|
||||
And I am on the Announcements "forum activity" page logged in as student1
|
||||
And "Add a new topic" "link" should not exist
|
||||
And "Forced subscription" "text" should exist in current page administration
|
||||
|
||||
|
||||
@@ -20,8 +20,7 @@ Feature: View subfolders in a course in-line
|
||||
| Name | Test folder |
|
||||
| Display folder contents | On a separate page |
|
||||
| Show subfolders expanded | |
|
||||
And I should see "Test folder"
|
||||
And I follow "Test folder"
|
||||
And I am on the "Test folder" "folder activity" page
|
||||
And I press "Edit"
|
||||
And I press "Create folder"
|
||||
And I set the field "New folder name" to "Test subfolder 1"
|
||||
@@ -32,7 +31,7 @@ Feature: View subfolders in a course in-line
|
||||
Scenario: Add a folder with two subfolders - view on separate page
|
||||
Given I am on "Course 1" course homepage
|
||||
And I should not see "Test subfolder 1"
|
||||
And I follow "Test folder"
|
||||
And I am on the "Test folder" "folder activity" page
|
||||
And I should see "Test subfolder 1"
|
||||
And I press "Edit"
|
||||
And I press "Create folder"
|
||||
@@ -41,13 +40,13 @@ Feature: View subfolders in a course in-line
|
||||
And I press "Save changes"
|
||||
When I am on "Course 1" course homepage
|
||||
Then I should not see "Test subfolder 2"
|
||||
And I follow "Test folder"
|
||||
And I am on the "Test folder" "folder activity" page
|
||||
And I should see "Test subfolder 2"
|
||||
Given I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test folder" "folder activity editing" page
|
||||
And I set the field "Show subfolders expanded" to "1"
|
||||
When I am on "Course 1" course homepage
|
||||
Then I should not see "Test subfolder 2"
|
||||
And I follow "Test folder"
|
||||
And I am on the "Test folder" "folder activity" page
|
||||
And I should see "Test subfolder 2"
|
||||
|
||||
@javascript
|
||||
@@ -63,8 +62,7 @@ Feature: View subfolders in a course in-line
|
||||
And I press "Save and return to course"
|
||||
Then I should see "Test subfolder 1"
|
||||
And I should not see "Test sub subfolder"
|
||||
Given I open "Test folder" actions menu
|
||||
When I click on "Edit settings" "link" in the "Test folder" activity
|
||||
And I am on the "Test folder" "folder activity editing" page
|
||||
And I set the field "Show subfolders expanded" to "1"
|
||||
And I press "Save and return to course"
|
||||
Then I should see "Test subfolder 1"
|
||||
|
||||
@@ -24,10 +24,7 @@ Feature: Render H5P content using filters
|
||||
|
||||
@javascript @external
|
||||
Scenario: Render an external H5P content URL.
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
Given I am on the PageName1 "page activity editing" page logged in as teacher1
|
||||
And I set the field "Page content" to "<div>Go for it</div>https://moodle.h5p.com/content/1290772960722742119/embed"
|
||||
When I click on "Save and display" "button"
|
||||
And I wait until the page is ready
|
||||
@@ -36,10 +33,7 @@ Feature: Render H5P content using filters
|
||||
|
||||
@javascript
|
||||
Scenario: Add an external H5P content URL in a link with the URL. Should be rendered.
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
Given I am on the PageName1 "page activity editing" page logged in as teacher1
|
||||
# This content won't be displayed, so this scenario shouldn't be labeled as external.
|
||||
And I set the field "Page content" to "<a href='https://moodle.h5p.com/content/1290772960722742119/embed'>https://moodle.h5p.com/content/1290772960722742119/embed</a>"
|
||||
When I click on "Save and display" "button"
|
||||
@@ -48,10 +42,7 @@ Feature: Render H5P content using filters
|
||||
Then I should see "Lorum ipsum"
|
||||
|
||||
Scenario: Add an external H5P content URL in a link with text. Shouldn't be rendered.
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
Given I am on the PageName1 "page activity editing" page logged in as teacher1
|
||||
# This content won't be displayed, so this scenario shouldn't be labeled as external.
|
||||
And I set the field "Page content" to "<a href='https://moodle.h5p.com/content/1290772960722742119/embed'>Here you are the content</a>"
|
||||
When I click on "Save and display" "button"
|
||||
@@ -67,8 +58,7 @@ Feature: Render H5P content using filters
|
||||
| Name | ipsumFile |
|
||||
And I upload "h5p/tests/fixtures/ipsums.h5p" file to "Select files" filemanager
|
||||
And I press "Save and return to course"
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName1 "page activity editing" page
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I click on "Browse repositories..." "button" in the "Insert H5P" "dialogue"
|
||||
And I click on "Server files" "link" in the ".fp-repo-area" "css_element"
|
||||
@@ -85,9 +75,7 @@ Feature: Render H5P content using filters
|
||||
Then I should see "Lorum ipsum"
|
||||
And I switch to the main frame
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I am on the PageName1 "page activity" page logged in as student1
|
||||
# Switch to iframe created by filter
|
||||
And I switch to "h5p-iframe" class iframe
|
||||
# Switch to iframe created by embed.php page
|
||||
@@ -104,8 +92,7 @@ Feature: Render H5P content using filters
|
||||
| Name | ipsumFile |
|
||||
And I upload "h5p/tests/fixtures/ipsums.h5p" file to "Select files" filemanager
|
||||
And I press "Save and return to course"
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName1 "page activity editing" page
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I click on "Browse repositories..." "button" in the "Insert H5P" "dialogue"
|
||||
And I click on "Server files" "link" in the ".fp-repo-area" "css_element"
|
||||
@@ -129,8 +116,7 @@ Feature: Render H5P content using filters
|
||||
| Name | ipsumFileTeacher |
|
||||
And I upload "h5p/tests/fixtures/ipsums.h5p" file to "Select files" filemanager
|
||||
And I press "Save and return to course"
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName1 "page activity editing" page
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I click on "Browse repositories..." "button" in the "Insert H5P" "dialogue"
|
||||
And I click on "Server files" "link" in the ".fp-repo-area" "css_element"
|
||||
@@ -153,8 +139,7 @@ Feature: Render H5P content using filters
|
||||
| Name | ipsumFile |
|
||||
And I upload "h5p/tests/fixtures/ipsums.h5p" file to "Select files" filemanager
|
||||
And I press "Save and return to course"
|
||||
And I follow "PageName2"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName2 "page activity editing" page
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I click on "Browse repositories..." "button" in the "Insert H5P" "dialogue"
|
||||
And I click on "Server files" "link" in the ".fp-repo-area" "css_element"
|
||||
@@ -172,9 +157,7 @@ Feature: Render H5P content using filters
|
||||
And I should see "Lorum ipsum"
|
||||
And I switch to the main frame
|
||||
And I log out
|
||||
When I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I am on the PageName1 "page activity" page logged in as teacher1
|
||||
# Switch to iframe created by filter
|
||||
And I switch to "h5p-iframe" class iframe
|
||||
# Switch to iframe created by embed.php page
|
||||
@@ -195,9 +178,8 @@ Feature: Render H5P content using filters
|
||||
And I click on "Upload H5P content types" "button" in the "#fitem_id_uploadlibraries" "css_element"
|
||||
And I click on "Disable" "link" in the "Accordion" "table_row"
|
||||
# Add H5P content to the page.
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on "Course 1" course homepage
|
||||
And I am on the PageName1 "page activity editing" page
|
||||
When I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I click on "Browse repositories..." "button" in the "Insert H5P" "dialogue"
|
||||
And I click on "Private files" "link" in the ".fp-repo-area" "css_element"
|
||||
@@ -212,9 +194,8 @@ Feature: Render H5P content using filters
|
||||
And I switch to the main frame
|
||||
And I navigate to "H5P > Manage H5P content types" in site administration
|
||||
And I click on "Enable" "link" in the "Accordion" "table_row"
|
||||
And I am on "Course 1" course homepage
|
||||
# Content should be deployed now that main library is enabled.
|
||||
And I follow "PageName1"
|
||||
And I am on the PageName1 "page activity" page
|
||||
# Switch to iframe created by filter.
|
||||
And I switch to "h5p-iframe" class iframe
|
||||
# Switch to iframe created by embed.php page.
|
||||
@@ -224,9 +205,8 @@ Feature: Render H5P content using filters
|
||||
And I switch to the main frame
|
||||
And I navigate to "H5P > Manage H5P content types" in site administration
|
||||
And I click on "Disable" "link" in the "Accordion" "table_row"
|
||||
And I am on "Course 1" course homepage
|
||||
# Library is disabled again, so an error should be displayed.
|
||||
And I follow "PageName1"
|
||||
And I am on the PageName1 "page activity" page
|
||||
And I switch to "h5p-iframe" class iframe
|
||||
And I should see "This file can't be displayed because its content type is disabled."
|
||||
And I should not see "Lorum ipsum"
|
||||
|
||||
@@ -20,7 +20,7 @@ Feature: Marking guides can be created and edited
|
||||
| activity | assign |
|
||||
| course | C1 |
|
||||
| idnumber | assign1 |
|
||||
| name | Test assignment 1 name |
|
||||
| name | Test assignment 1 name |
|
||||
| intro | Test assignment description |
|
||||
| section | 1 |
|
||||
| assignsubmission_file_enabled | 1 |
|
||||
@@ -30,10 +30,7 @@ Feature: Marking guides can be created and edited
|
||||
| assignfeedback_comments_enabled | 1 |
|
||||
| assignfeedback_file_enabled | 1 |
|
||||
| assignfeedback_comments_commentinline | 1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test assignment 1 name" "assign activity editing" page logged in as teacher1
|
||||
And I set the following fields to these values:
|
||||
| Grading method | Marking guide |
|
||||
And I press "Save and return to course"
|
||||
@@ -65,7 +62,8 @@ Feature: Marking guides can be created and edited
|
||||
@javascript
|
||||
Scenario: Deleting criterion and comment
|
||||
# Deleting criterion
|
||||
When I go to "Test assignment 1 name" advanced grading definition page
|
||||
When I am on "Course 1" course homepage
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And I click on "Delete criterion" "button" in the "Guide criterion B" "table_row"
|
||||
And I press "Yes"
|
||||
And I press "Save"
|
||||
@@ -74,7 +72,8 @@ Feature: Marking guides can be created and edited
|
||||
And I should see "WARNING: Your marking guide has a maximum grade of 70 points"
|
||||
But I should not see "Guide criterion B"
|
||||
# Deleting a frequently used comment
|
||||
When I go to "Test assignment 1 name" advanced grading definition page
|
||||
When I am on "Course 1" course homepage
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And I click on "Delete comment" "button" in the "Comment 3" "table_row"
|
||||
And I press "Yes"
|
||||
And I press "Save"
|
||||
@@ -105,9 +104,7 @@ Feature: Marking guides can be created and edited
|
||||
Then I should see "80" in the "Student 1" "table_row"
|
||||
And I log out
|
||||
# Viewing it as a student.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I am on the "Test assignment 1 name" "assign activity" page logged in as student1
|
||||
And I should see "80" in the ".feedback" "css_element"
|
||||
And I should see "Marking guide test description" in the ".feedback" "css_element"
|
||||
And I should see "Very good"
|
||||
|
||||
@@ -70,9 +70,7 @@ Feature: Rubrics can be created and edited
|
||||
And I should see "35" in the "Student 1" "table_row"
|
||||
And I log out
|
||||
# Viewing it as a student.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I am on the "Test assignment 1 name" "assign activity" page logged in as student1
|
||||
And I should see "35" in the ".feedback" "css_element"
|
||||
And I should see "Rubric test description" in the ".feedback" "css_element"
|
||||
And I should see "In general... work harder..."
|
||||
@@ -80,8 +78,7 @@ Feature: Rubrics can be created and edited
|
||||
And the level with "20" points is selected for the rubric criterion "Criterion 1"
|
||||
And the level with "5" points is selected for the rubric criterion "Criterion 3"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
# Editing a rubric definition without regrading students.
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And "Save as draft" "button" should not exist
|
||||
@@ -93,9 +90,7 @@ Feature: Rubrics can be created and edited
|
||||
And I press "Continue"
|
||||
And I log out
|
||||
# Check that the student still sees the grade.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I am on the "Test assignment 1 name" "assign activity" page logged in as student1
|
||||
And I should see "35" in the ".feedback" "css_element"
|
||||
And the level with "20" points is selected for the rubric criterion "Criterion 1"
|
||||
And I log out
|
||||
@@ -110,30 +105,23 @@ Feature: Rubrics can be created and edited
|
||||
And I press "Continue"
|
||||
And I log out
|
||||
# Check that the student doesn't see the grade.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I am on the "Test assignment 1 name" "assign activity" page logged in as student1
|
||||
And I should see "35" in the ".feedback" "css_element"
|
||||
And the level with "20" points is not selected for the rubric criterion "Criterion 1"
|
||||
And I log out
|
||||
# Regrade student.
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I am on the "Test assignment 1 name" "assign activity" page logged in as teacher1
|
||||
And I go to "Student 1" "Test assignment 1 name" activity advanced grading page
|
||||
And I should see "The rubric definition was changed after this student had been graded. The student can not see this rubric until you check the rubric and update the grade."
|
||||
And I save the advanced grading form
|
||||
And I log out
|
||||
# Check that the student sees the grade again.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I am on the "Test assignment 1 name" "assign activity" page logged in as student1
|
||||
And I should see "31.82" in the ".feedback" "css_element"
|
||||
And the level with "20" points is not selected for the rubric criterion "Criterion 1"
|
||||
# Hide all rubric info for students
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And I set the field "Allow users to preview rubric (otherwise it will only be displayed after grading)" to ""
|
||||
And I set the field "Display rubric description during evaluation" to ""
|
||||
@@ -145,9 +133,7 @@ Feature: Rubrics can be created and edited
|
||||
And I press "Continue"
|
||||
And I log out
|
||||
# Students should not see anything.
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1 name"
|
||||
And I am on the "Test assignment 1 name" "assign activity" page logged in as student1
|
||||
And I should not see "Criterion 1" in the ".submissionstatustable" "css_element"
|
||||
And I should not see "Criterion 2" in the ".submissionstatustable" "css_element"
|
||||
And I should not see "Criterion 3" in the ".submissionstatustable" "css_element"
|
||||
|
||||
@@ -18,10 +18,7 @@ Feature: Reuse my rubrics in other activities
|
||||
| activity | name | intro | course | section | idnumber |
|
||||
| assign | Test assignment 1 name | Test assignment 1 description | C1 | 1 | assign1 |
|
||||
| assign | Test assignment 2 name | Test assignment 2 description | C1 | 1 | assign1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test assignment 1 name" "assign activity editing" page logged in as teacher1
|
||||
And I set the following fields to these values:
|
||||
| Grading method | Rubric |
|
||||
And I press "Save and return to course"
|
||||
@@ -34,10 +31,7 @@ Feature: Reuse my rubrics in other activities
|
||||
| Criterion 2 | Level 21 | 21 | Level 22 | 22 | Level 3 | 23 |
|
||||
| Criterion 3 | Level 31 | 31 | Level 32 | 32 | | |
|
||||
And I press "Save rubric and make it ready"
|
||||
And I am on "Course 1" course homepage
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment 2"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test assignment 2 name" "assign activity editing" page
|
||||
And I set the following fields to these values:
|
||||
| Grading method | Rubric |
|
||||
And I press "Save and return to course"
|
||||
@@ -51,7 +45,6 @@ Feature: Reuse my rubrics in other activities
|
||||
And I should see "Criterion 1"
|
||||
And I should see "Criterion 2"
|
||||
And I should see "Criterion 3"
|
||||
And I press "Cancel"
|
||||
|
||||
@javascript
|
||||
Scenario: A teacher can reuse one of his/her previously created rubrics, with Javascript enabled
|
||||
|
||||
@@ -28,16 +28,13 @@ Feature: We can change what we are viewing on the grader report
|
||||
| Description | Submit your online text |
|
||||
| assignsubmission_onlinetext_enabled | 1 |
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment name 1"
|
||||
And I am on the "Test assignment name 1" "assign activity" page logged in as student1
|
||||
When I press "Add submission"
|
||||
And I set the following fields to these values:
|
||||
| Online text | This is a submission for assignment 1 |
|
||||
And I press "Save changes"
|
||||
Then I should see "Submitted for grading"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment name 2"
|
||||
And I am on the "Test assignment name 2" "assign activity" page
|
||||
When I press "Add submission"
|
||||
And I set the following fields to these values:
|
||||
| Online text | This is a submission for assignment 2 |
|
||||
@@ -55,7 +52,7 @@ Feature: We can change what we are viewing on the grader report
|
||||
|
||||
@javascript
|
||||
Scenario: View and minimise the grader report containing hidden activities
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
When I am on "Course 1" course homepage with editing mode on
|
||||
And I open "Test assignment name 2" actions menu
|
||||
And I click on "Hide" "link" in the "Test assignment name 2" activity
|
||||
And I am on "Course 1" course homepage
|
||||
@@ -83,7 +80,7 @@ Feature: We can change what we are viewing on the grader report
|
||||
|
||||
@javascript
|
||||
Scenario: View and minimise the grader report containing hidden activities without the 'moodle/grade:viewhidden' capability
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
When I am on "Course 1" course homepage with editing mode on
|
||||
And I open "Test assignment name 2" actions menu
|
||||
And I click on "Hide" "link" in the "Test assignment name 2" activity
|
||||
And I log out
|
||||
|
||||
@@ -30,9 +30,7 @@ Feature: We can bulk insert grades for students in a course
|
||||
| assign | C1 | a4 | Test assignment four | Submit nothing! |
|
||||
|
||||
Scenario: I can bulk insert grades and check their override flags for grade view.
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment one"
|
||||
Given I am on the "Test assignment one" "assign activity" page logged in as teacher1
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the following fields to these values:
|
||||
@@ -70,9 +68,7 @@ Feature: We can bulk insert grades for students in a course
|
||||
And the field "Override for Student 4" matches value "1"
|
||||
|
||||
Scenario: I can bulk insert grades and check their override flags for user view.
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment two"
|
||||
Given I am on the "Test assignment two" "assign activity" page logged in as teacher1
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the following fields to these values:
|
||||
@@ -101,8 +97,7 @@ Feature: We can bulk insert grades for students in a course
|
||||
And the field "Override for Test assignment four" matches value "1"
|
||||
|
||||
Scenario: I can not update grades if the value is out of bounds.
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
Given I am on the "Course 1" course page logged in as teacher1
|
||||
And I navigate to "View > Grader report" in the course gradebook
|
||||
And I follow "Single view for Test assignment one"
|
||||
And I set the field "Perform bulk insert" to "1"
|
||||
|
||||
@@ -11,8 +11,8 @@ Feature: We can change the grading type and maximum grade point values
|
||||
| fullname | shortname | category | format |
|
||||
| Course 1 | C1 | 0 | topics |
|
||||
Given the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro |
|
||||
| assign | C1 | assign1| Test Assignment 1 | Test Assignment 1 |
|
||||
| activity | course | idnumber | name | intro |
|
||||
| assign | C1 | assign1 | Test Assignment 1 | Test Assignment 1 |
|
||||
And I log in as "admin"
|
||||
And I navigate to "Grades > General settings" in site administration
|
||||
And I set the following fields to these values:
|
||||
@@ -23,9 +23,8 @@ Feature: We can change the grading type and maximum grade point values
|
||||
|
||||
@javascript
|
||||
Scenario: Validate that switching the type of grading used correctly disables input form elements
|
||||
When I follow "Test Assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
Given I am on the "Test Assignment 1" "assign activity editing" page
|
||||
When I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "Point"
|
||||
Then the "Scale" "select" should be disabled
|
||||
And the "Maximum grade" "field" should be enabled
|
||||
@@ -39,46 +38,42 @@ Feature: We can change the grading type and maximum grade point values
|
||||
|
||||
@javascript
|
||||
Scenario: Create an activity with a maximum grade point value less than the system maximum
|
||||
When I follow "Test Assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
Given I am on the "Test Assignment 1" "assign activity editing" page
|
||||
When I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "point"
|
||||
And I set the field "grade[modgrade_point]" to "600"
|
||||
And I press "Save and display"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test Assignment 1" "assign activity editing" page
|
||||
Then the field "grade[modgrade_point]" matches value "600"
|
||||
And the "Scale" "select" should be disabled
|
||||
And I press "Save and return to course"
|
||||
|
||||
@javascript
|
||||
Scenario: Create an activity with a scale as the grade type
|
||||
When I follow "Test Assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
Given I am on the "Test Assignment 1" "assign activity editing" page
|
||||
When I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "Scale"
|
||||
And I set the field "grade[modgrade_scale]" to "Separate and Connected ways of knowing"
|
||||
And I press "Save and display"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test Assignment 1" "assign activity editing" page
|
||||
Then the field "grade[modgrade_scale]" matches value "Separate and Connected ways of knowing"
|
||||
And the "Maximum grade" "field" should be disabled
|
||||
And I press "Save and return to course"
|
||||
|
||||
@javascript
|
||||
Scenario: Create an activity with no grade as the grade type
|
||||
When I follow "Test Assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
Given I am on the "Test Assignment 1" "assign activity editing" page
|
||||
When I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "None"
|
||||
And I press "Save and display"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test Assignment 1" "assign activity editing" page
|
||||
And the "Scale" "select" should be disabled
|
||||
And the "Maximum grade" "field" should be disabled
|
||||
And I press "Save and return to course"
|
||||
|
||||
Scenario: Create an activity with a maximum grade point value higher than the system maximum
|
||||
When I follow "Test Assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
Given I am on the "Test Assignment 1" "assign activity editing" page
|
||||
When I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "Point"
|
||||
And I set the field "grade[modgrade_point]" to "20000"
|
||||
And I press "Save and display"
|
||||
@@ -86,9 +81,8 @@ Feature: We can change the grading type and maximum grade point values
|
||||
And I press "Cancel"
|
||||
|
||||
Scenario: Create an activity with a valid maximum grade point and then change the system maximum to be lower
|
||||
When I follow "Test Assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
Given I am on the "Test Assignment 1" "assign activity editing" page
|
||||
When I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "point"
|
||||
And I set the field "grade[modgrade_point]" to "600"
|
||||
And I press "Save and display"
|
||||
@@ -96,9 +90,7 @@ Feature: We can change the grading type and maximum grade point values
|
||||
And I set the following fields to these values:
|
||||
| Grade point maximum | 100 |
|
||||
And I press "Save changes"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test Assignment 1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test Assignment 1" "assign activity editing" page
|
||||
And I press "Save and display"
|
||||
Then I should see "Invalid grade value. This must be an integer between 1 and 100"
|
||||
And I press "Cancel"
|
||||
|
||||
@@ -23,8 +23,7 @@ Feature: Regrading grades does not unnecessarily mark some as overriden
|
||||
And I navigate to "Grades > Grade category settings" in site administration
|
||||
And I set the field "Available aggregation types" to "Weighted mean of grades"
|
||||
And I press "Save changes"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Assignment 1"
|
||||
And I am on the "Assignment 1" "assign activity" page
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Grade out of 100" to "80"
|
||||
@@ -58,8 +57,7 @@ Feature: Regrading grades does not unnecessarily mark some as overriden
|
||||
|
||||
@javascript
|
||||
Scenario: Confirm overridden course total does not get regraded when activity grade is changed
|
||||
Given I am on "Course 1" course homepage
|
||||
And I follow "Assignment 1"
|
||||
Given I am on the "Assignment 1" "assign activity" page
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Grade out of 100" to "90"
|
||||
|
||||
@@ -43,10 +43,7 @@ Feature: View gradebook when scales are used
|
||||
And the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro | gradecategory |
|
||||
| assign | C1 | a1 | Test assignment one | Submit something! | Sub category 1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment one"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test assignment one" "assign activity editing" page logged in as teacher1
|
||||
And I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "Scale"
|
||||
And I set the field "grade[modgrade_scale]" to "EN Letterscale"
|
||||
|
||||
@@ -39,10 +39,7 @@ Feature: View gradebook when single item scales are used
|
||||
| assign | C1 | a1 | Test assignment one | Submit something! | <span lang="en" class="multilang">EN</span><span lang="fr" class="multilang">FR</span> Sub category 1 |
|
||||
And the "multilang" filter is "on"
|
||||
And the "multilang" filter applies to "content and headings"
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment one"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test assignment one" "assign activity editing" page logged in as teacher1
|
||||
And I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "Scale"
|
||||
And I set the field "grade[modgrade_scale]" to "EN Singleitem"
|
||||
|
||||
@@ -29,5 +29,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['parentlanguage'] = '';
|
||||
$string['thisdirection'] = 'ltr';
|
||||
$string['thislanguage'] = 'Afrikaans';
|
||||
|
||||
@@ -45,6 +45,6 @@ $string['invalidmd5'] = 'La variable de comprobación era incorrecta - inténtel
|
||||
$string['missingrequiredfield'] = 'Falta algún campo necesario';
|
||||
$string['remotedownloaderror'] = '<p>Falló la descarga del componente a su servidor. Se recomienda verificar los ajustes del proxy, extensión PHP cURL.</p>
|
||||
<p>Debe descargar el <a href="{$a->url}">{$a->url}</a> archivo manualmente, copiarlo en "{$a->dest}" en su servidor y descomprimirlo allí.</p>';
|
||||
$string['wrongdestpath'] = 'Ruta de destino errónea.';
|
||||
$string['wrongsourcebase'] = 'Base de fuente de URL errónea.';
|
||||
$string['wrongdestpath'] = 'Ruta de destino errónea';
|
||||
$string['wrongsourcebase'] = 'Base de URL de origen errónea';
|
||||
$string['wrongzipfilename'] = 'Nombre de fichero ZIP incorrecto';
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?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['thislanguage'] = 'Hornjoserbsski';
|
||||
@@ -33,7 +33,7 @@ $string['admindirname'] = 'Rendszergazda-könyvtár';
|
||||
$string['availablelangs'] = 'Elérhető nyelvek listája';
|
||||
$string['chooselanguagehead'] = 'Nyelv kiválasztása';
|
||||
$string['chooselanguagesub'] = 'Válasszon nyelvet a telepítéshez! Ez lesz a portál alapbeállítás szerinti nyelve, de később módosíthatja.';
|
||||
$string['clialreadyconfigured'] = 'A config.php már létezik, a portál telepítéséhez használja az admin/cli/install_database.php állományt.';
|
||||
$string['clialreadyconfigured'] = 'A config.php már létezik, a Moodle portálra telepítéséhez használja az admin/cli/install_database.php állományt.';
|
||||
$string['clialreadyinstalled'] = 'A config.php már létezik, a portál frissítéséhez használja az admin/cli/upgrade.php állományt.';
|
||||
$string['cliinstallheader'] = 'A Moodle {$a} parancssori telepítő programja';
|
||||
$string['clitablesexist'] = 'Azt adatbázistáblák már léteznek, a CLI telepítése leáll.';
|
||||
@@ -57,7 +57,9 @@ $string['pathshead'] = 'Útvonalak megerősítése';
|
||||
$string['pathsrodataroot'] = 'Az adatok gyökérkönyvtára nem írható.';
|
||||
$string['pathsroparentdataroot'] = 'A felettes könyvtár ({$a->parent}) nem írható. A telepítő nem tudja létrehozni az adatkönyvtárat ({$a->dataroot}).';
|
||||
$string['pathssubadmindir'] = 'Egy pár webes gazdagép esetén az /admin speciális URL pl. a vezérlőpanel eléréséhez. Ez ütközik a Moodle admin oldalainak standard helyével. Javítás: a telepítésben nevezze át a rendszergazda könyvtárát, az új nevet pedig írja be ide. Például: <em>moodleadmin</em>. Ezzel helyrehozhatók a Moodle rendszergazdai hivatkozásai.';
|
||||
$string['pathssubdataroot'] = 'Szüksége van egy helyre, ahol a Moodle mentheti a feltöltött állományokat. Ez a könyvtár a webszerver felhasználója (általában \'nobody\' vagy \'apache\') számára legyen mind olvasható, MIND ÍRHATÓ. Ha nem létezik, a telepítő megpróbálja létrehozni.';
|
||||
$string['pathssubdataroot'] = 'Szüksége van egy helyre, ahol a Moodle mentheti a feltöltött állományokat. Ez a könyvtár a webszerver felhasználója (általában \'www-data\', \'nobody\' vagy \'apache\') számára legyen mind olvasható, mind írható.
|
||||
A könyvtár a webről közvetlenül nem lehet elérhető.
|
||||
Ha nem létezik, a telepítő megpróbálja létrehozni.';
|
||||
$string['pathssubdirroot'] = 'Teljes útvonal a Moodle telepítéséhez. ';
|
||||
$string['pathssubwwwroot'] = 'A Moodle elérésére használandó teljes webcím. A Moodle egyszerre több címről nem érhető el. Ha portálja több címet használ, a jelen cím kivételével az összeshez állandó
|
||||
átirányítást kell beállítania. Ha portálja mind intranetről, mind az internetről elérhető, a nyilvános címet itt adja meg, a DNS-t pedig úgy állítsa be, hogy az intranetről a nyilvános cím is elérhető legyen. Ha a cím hibás, módosítsa böngészőjében a webcímet, hogy a telepítés egy másik értékkel induljon újra.';
|
||||
|
||||
@@ -151,6 +151,7 @@ $string['on'] = 'On';
|
||||
$string['online'] = 'Online';
|
||||
$string['otherparticipants'] = 'Other participants';
|
||||
$string['outputnotavailable'] = 'Not available';
|
||||
$string['page-message-x'] = 'Any message pages';
|
||||
$string['participants'] = 'Participants';
|
||||
$string['pendingcontactrequests'] = 'There are {$a} pending contact requests';
|
||||
$string['permitted'] = 'Permitted';
|
||||
|
||||
@@ -209,6 +209,7 @@ function uninstall_plugin($type, $name) {
|
||||
$DB->delete_records('event', ['component' => $component]);
|
||||
|
||||
// Delete scheduled tasks.
|
||||
$DB->delete_records('task_adhoc', ['component' => $component]);
|
||||
$DB->delete_records('task_scheduled', array('component' => $component));
|
||||
|
||||
// Delete Inbound Message datakeys.
|
||||
|
||||
File diff suppressed because one or more lines are too long
+24
-8
@@ -150,29 +150,45 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo
|
||||
* @return {Promise[]} The Promises for each of the supplied requests.
|
||||
* The order of the Promise matches the order of requests exactly.
|
||||
*
|
||||
* @example <caption>A simple example that you might find in a repository module</caption>
|
||||
*
|
||||
* import {call as fetchMany} from 'core/ajax';
|
||||
*
|
||||
* export const fetchMessages = timeSince => fetchMany([{methodname: 'core_message_get_messages', args: {timeSince}}])[0];
|
||||
*
|
||||
* export const fetchNotifications = timeSince => fetchMany([{
|
||||
* methodname: 'core_message_get_notifications',
|
||||
* args: {
|
||||
* timeSince,
|
||||
* }
|
||||
* }])[0];
|
||||
*
|
||||
* export const fetchSomethingElse = (some, params, here) => fetchMany([{
|
||||
* methodname: 'core_get_something_else',
|
||||
* args: {
|
||||
* some,
|
||||
* params,
|
||||
* gohere: here,
|
||||
* },
|
||||
* }])[0];
|
||||
*
|
||||
* @example <caption>An example of fetching a string using the cachekey parameter</caption>
|
||||
* import {call as fetchMany} from 'core/ajax';
|
||||
* import * as Notification from 'core/notification';
|
||||
*
|
||||
* export const performAction = (some, args) => {
|
||||
* Promises.all(fetchMany('core_get_string', {
|
||||
* Promises.all(fetchMany([{methodname: 'core_get_string', args: {
|
||||
* stringid: 'do_not_copy',
|
||||
* component: 'core',
|
||||
* lang: 'en',
|
||||
* stringparams: [],
|
||||
* }, true, false, false, undefined, M.cfg.langrev))
|
||||
* }}], true, false, false, undefined, M.cfg.langrev))
|
||||
* .then(([doNotCopyString]) => {
|
||||
* window.console.log(doNotCopyString);
|
||||
* })
|
||||
* .catch(Notification.exception);
|
||||
* };
|
||||
*
|
||||
* @example <caption>A simple example that you might find in a repository function</caption>
|
||||
*
|
||||
* import {call as fetchMany} from 'core/ajax';
|
||||
*
|
||||
* export const fetchMessages = timeSince => fetchMany('core_message_get_messages', {timeSince}})[0];
|
||||
* export const fetchNotifications = timeSince => fetchMany('core_message_get_notifications', {timeSince}})[0];
|
||||
*/
|
||||
call: function(requests, async, loginrequired, nosessionupdate, timeout, cachekey) {
|
||||
$(window).bind('beforeunload', function() {
|
||||
|
||||
@@ -120,7 +120,7 @@ class behat_core_generator extends behat_generator_base {
|
||||
'activities' => [
|
||||
'singular' => 'activity',
|
||||
'datagenerator' => 'activity',
|
||||
'required' => ['activity', 'idnumber', 'course'],
|
||||
'required' => ['activity', 'course'],
|
||||
'switchids' => ['course' => 'course', 'gradecategory' => 'gradecat', 'grouping' => 'groupingid'],
|
||||
],
|
||||
'blocks' => [
|
||||
@@ -389,6 +389,17 @@ class behat_core_generator extends behat_generator_base {
|
||||
}
|
||||
}
|
||||
|
||||
if (!array_key_exists('idnumber', $data)) {
|
||||
$data['idnumber'] = $data['name'];
|
||||
if (strlen($data['name']) > 100) {
|
||||
throw new Exception(
|
||||
"Activity '{$activityname}' cannot be used as the default idnumber. " .
|
||||
"The idnumber has a max length of 100 chars. " .
|
||||
"Please manually specify an idnumber."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// We split $data in the activity $record and the course module $options.
|
||||
$cmoptions = array();
|
||||
$cmcolumns = $DB->get_columns('course_modules');
|
||||
|
||||
@@ -368,6 +368,25 @@ abstract class behat_generator_base {
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the course cmid for the specified activity based on the activity's idnumber.
|
||||
*
|
||||
* Note: this does not check the module type, only the idnumber.
|
||||
*
|
||||
* @throws Exception
|
||||
* @param string $idnumber
|
||||
* @return int
|
||||
*/
|
||||
protected function get_activity_id(string $idnumber) {
|
||||
global $DB;
|
||||
|
||||
if (!$id = $DB->get_field('course_modules', 'id', ['idnumber' => $idnumber])) {
|
||||
throw new Exception('The specified activity with idnumber "' . $idnumber . '" could not be found.');
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the group id from it's idnumber.
|
||||
* @throws Exception
|
||||
@@ -512,4 +531,46 @@ abstract class behat_generator_base {
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a coursemodule from an activity name or idnumber.
|
||||
*
|
||||
* @param string $activity
|
||||
* @param string $identifier
|
||||
* @return cm_info
|
||||
*/
|
||||
protected function get_cm_by_activity_name(string $activity, string $identifier): cm_info {
|
||||
global $DB;
|
||||
|
||||
$coursetable = new \core\dml\table('course', 'c', 'c');
|
||||
$courseselect = $coursetable->get_field_select();
|
||||
$coursefrom = $coursetable->get_from_sql();
|
||||
|
||||
$cmtable = new \core\dml\table('course_modules', 'cm', 'cm');
|
||||
$cmfrom = $cmtable->get_from_sql();
|
||||
|
||||
$acttable = new \core\dml\table($activity, 'act', 'act');
|
||||
$actselect = $acttable->get_field_select();
|
||||
$actfrom = $acttable->get_from_sql();
|
||||
|
||||
$sql = <<<EOF
|
||||
SELECT cm.id as cmid, {$courseselect}, {$actselect}
|
||||
FROM {$cmfrom}
|
||||
INNER JOIN {$coursefrom} ON c.id = cm.course
|
||||
INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
INNER JOIN {$actfrom} ON cm.instance = act.id
|
||||
WHERE cm.idnumber = :idnumber OR act.name = :name
|
||||
EOF;
|
||||
|
||||
$result = $DB->get_record_sql($sql, [
|
||||
'modname' => $activity,
|
||||
'idnumber' => $identifier,
|
||||
'name' => $identifier,
|
||||
], MUST_EXIST);
|
||||
|
||||
$course = $coursetable->extract_from_result($result);
|
||||
$instancedata = $acttable->extract_from_result($result);
|
||||
|
||||
return get_fast_modinfo($course)->get_cm($result->cmid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,10 +498,11 @@ trait behat_session_trait {
|
||||
/**
|
||||
* Require that javascript be available in the current Session.
|
||||
*
|
||||
* @param null|string $message An additional information message to show when JS is not available
|
||||
* @throws DriverException
|
||||
*/
|
||||
protected function require_javascript() {
|
||||
return self::require_javascript_in_session($this->getSession());
|
||||
protected function require_javascript(?string $message = null) {
|
||||
return self::require_javascript_in_session($this->getSession(), $message);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -518,14 +519,19 @@ trait behat_session_trait {
|
||||
* Require that javascript be available for the specified Session.
|
||||
*
|
||||
* @param Session $session
|
||||
* @param null|string $message An additional information message to show when JS is not available
|
||||
* @throws DriverException
|
||||
*/
|
||||
protected static function require_javascript_in_session(Session $session): void {
|
||||
protected static function require_javascript_in_session(Session $session, ?string $message = null): void {
|
||||
if (self::running_javascript_in_session($session)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new DriverException('Javascript is required');
|
||||
$error = "Javascript is required for this step.";
|
||||
if ($message) {
|
||||
$error = "{$error} {$message}";
|
||||
}
|
||||
throw new DriverException($error);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1437,4 +1443,137 @@ EOF;
|
||||
$timeout = self::get_real_timeout(30) * 1000 * $factor;
|
||||
$driver->getWebDriver()->getCommandExecutor()->setRequestTimeout($timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the course category id from an identifier.
|
||||
*
|
||||
* The category idnumber, and name are checked.
|
||||
*
|
||||
* @param string $identifier
|
||||
* @return int|null
|
||||
*/
|
||||
protected function get_category_id(string $identifier): ?int {
|
||||
global $DB;
|
||||
|
||||
$sql = <<<EOF
|
||||
SELECT id
|
||||
FROM {course_categories}
|
||||
WHERE idnumber = :idnumber
|
||||
OR name = :name
|
||||
EOF;
|
||||
|
||||
$result = $DB->get_field_sql($sql, [
|
||||
'idnumber' => $identifier,
|
||||
'name' => $identifier,
|
||||
]);
|
||||
|
||||
return $result ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the course id from an identifier.
|
||||
*
|
||||
* The course idnumber, shortname, and fullname are checked.
|
||||
*
|
||||
* @param string $identifier
|
||||
* @return int|null
|
||||
*/
|
||||
protected function get_course_id(string $identifier): ?int {
|
||||
global $DB;
|
||||
|
||||
$sql = <<<EOF
|
||||
SELECT id
|
||||
FROM {course}
|
||||
WHERE idnumber = :idnumber
|
||||
OR shortname = :shortname
|
||||
OR fullname = :fullname
|
||||
EOF;
|
||||
|
||||
$result = $DB->get_field_sql($sql, [
|
||||
'idnumber' => $identifier,
|
||||
'shortname' => $identifier,
|
||||
'fullname' => $identifier,
|
||||
]);
|
||||
|
||||
return $result ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activity course module id from its idnumber.
|
||||
*
|
||||
* Note: Only idnumber is supported here, not name at this time.
|
||||
*
|
||||
* @param string $identifier
|
||||
* @return cm_info|null
|
||||
*/
|
||||
protected function get_course_module_for_identifier(string $identifier): ?cm_info {
|
||||
global $DB;
|
||||
|
||||
$coursetable = new \core\dml\table('course', 'c', 'c');
|
||||
$courseselect = $coursetable->get_field_select();
|
||||
$coursefrom = $coursetable->get_from_sql();
|
||||
|
||||
$cmtable = new \core\dml\table('course_modules', 'cm', 'cm');
|
||||
$cmfrom = $cmtable->get_from_sql();
|
||||
|
||||
$sql = <<<EOF
|
||||
SELECT {$courseselect}, cm.id as cmid
|
||||
FROM {$cmfrom}
|
||||
INNER JOIN {$coursefrom} ON c.id = cm.course
|
||||
WHERE cm.idnumber = :idnumber
|
||||
EOF;
|
||||
|
||||
$result = $DB->get_record_sql($sql, [
|
||||
'idnumber' => $identifier,
|
||||
]);
|
||||
|
||||
if ($result) {
|
||||
$course = $coursetable->extract_from_result($result);
|
||||
return get_fast_modinfo($course)->get_cm($result->cmid);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a coursemodule from an activity name or idnumber.
|
||||
*
|
||||
* @param string $activity
|
||||
* @param string $identifier
|
||||
* @return cm_info
|
||||
*/
|
||||
protected function get_cm_by_activity_name(string $activity, string $identifier): cm_info {
|
||||
global $DB;
|
||||
|
||||
$coursetable = new \core\dml\table('course', 'c', 'c');
|
||||
$courseselect = $coursetable->get_field_select();
|
||||
$coursefrom = $coursetable->get_from_sql();
|
||||
|
||||
$cmtable = new \core\dml\table('course_modules', 'cm', 'cm');
|
||||
$cmfrom = $cmtable->get_from_sql();
|
||||
|
||||
$acttable = new \core\dml\table($activity, 'act', 'act');
|
||||
$actselect = $acttable->get_field_select();
|
||||
$actfrom = $acttable->get_from_sql();
|
||||
|
||||
$sql = <<<EOF
|
||||
SELECT cm.id as cmid, {$courseselect}, {$actselect}
|
||||
FROM {$cmfrom}
|
||||
INNER JOIN {$coursefrom} ON c.id = cm.course
|
||||
INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
INNER JOIN {$actfrom} ON cm.instance = act.id
|
||||
WHERE cm.idnumber = :idnumber OR act.name = :name
|
||||
EOF;
|
||||
|
||||
$result = $DB->get_record_sql($sql, [
|
||||
'modname' => $activity,
|
||||
'idnumber' => $identifier,
|
||||
'name' => $identifier,
|
||||
], MUST_EXIST);
|
||||
|
||||
$course = $coursetable->extract_from_result($result);
|
||||
$instancedata = $acttable->extract_from_result($result);
|
||||
|
||||
return get_fast_modinfo($course)->get_cm($result->cmid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,10 +60,15 @@ class curl_security_helper extends curl_security_helper_base {
|
||||
* could not be parsed, as well as those valid URLs which were found in the blocklist.
|
||||
*
|
||||
* @param string $urlstring the URL to check.
|
||||
* @param int $maxredirects Optional number of maximum redirects to follow - prevents infinite recursion.
|
||||
* @param int $notused There used to be an optional parameter $maxredirects for a short while here, not used any more.
|
||||
* @return bool true if the URL is blocked or invalid and false if the URL is not blocked.
|
||||
*/
|
||||
public function url_is_blocked($urlstring, $maxredirects = 3) {
|
||||
public function url_is_blocked($urlstring, $notused = null) {
|
||||
|
||||
if ($notused !== null) {
|
||||
debugging('The $maxredirects parameter of curl_security_helper::url_is_blocked() has been dropped!', DEBUG_DEVELOPER);
|
||||
}
|
||||
|
||||
// If no config data is present, then all hosts/ports are allowed.
|
||||
if (!$this->is_enabled()) {
|
||||
return false;
|
||||
@@ -86,30 +91,9 @@ class curl_security_helper extends curl_security_helper_base {
|
||||
}
|
||||
|
||||
if ($parsed['port'] && $parsed['host']) {
|
||||
// Check the host and port against the allow/block entries, and that we have not run out of redirects.
|
||||
if ($this->host_is_blocked($parsed['host']) || $this->port_is_blocked($parsed['port']) || $maxredirects < 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the host has a redirect in place, without following it.
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $urlstring);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
|
||||
|
||||
curl_exec($ch);
|
||||
$curlinfo = curl_getinfo($ch);
|
||||
$redirecturl = $curlinfo['redirect_url'];
|
||||
|
||||
if (!$redirecturl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Recursively check redirects, until final URL checked, redirects to a blocked host/port, or has too many redirects.
|
||||
$maxredirects--;
|
||||
return $this->url_is_blocked($redirecturl, $maxredirects);
|
||||
// Check the host and port against the allow/block entries.
|
||||
return $this->host_is_blocked($parsed['host']) || $this->port_is_blocked($parsed['port']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -136,6 +136,28 @@ abstract class task_base {
|
||||
* @return string
|
||||
*/
|
||||
public function get_component() {
|
||||
if (empty($this->component)) {
|
||||
// The component should be the root of the class namespace.
|
||||
$classname = get_class($this);
|
||||
$parts = explode('\\', $classname);
|
||||
|
||||
if (count($parts) === 1) {
|
||||
$component = substr($classname, 0, strpos($classname, '_task'));
|
||||
} else {
|
||||
[$component] = $parts;
|
||||
}
|
||||
|
||||
// Load component information from plugin manager.
|
||||
if ($component !== 'core' && strpos($component, 'core_') !== 0) {
|
||||
$plugininfo = \core_plugin_manager::instance()->get_plugin_info($component);
|
||||
if ($plugininfo && $plugininfo->component) {
|
||||
$this->set_component($plugininfo->component);
|
||||
} else {
|
||||
debugging("Component not set and the class namespace does not match a valid component ({$component}).");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->component;
|
||||
}
|
||||
|
||||
|
||||
@@ -2337,13 +2337,6 @@ function message_mark_messages_read() {
|
||||
'\core_message\api::mark_all_messages_as_read() instead.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Moodle 3.2
|
||||
*/
|
||||
function message_page_type_list() {
|
||||
throw new coding_exception('message_page_type_list() can not be used anymore.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Moodle 3.2
|
||||
*/
|
||||
|
||||
+4
-1
@@ -24,7 +24,9 @@ YUI.add('moodle-atto_emojipicker-button', function (Y, NAME) {
|
||||
/**
|
||||
* @module moodle-atto_emojipicker-button
|
||||
*/
|
||||
var COMPONENTNAME = 'atto_emojipicker';
|
||||
|
||||
var COMPONENTNAME = 'atto_emojipicker',
|
||||
EMOJI_PICKER_SEARCH_INPUT = '[data-region="search-input"]';
|
||||
|
||||
/**
|
||||
* Atto text editor emoji picker plugin.
|
||||
@@ -123,6 +125,7 @@ Y.namespace('M.atto_emojipicker').Button = Y.Base.create('button', Y.M.editor_at
|
||||
domNode.innerHTML = html;
|
||||
initialiseEmojiPicker(domNode, this._insertEmoji.bind(this));
|
||||
this.getDialogue().centerDialogue();
|
||||
domNode.querySelector(EMOJI_PICKER_SEARCH_INPUT).focus();
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
YUI.add("moodle-atto_emojipicker-button",function(e,t){e.namespace("M.atto_emojipicker").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,initializer:function(){this.get("disabled")||this.addButton({icon:"e/emoticons",callback:this._displayDialogue})},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection(),!1!==this._currentSelection&&this.getDialogue({headerContent:M.util.get_string("emojipicker","atto_emojipicker"),width:"auto",focusAfterHide:!0,additionalBaseClass:"emoji-picker-dialogue"},!0).set("bodyContent",this._getDialogueContent()).show()},_insertEmoji:function(e){var t=this.get("host");this.getDialogue({focusAfterHide:null}).hide(),t.setSelection(this._currentSelection),t.insertContentAtFocusPoint(e),this.markUpdated()},_getDialogueContent:function(){var o=e.Node.create("<div></div>");return require(["core/templates","core/emoji/picker"],function(e,i){e.render("core/emoji/picker",{}).then(function(e){var t=o.getDOMNode();t.innerHTML=e,i(t,this._insertEmoji.bind(this)),this.getDialogue().centerDialogue()}.bind(this))}.bind(this)),o}},{ATTRS:{disabled:{value:!0}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
|
||||
YUI.add("moodle-atto_emojipicker-button",function(e,t){e.namespace("M.atto_emojipicker").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,initializer:function(){this.get("disabled")||this.addButton({icon:"e/emoticons",callback:this._displayDialogue})},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection(),!1!==this._currentSelection&&this.getDialogue({headerContent:M.util.get_string("emojipicker","atto_emojipicker"),width:"auto",focusAfterHide:!0,additionalBaseClass:"emoji-picker-dialogue"},!0).set("bodyContent",this._getDialogueContent()).show()},_insertEmoji:function(e){var t=this.get("host");this.getDialogue({focusAfterHide:null}).hide(),t.setSelection(this._currentSelection),t.insertContentAtFocusPoint(e),this.markUpdated()},_getDialogueContent:function(){var o=e.Node.create("<div></div>");return require(["core/templates","core/emoji/picker"],function(e,i){e.render("core/emoji/picker",{}).then(function(e){var t=o.getDOMNode();t.innerHTML=e,i(t,this._insertEmoji.bind(this)),this.getDialogue().centerDialogue(),t.querySelector('[data-region="search-input"]').focus()}.bind(this))}.bind(this)),o}},{ATTRS:{disabled:{value:!0}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
|
||||
+4
-1
@@ -24,7 +24,9 @@ YUI.add('moodle-atto_emojipicker-button', function (Y, NAME) {
|
||||
/**
|
||||
* @module moodle-atto_emojipicker-button
|
||||
*/
|
||||
var COMPONENTNAME = 'atto_emojipicker';
|
||||
|
||||
var COMPONENTNAME = 'atto_emojipicker',
|
||||
EMOJI_PICKER_SEARCH_INPUT = '[data-region="search-input"]';
|
||||
|
||||
/**
|
||||
* Atto text editor emoji picker plugin.
|
||||
@@ -123,6 +125,7 @@ Y.namespace('M.atto_emojipicker').Button = Y.Base.create('button', Y.M.editor_at
|
||||
domNode.innerHTML = html;
|
||||
initialiseEmojiPicker(domNode, this._insertEmoji.bind(this));
|
||||
this.getDialogue().centerDialogue();
|
||||
domNode.querySelector(EMOJI_PICKER_SEARCH_INPUT).focus();
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
/**
|
||||
* @module moodle-atto_emojipicker-button
|
||||
*/
|
||||
var COMPONENTNAME = 'atto_emojipicker';
|
||||
|
||||
var COMPONENTNAME = 'atto_emojipicker',
|
||||
EMOJI_PICKER_SEARCH_INPUT = '[data-region="search-input"]';
|
||||
|
||||
/**
|
||||
* Atto text editor emoji picker plugin.
|
||||
@@ -121,6 +123,7 @@ Y.namespace('M.atto_emojipicker').Button = Y.Base.create('button', Y.M.editor_at
|
||||
domNode.innerHTML = html;
|
||||
initialiseEmojiPicker(domNode, this._insertEmoji.bind(this));
|
||||
this.getDialogue().centerDialogue();
|
||||
domNode.querySelector(EMOJI_PICKER_SEARCH_INPUT).focus();
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ function atto_equation_params_for_js($elementid, $options, $fpoptions) {
|
||||
'group1' => array(
|
||||
'groupname' => 'librarygroup1',
|
||||
'elements' => get_config('atto_equation', 'librarygroup1'),
|
||||
'active' => true,
|
||||
),
|
||||
'group2' => array(
|
||||
'groupname' => 'librarygroup2',
|
||||
|
||||
+6
-5
@@ -75,7 +75,10 @@ var COMPONENTNAME = 'atto_equation',
|
||||
'<ul class="root nav nav-tabs mb-1" role="tablist">' +
|
||||
'{{#each library}}' +
|
||||
'<li class="nav-item">' +
|
||||
'<a class="nav-link" href="#{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}" ' +
|
||||
'<a class="nav-link{{#active}} active{{/active}}" ' +
|
||||
'{{#active}}aria-selected="true"{{/active}}' +
|
||||
'{{^active}}aria-selected="false" tabindex="-1"{{/active}}' +
|
||||
' href="#{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}" ' +
|
||||
' data-target="#{{../elementidescaped}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}"' +
|
||||
' role="tab" data-toggle="tab">' +
|
||||
'{{get_string groupname ../component}}' +
|
||||
@@ -85,7 +88,7 @@ var COMPONENTNAME = 'atto_equation',
|
||||
'</ul>' +
|
||||
'<div class="tab-content mb-1 {{CSS.LIBRARY_GROUPS}}">' +
|
||||
'{{#each library}}' +
|
||||
'<div data-medium-type="{{CSS.LINK}}" class="tab-pane" ' +
|
||||
'<div data-medium-type="{{CSS.LINK}}" class="tab-pane{{#active}} active{{/active}}" ' +
|
||||
'id="{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}">' +
|
||||
'<div role="toolbar">' +
|
||||
'{{#split "\n" elements}}' +
|
||||
@@ -222,14 +225,12 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
|
||||
headerContent: M.util.get_string('pluginname', COMPONENTNAME),
|
||||
focusAfterHide: true,
|
||||
width: 600,
|
||||
focusOnShowSelector: SELECTORS.EQUATION_TEXT
|
||||
focusOnShowSelector: SELECTORS.LIBRARY_BUTTON
|
||||
});
|
||||
|
||||
var content = this._getDialogueContent();
|
||||
dialogue.set('bodyContent', content);
|
||||
|
||||
content.one('.nav-item:first-child .nav-link').getDOMNode().click();
|
||||
|
||||
dialogue.show();
|
||||
// Notify the filters about the modified nodes.
|
||||
require(['core/event'], function(event) {
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
+6
-5
@@ -75,7 +75,10 @@ var COMPONENTNAME = 'atto_equation',
|
||||
'<ul class="root nav nav-tabs mb-1" role="tablist">' +
|
||||
'{{#each library}}' +
|
||||
'<li class="nav-item">' +
|
||||
'<a class="nav-link" href="#{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}" ' +
|
||||
'<a class="nav-link{{#active}} active{{/active}}" ' +
|
||||
'{{#active}}aria-selected="true"{{/active}}' +
|
||||
'{{^active}}aria-selected="false" tabindex="-1"{{/active}}' +
|
||||
' href="#{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}" ' +
|
||||
' data-target="#{{../elementidescaped}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}"' +
|
||||
' role="tab" data-toggle="tab">' +
|
||||
'{{get_string groupname ../component}}' +
|
||||
@@ -85,7 +88,7 @@ var COMPONENTNAME = 'atto_equation',
|
||||
'</ul>' +
|
||||
'<div class="tab-content mb-1 {{CSS.LIBRARY_GROUPS}}">' +
|
||||
'{{#each library}}' +
|
||||
'<div data-medium-type="{{CSS.LINK}}" class="tab-pane" ' +
|
||||
'<div data-medium-type="{{CSS.LINK}}" class="tab-pane{{#active}} active{{/active}}" ' +
|
||||
'id="{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}">' +
|
||||
'<div role="toolbar">' +
|
||||
'{{#split "\n" elements}}' +
|
||||
@@ -222,14 +225,12 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
|
||||
headerContent: M.util.get_string('pluginname', COMPONENTNAME),
|
||||
focusAfterHide: true,
|
||||
width: 600,
|
||||
focusOnShowSelector: SELECTORS.EQUATION_TEXT
|
||||
focusOnShowSelector: SELECTORS.LIBRARY_BUTTON
|
||||
});
|
||||
|
||||
var content = this._getDialogueContent();
|
||||
dialogue.set('bodyContent', content);
|
||||
|
||||
content.one('.nav-item:first-child .nav-link').getDOMNode().click();
|
||||
|
||||
dialogue.show();
|
||||
// Notify the filters about the modified nodes.
|
||||
require(['core/event'], function(event) {
|
||||
|
||||
@@ -73,7 +73,10 @@ var COMPONENTNAME = 'atto_equation',
|
||||
'<ul class="root nav nav-tabs mb-1" role="tablist">' +
|
||||
'{{#each library}}' +
|
||||
'<li class="nav-item">' +
|
||||
'<a class="nav-link" href="#{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}" ' +
|
||||
'<a class="nav-link{{#active}} active{{/active}}" ' +
|
||||
'{{#active}}aria-selected="true"{{/active}}' +
|
||||
'{{^active}}aria-selected="false" tabindex="-1"{{/active}}' +
|
||||
' href="#{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}" ' +
|
||||
' data-target="#{{../elementidescaped}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}"' +
|
||||
' role="tab" data-toggle="tab">' +
|
||||
'{{get_string groupname ../component}}' +
|
||||
@@ -83,7 +86,7 @@ var COMPONENTNAME = 'atto_equation',
|
||||
'</ul>' +
|
||||
'<div class="tab-content mb-1 {{CSS.LIBRARY_GROUPS}}">' +
|
||||
'{{#each library}}' +
|
||||
'<div data-medium-type="{{CSS.LINK}}" class="tab-pane" ' +
|
||||
'<div data-medium-type="{{CSS.LINK}}" class="tab-pane{{#active}} active{{/active}}" ' +
|
||||
'id="{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}">' +
|
||||
'<div role="toolbar">' +
|
||||
'{{#split "\n" elements}}' +
|
||||
@@ -220,14 +223,12 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
|
||||
headerContent: M.util.get_string('pluginname', COMPONENTNAME),
|
||||
focusAfterHide: true,
|
||||
width: 600,
|
||||
focusOnShowSelector: SELECTORS.EQUATION_TEXT
|
||||
focusOnShowSelector: SELECTORS.LIBRARY_BUTTON
|
||||
});
|
||||
|
||||
var content = this._getDialogueContent();
|
||||
dialogue.set('bodyContent', content);
|
||||
|
||||
content.one('.nav-item:first-child .nav-link').getDOMNode().click();
|
||||
|
||||
dialogue.show();
|
||||
// Notify the filters about the modified nodes.
|
||||
require(['core/event'], function(event) {
|
||||
|
||||
@@ -21,11 +21,8 @@ Feature: Add h5ps to Atto
|
||||
|
||||
@javascript @external
|
||||
Scenario: Insert an embedded h5p
|
||||
Given I log in as "admin"
|
||||
And I change window size to "large"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
Given I change window size to "large"
|
||||
And I am on the PageName1 "page activity editing" page logged in as admin
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I set the field with xpath "//input[@data-region='h5pfile']" to "https://moodle.h5p.com/content/1290772960722742119"
|
||||
And I click on "Insert H5P" "button" in the "Insert H5P" "dialogue"
|
||||
@@ -42,9 +39,7 @@ Feature: Add h5ps to Atto
|
||||
And I follow "Manage private files..."
|
||||
And I upload "h5p/tests/fixtures/guess-the-answer.h5p" file to "Files" filemanager
|
||||
And I click on "Save changes" "button"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName1 "page activity editing" page
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I click on "Browse repositories..." "button" in the "Insert H5P" "dialogue"
|
||||
And I click on "Private files" "link" in the ".fp-repo-area" "css_element"
|
||||
@@ -57,11 +52,8 @@ Feature: Add h5ps to Atto
|
||||
|
||||
@javascript
|
||||
Scenario: Test an invalid url
|
||||
Given I log in as "admin"
|
||||
And I change window size to "large"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
Given I change window size to "large"
|
||||
And I am on the PageName1 "page activity editing" page logged in as admin
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
# This is not a real external URL, so this scenario shouldn't be labeled as external.
|
||||
And I set the field with xpath "//input[@data-region='h5pfile']" to "ftp://moodle.h5p.com/content/1290772960722742119"
|
||||
@@ -75,10 +67,7 @@ Feature: Add h5ps to Atto
|
||||
| capability | permission | role | contextlevel | reference |
|
||||
| atto/h5p:addembed | Prohibit | editingteacher | Course | C1 |
|
||||
| moodle/h5p:deploy | Prohibit | editingteacher | Course | C1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
When I navigate to "Edit settings" in current page administration
|
||||
When I am on the PageName1 "page activity editing" page logged in as teacher1
|
||||
Then "Insert H5P" "button" should not exist
|
||||
|
||||
@javascript
|
||||
@@ -86,10 +75,7 @@ Feature: Add h5ps to Atto
|
||||
Given the following "permission overrides" exist:
|
||||
| capability | permission | role | contextlevel | reference |
|
||||
| atto/h5p:addembed | Prohibit | editingteacher | Course | C1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
When I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName1 "page activity editing" page logged in as teacher1
|
||||
And I click on "Insert H5P" "button"
|
||||
Then I should not see "H5P URL" in the "Insert H5P" "dialogue"
|
||||
And I should see "H5P file upload" in the "Insert H5P" "dialogue"
|
||||
@@ -100,10 +86,7 @@ Feature: Add h5ps to Atto
|
||||
Given the following "permission overrides" exist:
|
||||
| capability | permission | role | contextlevel | reference |
|
||||
| moodle/h5p:deploy | Prohibit | editingteacher | Course | C1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
When I navigate to "Edit settings" in current page administration
|
||||
When I am on the PageName1 "page activity editing" page logged in as teacher1
|
||||
And I click on "Insert H5P" "button"
|
||||
Then I should not see "H5P file upload" in the "Insert H5P" "dialogue"
|
||||
And I should see "H5P URL" in the "Insert H5P" "dialogue"
|
||||
@@ -115,9 +98,7 @@ Feature: Add h5ps to Atto
|
||||
And I follow "Manage private files..."
|
||||
And I upload "lib/editor/atto/tests/fixtures/drag.h5p" file to "Files" filemanager
|
||||
And I click on "Save changes" "button"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName1 "page activity editing" page
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
# H5P file content
|
||||
And I click on "Browse repositories..." "button" in the "Insert H5P" "dialogue"
|
||||
@@ -151,9 +132,7 @@ Feature: Add h5ps to Atto
|
||||
And I follow "Manage private files..."
|
||||
And I upload "h5p/tests/fixtures/guess-the-answer.h5p" file to "Files" filemanager
|
||||
And I click on "Save changes" "button"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName1 "page activity editing" page
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I click on "Browse repositories..." "button" in the "Insert H5P" "dialogue"
|
||||
And I click on "Private files" "link" in the ".fp-repo-area" "css_element"
|
||||
@@ -205,22 +184,17 @@ Feature: Add h5ps to Atto
|
||||
|
||||
@javascript @external
|
||||
Scenario: H5P options are ignored for H5P URLs
|
||||
Given I log in as "admin"
|
||||
And I change window size to "large"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
Given I change window size to "large"
|
||||
And I am on the PageName1 "page activity editing" page logged in as admin
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I set the field with xpath "//input[@data-region='h5pfile']" to "https://moodle.h5p.com/content/1290752078589054689"
|
||||
And I set the field with xpath "//input[@data-region='h5pfile']" to "https://moodle.h5p.com/content/1291366510035871129"
|
||||
And I click on "H5P options" "link"
|
||||
And I click on "Embed button" "checkbox"
|
||||
And I click on "Insert H5P" "button" in the "Insert H5P" "dialogue"
|
||||
And I wait until the page is ready
|
||||
When I click on "Save and display" "button"
|
||||
Then ".h5p-placeholder" "css_element" should exist
|
||||
And I wait until the page is ready
|
||||
And I switch to "h5pcontent" iframe
|
||||
And I should see "History of strawberries"
|
||||
And I should see "Far far away"
|
||||
And I should not see "Embed"
|
||||
And I switch to the main frame
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
@@ -241,9 +215,7 @@ Feature: Add h5ps to Atto
|
||||
And I follow "Manage private files..."
|
||||
And I upload "h5p/tests/fixtures/guess-the-answer.h5p" file to "Files" filemanager
|
||||
And I click on "Save changes" "button"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the PageName1 "page activity editing" page
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I click on "Browse repositories..." "button" in the "Insert H5P" "dialogue"
|
||||
And I click on "Private files" "link" in the ".fp-repo-area" "css_element"
|
||||
@@ -253,9 +225,7 @@ Feature: Add h5ps to Atto
|
||||
And I wait until the page is ready
|
||||
And I click on "Save and display" "button"
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
When I am on the PageName1 "page activity" page logged in as student1
|
||||
Then I switch to "h5pcontent" iframe
|
||||
And I switch to "h5p-iframe" class iframe
|
||||
And I should see "reveal"
|
||||
|
||||
+19
-1
@@ -33,7 +33,9 @@ YUI.add('moodle-atto_managefiles-button', function (Y, NAME) {
|
||||
* @extends M.editor_atto.EditorPlugin
|
||||
*/
|
||||
|
||||
var LOGNAME = 'atto_managefiles';
|
||||
var LOGNAME = 'atto_managefiles',
|
||||
CAN_RECEIVE_FOCUS_SELECTOR = '.fp-navbar a:not([disabled])',
|
||||
FILE_MANAGER_SELECTOR = '#fitem_id_files_filemanager';
|
||||
|
||||
Y.namespace('M.atto_managefiles').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
|
||||
|
||||
@@ -96,6 +98,22 @@ Y.namespace('M.atto_managefiles').Button = Y.Base.create('button', Y.M.editor_at
|
||||
});
|
||||
iframe.setAttribute('src', this._getIframeURL());
|
||||
|
||||
// Focus on the first focusable element of the file manager after it is fully loaded.
|
||||
iframe.on('load', function(e, frame) {
|
||||
var fileManager = frame.getDOMNode().contentDocument.querySelector(FILE_MANAGER_SELECTOR);
|
||||
// The file manager component is loaded asynchronously after the page is loaded.
|
||||
// We check for the presence of .fm-loaded every 200 ms to determine if the file manager is loaded yet.
|
||||
var intervalId = setInterval(function() {
|
||||
if (fileManager.querySelector('.fm-loaded')) {
|
||||
var firstFocusableElement = fileManager.querySelector(CAN_RECEIVE_FOCUS_SELECTOR);
|
||||
if (firstFocusableElement) {
|
||||
firstFocusableElement.focus();
|
||||
}
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
}, 200);
|
||||
}, this, iframe);
|
||||
|
||||
dialogue.set('bodyContent', iframe)
|
||||
.show();
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
YUI.add("moodle-atto_managefiles-button",function(a,e){a.namespace("M.atto_managefiles").Button=a.Base.create("button",a.M.editor_atto.EditorPlugin,[],{_currentSelection:null,initializer:function(){if(!this.get("disabled")){var e=this.get("host"),t=this.get("area"),i=e.get("filepickeroptions");i.image&&i.image.itemid&&(t.itemid=i.image.itemid,this.set("area",t),this.addButton({icon:"e/manage_files",callback:this._displayDialogue}))}},_displayDialogue:function(e){var t,i;e.preventDefault(),t=this.getDialogue({headerContent:M.util.get_string("managefiles","atto_managefiles"),width:"800px",focusAfterHide:!0}),(i=a.Node.create("<iframe></iframe>")).setStyles({height:"700px",border:"none",width:"100%"}),i.setAttribute("src",this._getIframeURL()),t.set("bodyContent",i).show(),this.markUpdated()},_getIframeURL:function(){var e=a.mix({elementid:this.get("host").get("elementid")},this.get("area"));return M.cfg.wwwroot+"/lib/editor/atto/plugins/managefiles/manage.php?"+a.QueryString.stringify(e)}},{ATTRS:{disabled:{value:!0},area:{value:{}}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
|
||||
YUI.add("moodle-atto_managefiles-button",function(a,e){a.namespace("M.atto_managefiles").Button=a.Base.create("button",a.M.editor_atto.EditorPlugin,[],{_currentSelection:null,initializer:function(){if(!this.get("disabled")){var e=this.get("host"),t=this.get("area"),i=e.get("filepickeroptions");i.image&&i.image.itemid&&(t.itemid=i.image.itemid,this.set("area",t),this.addButton({icon:"e/manage_files",callback:this._displayDialogue}))}},_displayDialogue:function(e){var t,i;e.preventDefault(),t=this.getDialogue({headerContent:M.util.get_string("managefiles","atto_managefiles"),width:"800px",focusAfterHide:!0}),(i=a.Node.create("<iframe></iframe>")).setStyles({height:"700px",border:"none",width:"100%"}),i.setAttribute("src",this._getIframeURL()),i.on("load",function(e,t){var i=t.getDOMNode().contentDocument.querySelector("#fitem_id_files_filemanager"),a=setInterval(function(){if(i.querySelector(".fm-loaded")){var e=i.querySelector(".fp-navbar a:not([disabled])");e&&e.focus(),clearInterval(a)}},200)},this,i),t.set("bodyContent",i).show(),this.markUpdated()},_getIframeURL:function(){var e=a.mix({elementid:this.get("host").get("elementid")},this.get("area"));return M.cfg.wwwroot+"/lib/editor/atto/plugins/managefiles/manage.php?"+a.QueryString.stringify(e)}},{ATTRS:{disabled:{value:!0},area:{value:{}}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
|
||||
+19
-1
@@ -33,7 +33,9 @@ YUI.add('moodle-atto_managefiles-button', function (Y, NAME) {
|
||||
* @extends M.editor_atto.EditorPlugin
|
||||
*/
|
||||
|
||||
var LOGNAME = 'atto_managefiles';
|
||||
var LOGNAME = 'atto_managefiles',
|
||||
CAN_RECEIVE_FOCUS_SELECTOR = '.fp-navbar a:not([disabled])',
|
||||
FILE_MANAGER_SELECTOR = '#fitem_id_files_filemanager';
|
||||
|
||||
Y.namespace('M.atto_managefiles').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
|
||||
|
||||
@@ -94,6 +96,22 @@ Y.namespace('M.atto_managefiles').Button = Y.Base.create('button', Y.M.editor_at
|
||||
});
|
||||
iframe.setAttribute('src', this._getIframeURL());
|
||||
|
||||
// Focus on the first focusable element of the file manager after it is fully loaded.
|
||||
iframe.on('load', function(e, frame) {
|
||||
var fileManager = frame.getDOMNode().contentDocument.querySelector(FILE_MANAGER_SELECTOR);
|
||||
// The file manager component is loaded asynchronously after the page is loaded.
|
||||
// We check for the presence of .fm-loaded every 200 ms to determine if the file manager is loaded yet.
|
||||
var intervalId = setInterval(function() {
|
||||
if (fileManager.querySelector('.fm-loaded')) {
|
||||
var firstFocusableElement = fileManager.querySelector(CAN_RECEIVE_FOCUS_SELECTOR);
|
||||
if (firstFocusableElement) {
|
||||
firstFocusableElement.focus();
|
||||
}
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
}, 200);
|
||||
}, this, iframe);
|
||||
|
||||
dialogue.set('bodyContent', iframe)
|
||||
.show();
|
||||
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
* @extends M.editor_atto.EditorPlugin
|
||||
*/
|
||||
|
||||
var LOGNAME = 'atto_managefiles';
|
||||
var LOGNAME = 'atto_managefiles',
|
||||
CAN_RECEIVE_FOCUS_SELECTOR = '.fp-navbar a:not([disabled])',
|
||||
FILE_MANAGER_SELECTOR = '#fitem_id_files_filemanager';
|
||||
|
||||
Y.namespace('M.atto_managefiles').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
|
||||
|
||||
@@ -94,6 +96,22 @@ Y.namespace('M.atto_managefiles').Button = Y.Base.create('button', Y.M.editor_at
|
||||
});
|
||||
iframe.setAttribute('src', this._getIframeURL());
|
||||
|
||||
// Focus on the first focusable element of the file manager after it is fully loaded.
|
||||
iframe.on('load', function(e, frame) {
|
||||
var fileManager = frame.getDOMNode().contentDocument.querySelector(FILE_MANAGER_SELECTOR);
|
||||
// The file manager component is loaded asynchronously after the page is loaded.
|
||||
// We check for the presence of .fm-loaded every 200 ms to determine if the file manager is loaded yet.
|
||||
var intervalId = setInterval(function() {
|
||||
if (fileManager.querySelector('.fm-loaded')) {
|
||||
var firstFocusableElement = fileManager.querySelector(CAN_RECEIVE_FOCUS_SELECTOR);
|
||||
if (firstFocusableElement) {
|
||||
firstFocusableElement.focus();
|
||||
}
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
}, 200);
|
||||
}, this, iframe);
|
||||
|
||||
dialogue.set('bodyContent', iframe)
|
||||
.show();
|
||||
|
||||
|
||||
+12
-4
@@ -184,7 +184,9 @@ Y.namespace('M.atto_recordrtc').Button = Y.Base.create('button', Y.M.editor_atto
|
||||
* @private
|
||||
*/
|
||||
_audio: function() {
|
||||
var dialogue = this.getDialogue();
|
||||
var dialogue = this.getDialogue({
|
||||
focusAfterHide: 'audio'
|
||||
});
|
||||
|
||||
dialogue.set('headerContent', M.util.get_string('audiortc', 'atto_recordrtc'));
|
||||
dialogue.set('bodyContent', this._createContent('audio'));
|
||||
@@ -201,7 +203,9 @@ Y.namespace('M.atto_recordrtc').Button = Y.Base.create('button', Y.M.editor_atto
|
||||
* @private
|
||||
*/
|
||||
_video: function() {
|
||||
var dialogue = this.getDialogue();
|
||||
var dialogue = this.getDialogue({
|
||||
focusAfterHide: 'video'
|
||||
});
|
||||
|
||||
dialogue.set('headerContent', M.util.get_string('videortc', 'atto_recordrtc'));
|
||||
dialogue.set('bodyContent', this._createContent('video'));
|
||||
@@ -249,7 +253,9 @@ Y.namespace('M.atto_recordrtc').Button = Y.Base.create('button', Y.M.editor_atto
|
||||
* @param {Object} scope The "this" context of the editor.
|
||||
*/
|
||||
closeDialogue: function(scope) {
|
||||
scope.getDialogue().hide();
|
||||
scope.getDialogue().hide({
|
||||
focusAfterHide: null
|
||||
});
|
||||
|
||||
scope.editor.focus();
|
||||
},
|
||||
@@ -262,7 +268,9 @@ Y.namespace('M.atto_recordrtc').Button = Y.Base.create('button', Y.M.editor_atto
|
||||
* @param {string} annotation The HTML link to the recording.
|
||||
*/
|
||||
setLink: function(scope, annotation) {
|
||||
scope.getDialogue().hide();
|
||||
scope.getDialogue().hide({
|
||||
focusAfterHide: null
|
||||
});
|
||||
|
||||
scope.editor.focus();
|
||||
scope.get('host').insertContentAtFocusPoint(annotation);
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
YUI.add("moodle-atto_recordrtc-button",function(s,t){var n="atto_recordrtc";s.namespace("M.atto_recordrtc").Button=s.Base.create("button",s.M.editor_atto.EditorPlugin,[],{_lang:"en",initializer:function(){var t,e,i;if(this.get("host").canShowFilepicker("media")){if(e=!1,"both"!==(t=this.get("allowedtypes"))&&"audio"!==t||(this._addButton("audio",this._audio),e=!0),"both"!==t&&"video"!==t||(this._addButton("video",this._video),e=!0),!e)return;(i=this.getDialogue({width:1e3,focusAfterHide:null})).after("visibleChange",function(){var t=!i.get("visible"),e=M.atto_recordrtc.commonmodule;t&&(window.clearInterval(e.countdownTicker),e.mediaRecorder&&"inactive"!==e.mediaRecorder.state&&e.mediaRecorder.stop(),e.stream&&e.stream.getTracks().forEach(function(t){"ended"!==t.readyState&&t.stop()}),this.getDialogue().set("bodyContent",""))},this),i.on("click",function(){this.centered()}),window.require(["core/adapter"],function(t){window.adapter=t})}},_addButton:function(t,e){this.addButton({buttonName:t,icon:this.get(t+"rtcicon"),iconComponent:n,callback:e,title:t+"rtc",tags:t+"rtc",tagMatchRequiresAll:!1})},_audio:function(){var t=this.getDialogue();t.set("headerContent",M.util.get_string("audiortc","atto_recordrtc")),t.set("bodyContent",this._createContent("audio")),t.show(),M.atto_recordrtc.audiomodule.init(this)},_video:function(){var t=this.getDialogue();t.set("headerContent",M.util.get_string("videortc","atto_recordrtc")),t.set("bodyContent",this._createContent("video")),t.show(),M.atto_recordrtc.videomodule.init(this)},_createContent:function(t){var e="audio"===t,i="row",o="col-",r="alert-danger",a="btn btn-lg btn-outline-danger btn-block",d=s.Handlebars.compile('<div class="{{PLUGINNAME}} container-fluid"><div class="{{bs_row}} hide"><div class="{{bs_col}}12"><div id="alert-danger" class="alert {{bs_al_dang}}"><strong>{{insecurealert_title}}</strong> {{insecurealert}}</div></div></div><div class="{{bs_row}} hide">{{#if isAudio}}<div class="{{bs_col}}1"></div><div class="{{bs_col}}10"><audio id="player"></audio></div><div class="{{bs_col}}1"></div>{{else}}<div class="{{bs_col}}12"><video id="player"></video></div>{{/if}}</div><div class="{{bs_row}}"><div class="{{bs_col}}1"></div><div class="{{bs_col}}10"><button id="start-stop" class="{{bs_ss_btn}}">{{startrecording}}</button></div><div class="{{bs_col}}1"></div></div><div class="{{bs_row}} hide"><div class="{{bs_col}}3"></div><div class="{{bs_col}}6"><button id="upload" class="btn btn-primary btn-block">{{attachrecording}}</button></div><div class="{{bs_col}}3"></div></div></div>')({PLUGINNAME:n,isAudio:e,bs_row:i,bs_col:o,bs_al_dang:r,bs_ss_btn:a,insecurealert_title:M.util.get_string("insecurealert_title","atto_recordrtc"),insecurealert:M.util.get_string("insecurealert","atto_recordrtc"),startrecording:M.util.get_string("startrecording","atto_recordrtc"),attachrecording:M.util.get_string("attachrecording","atto_recordrtc")});return d},closeDialogue:function(t){t.getDialogue().hide(),t.editor.focus()},setLink:function(t,e){t.getDialogue().hide(),t.editor.focus(),t.get("host").insertContentAtFocusPoint(e),t.markUpdated()}},{ATTRS:{contextid:{value:null},sesskey:{value:null},allowedtypes:{value:null},audiobitrate:{value:null},videobitrate:{value:null},timelimit:{value:null},audiortcicon:{value:null},videortcicon:{value:null},maxrecsize:{value:null}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","moodle-atto_recordrtc-recording"]});
|
||||
YUI.add("moodle-atto_recordrtc-button",function(s,t){var n="atto_recordrtc";s.namespace("M.atto_recordrtc").Button=s.Base.create("button",s.M.editor_atto.EditorPlugin,[],{_lang:"en",initializer:function(){var t,e,i;if(this.get("host").canShowFilepicker("media")){if(e=!1,"both"!==(t=this.get("allowedtypes"))&&"audio"!==t||(this._addButton("audio",this._audio),e=!0),"both"!==t&&"video"!==t||(this._addButton("video",this._video),e=!0),!e)return;(i=this.getDialogue({width:1e3,focusAfterHide:null})).after("visibleChange",function(){var t=!i.get("visible"),e=M.atto_recordrtc.commonmodule;t&&(window.clearInterval(e.countdownTicker),e.mediaRecorder&&"inactive"!==e.mediaRecorder.state&&e.mediaRecorder.stop(),e.stream&&e.stream.getTracks().forEach(function(t){"ended"!==t.readyState&&t.stop()}),this.getDialogue().set("bodyContent",""))},this),i.on("click",function(){this.centered()}),window.require(["core/adapter"],function(t){window.adapter=t})}},_addButton:function(t,e){this.addButton({buttonName:t,icon:this.get(t+"rtcicon"),iconComponent:n,callback:e,title:t+"rtc",tags:t+"rtc",tagMatchRequiresAll:!1})},_audio:function(){var t=this.getDialogue({focusAfterHide:"audio"});t.set("headerContent",M.util.get_string("audiortc","atto_recordrtc")),t.set("bodyContent",this._createContent("audio")),t.show(),M.atto_recordrtc.audiomodule.init(this)},_video:function(){var t=this.getDialogue({focusAfterHide:"video"});t.set("headerContent",M.util.get_string("videortc","atto_recordrtc")),t.set("bodyContent",this._createContent("video")),t.show(),M.atto_recordrtc.videomodule.init(this)},_createContent:function(t){var e="audio"===t,i="row",o="col-",r="alert-danger",d="btn btn-lg btn-outline-danger btn-block",a=s.Handlebars.compile('<div class="{{PLUGINNAME}} container-fluid"><div class="{{bs_row}} hide"><div class="{{bs_col}}12"><div id="alert-danger" class="alert {{bs_al_dang}}"><strong>{{insecurealert_title}}</strong> {{insecurealert}}</div></div></div><div class="{{bs_row}} hide">{{#if isAudio}}<div class="{{bs_col}}1"></div><div class="{{bs_col}}10"><audio id="player"></audio></div><div class="{{bs_col}}1"></div>{{else}}<div class="{{bs_col}}12"><video id="player"></video></div>{{/if}}</div><div class="{{bs_row}}"><div class="{{bs_col}}1"></div><div class="{{bs_col}}10"><button id="start-stop" class="{{bs_ss_btn}}">{{startrecording}}</button></div><div class="{{bs_col}}1"></div></div><div class="{{bs_row}} hide"><div class="{{bs_col}}3"></div><div class="{{bs_col}}6"><button id="upload" class="btn btn-primary btn-block">{{attachrecording}}</button></div><div class="{{bs_col}}3"></div></div></div>')({PLUGINNAME:n,isAudio:e,bs_row:i,bs_col:o,bs_al_dang:r,bs_ss_btn:d,insecurealert_title:M.util.get_string("insecurealert_title","atto_recordrtc"),insecurealert:M.util.get_string("insecurealert","atto_recordrtc"),startrecording:M.util.get_string("startrecording","atto_recordrtc"),attachrecording:M.util.get_string("attachrecording","atto_recordrtc")});return a},closeDialogue:function(t){t.getDialogue().hide({focusAfterHide:null}),t.editor.focus()},setLink:function(t,e){t.getDialogue().hide({focusAfterHide:null}),t.editor.focus(),t.get("host").insertContentAtFocusPoint(e),t.markUpdated()}},{ATTRS:{contextid:{value:null},sesskey:{value:null},allowedtypes:{value:null},audiobitrate:{value:null},videobitrate:{value:null},timelimit:{value:null},audiortcicon:{value:null},videortcicon:{value:null},maxrecsize:{value:null}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","moodle-atto_recordrtc-recording"]});
|
||||
+12
-4
@@ -184,7 +184,9 @@ Y.namespace('M.atto_recordrtc').Button = Y.Base.create('button', Y.M.editor_atto
|
||||
* @private
|
||||
*/
|
||||
_audio: function() {
|
||||
var dialogue = this.getDialogue();
|
||||
var dialogue = this.getDialogue({
|
||||
focusAfterHide: 'audio'
|
||||
});
|
||||
|
||||
dialogue.set('headerContent', M.util.get_string('audiortc', 'atto_recordrtc'));
|
||||
dialogue.set('bodyContent', this._createContent('audio'));
|
||||
@@ -201,7 +203,9 @@ Y.namespace('M.atto_recordrtc').Button = Y.Base.create('button', Y.M.editor_atto
|
||||
* @private
|
||||
*/
|
||||
_video: function() {
|
||||
var dialogue = this.getDialogue();
|
||||
var dialogue = this.getDialogue({
|
||||
focusAfterHide: 'video'
|
||||
});
|
||||
|
||||
dialogue.set('headerContent', M.util.get_string('videortc', 'atto_recordrtc'));
|
||||
dialogue.set('bodyContent', this._createContent('video'));
|
||||
@@ -249,7 +253,9 @@ Y.namespace('M.atto_recordrtc').Button = Y.Base.create('button', Y.M.editor_atto
|
||||
* @param {Object} scope The "this" context of the editor.
|
||||
*/
|
||||
closeDialogue: function(scope) {
|
||||
scope.getDialogue().hide();
|
||||
scope.getDialogue().hide({
|
||||
focusAfterHide: null
|
||||
});
|
||||
|
||||
scope.editor.focus();
|
||||
},
|
||||
@@ -262,7 +268,9 @@ Y.namespace('M.atto_recordrtc').Button = Y.Base.create('button', Y.M.editor_atto
|
||||
* @param {string} annotation The HTML link to the recording.
|
||||
*/
|
||||
setLink: function(scope, annotation) {
|
||||
scope.getDialogue().hide();
|
||||
scope.getDialogue().hide({
|
||||
focusAfterHide: null
|
||||
});
|
||||
|
||||
scope.editor.focus();
|
||||
scope.get('host').insertContentAtFocusPoint(annotation);
|
||||
|
||||
@@ -182,7 +182,9 @@ Y.namespace('M.atto_recordrtc').Button = Y.Base.create('button', Y.M.editor_atto
|
||||
* @private
|
||||
*/
|
||||
_audio: function() {
|
||||
var dialogue = this.getDialogue();
|
||||
var dialogue = this.getDialogue({
|
||||
focusAfterHide: 'audio'
|
||||
});
|
||||
|
||||
dialogue.set('headerContent', M.util.get_string('audiortc', 'atto_recordrtc'));
|
||||
dialogue.set('bodyContent', this._createContent('audio'));
|
||||
@@ -199,7 +201,9 @@ Y.namespace('M.atto_recordrtc').Button = Y.Base.create('button', Y.M.editor_atto
|
||||
* @private
|
||||
*/
|
||||
_video: function() {
|
||||
var dialogue = this.getDialogue();
|
||||
var dialogue = this.getDialogue({
|
||||
focusAfterHide: 'video'
|
||||
});
|
||||
|
||||
dialogue.set('headerContent', M.util.get_string('videortc', 'atto_recordrtc'));
|
||||
dialogue.set('bodyContent', this._createContent('video'));
|
||||
@@ -247,7 +251,9 @@ Y.namespace('M.atto_recordrtc').Button = Y.Base.create('button', Y.M.editor_atto
|
||||
* @param {Object} scope The "this" context of the editor.
|
||||
*/
|
||||
closeDialogue: function(scope) {
|
||||
scope.getDialogue().hide();
|
||||
scope.getDialogue().hide({
|
||||
focusAfterHide: null
|
||||
});
|
||||
|
||||
scope.editor.focus();
|
||||
},
|
||||
@@ -260,7 +266,9 @@ Y.namespace('M.atto_recordrtc').Button = Y.Base.create('button', Y.M.editor_atto
|
||||
* @param {string} annotation The HTML link to the recording.
|
||||
*/
|
||||
setLink: function(scope, annotation) {
|
||||
scope.getDialogue().hide();
|
||||
scope.getDialogue().hide({
|
||||
focusAfterHide: null
|
||||
});
|
||||
|
||||
scope.editor.focus();
|
||||
scope.get('host').insertContentAtFocusPoint(annotation);
|
||||
|
||||
+33
-15
@@ -3079,7 +3079,7 @@ class curl {
|
||||
public $error;
|
||||
/** @var int error code */
|
||||
public $errno;
|
||||
/** @var bool use workaround for open_basedir restrictions, to be changed from unit tests only! */
|
||||
/** @var bool Perform redirects at PHP level instead of relying on native cURL functionality. Always true now. */
|
||||
public $emulateredirects = null;
|
||||
|
||||
/** @var array cURL options */
|
||||
@@ -3176,9 +3176,13 @@ class curl {
|
||||
$this->proxy = false;
|
||||
}
|
||||
|
||||
if (!isset($this->emulateredirects)) {
|
||||
$this->emulateredirects = ini_get('open_basedir');
|
||||
}
|
||||
// All redirects are performed at PHP level now and each one is checked against blocked URLs rules. We do not
|
||||
// want to let cURL naively follow the redirect chain and visit every URL for security reasons. Even when the
|
||||
// caller explicitly wants to ignore the security checks, we would need to fall back to the original
|
||||
// implementation and use emulated redirects if open_basedir is in effect to avoid the PHP warning
|
||||
// "CURLOPT_FOLLOWLOCATION cannot be activated when in safe_mode or an open_basedir". So it is better to simply
|
||||
// ignore this property and always handle redirects at this PHP wrapper level and not inside the native cURL.
|
||||
$this->emulateredirects = true;
|
||||
|
||||
// Curl security setup. Allow injection of a security helper, but if not found, default to the core helper.
|
||||
if (isset($settings['securityhelper']) && $settings['securityhelper'] instanceof \core\files\curl_security_helper_base) {
|
||||
@@ -3487,8 +3491,8 @@ class curl {
|
||||
|
||||
// Set options.
|
||||
foreach($this->options as $name => $val) {
|
||||
if ($name === 'CURLOPT_FOLLOWLOCATION' and $this->emulateredirects) {
|
||||
// The redirects are emulated elsewhere.
|
||||
if ($name === 'CURLOPT_FOLLOWLOCATION') {
|
||||
// All the redirects are emulated at PHP level.
|
||||
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 0);
|
||||
continue;
|
||||
}
|
||||
@@ -3705,7 +3709,11 @@ class curl {
|
||||
}
|
||||
}
|
||||
|
||||
// This will only check the base url. In the case of redirects, the blocking check is also after the curl_exec.
|
||||
if (empty($this->emulateredirects)) {
|
||||
// Just in case someone had tried to explicitly disable emulated redirects in legacy code.
|
||||
debugging('Attempting to disable emulated redirects has no effect any more!', DEBUG_DEVELOPER);
|
||||
}
|
||||
|
||||
$urlisblocked = $this->check_securityhelper_blocklist($url);
|
||||
if (!is_null($urlisblocked)) {
|
||||
return $urlisblocked;
|
||||
@@ -3728,17 +3736,14 @@ class curl {
|
||||
$this->errno = curl_errno($curl);
|
||||
// Note: $this->response and $this->rawresponse are filled by $hits->formatHeader callback.
|
||||
|
||||
// In the case of redirects (which curl blindly follows), check the post-redirect URL against the list of blocked list too.
|
||||
if (intval($this->info['redirect_count']) > 0) {
|
||||
$urlisblocked = $this->check_securityhelper_blocklist($this->info['url']);
|
||||
if (!is_null($urlisblocked)) {
|
||||
$this->reset_request_state_vars();
|
||||
curl_close($curl);
|
||||
return $urlisblocked;
|
||||
}
|
||||
// For security reasons we do not allow the cURL handle to follow redirects on its own.
|
||||
// See setting CURLOPT_FOLLOWLOCATION in {@see self::apply_opt()} method.
|
||||
throw new coding_exception('Internal cURL handle should never follow redirects on its own!',
|
||||
'Reported number of redirects: ' . $this->info['redirect_count']);
|
||||
}
|
||||
|
||||
if ($this->emulateredirects and $this->options['CURLOPT_FOLLOWLOCATION'] and $this->info['http_code'] != 200) {
|
||||
if ($this->options['CURLOPT_FOLLOWLOCATION'] && $this->info['http_code'] != 200) {
|
||||
$redirects = 0;
|
||||
|
||||
while($redirects <= $this->options['CURLOPT_MAXREDIRS']) {
|
||||
@@ -3772,6 +3777,12 @@ class curl {
|
||||
if (isset($this->info['redirect_url'])) {
|
||||
if (preg_match('|^https?://|i', $this->info['redirect_url'])) {
|
||||
$redirecturl = $this->info['redirect_url'];
|
||||
} else {
|
||||
// Emulate CURLOPT_REDIR_PROTOCOLS behaviour which we have set to (CURLPROTO_HTTP | CURLPROTO_HTTPS) only.
|
||||
$this->errno = CURLE_UNSUPPORTED_PROTOCOL;
|
||||
$this->error = 'Redirect to a URL with unsuported protocol: ' . $this->info['redirect_url'];
|
||||
curl_close($curl);
|
||||
return $this->error;
|
||||
}
|
||||
}
|
||||
if (!$redirecturl) {
|
||||
@@ -3801,6 +3812,13 @@ class curl {
|
||||
}
|
||||
}
|
||||
|
||||
$urlisblocked = $this->check_securityhelper_blocklist($redirecturl);
|
||||
if (!is_null($urlisblocked)) {
|
||||
$this->reset_request_state_vars();
|
||||
curl_close($curl);
|
||||
return $urlisblocked;
|
||||
}
|
||||
|
||||
curl_setopt($curl, CURLOPT_URL, $redirecturl);
|
||||
$ret = curl_exec($curl);
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class behat_core_form extends behat_question_base {
|
||||
* @throws Exception with a meaningful error message if the specified page cannot be found.
|
||||
*/
|
||||
protected function resolve_page_url(string $page): moodle_url {
|
||||
switch ($page) {
|
||||
switch (strtolower($page)) {
|
||||
default:
|
||||
throw new Exception('Unrecognised core_form page type "' . $page . '."');
|
||||
}
|
||||
@@ -70,8 +70,8 @@ class behat_core_form extends behat_question_base {
|
||||
* @throws Exception with a meaningful error message if the specified page cannot be found.
|
||||
*/
|
||||
protected function resolve_page_instance_url(string $type, string $identifier): moodle_url {
|
||||
switch ($type) {
|
||||
case 'Fixture':
|
||||
switch (strtolower($type)) {
|
||||
case 'fixture':
|
||||
return new moodle_url('/lib/form/tests/fixtures/' .
|
||||
clean_param($identifier, PARAM_ALPHAEXT) . '.php');
|
||||
|
||||
|
||||
@@ -22,16 +22,13 @@ Feature: Using the database activities which support point scale
|
||||
|
||||
@javascript
|
||||
Scenario: Database rescale grade should not be possible when users are graded
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
Given I am on the "Course 1" course page logged in as teacher1
|
||||
And I add a "Text input" field to "Test database name" database and I fill the form with:
|
||||
| Field name | Test field name |
|
||||
| Field description | Test field description |
|
||||
And I follow "Templates"
|
||||
And I wait until the page is ready
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test database name"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test database name" "data activity editing" page
|
||||
And I expand all fieldsets
|
||||
And I set the field "Ratings > Aggregate type" to "Count of ratings"
|
||||
And I set the field "Ratings > Type" to "Point"
|
||||
@@ -44,13 +41,9 @@ Feature: Using the database activities which support point scale
|
||||
| Test field name | Student original entry 2 |
|
||||
And I press "Save and view"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test database name"
|
||||
And I am on the "Test database name" "data activity" page logged in as teacher1
|
||||
And I follow "View single"
|
||||
And I set the field "rating" to "51"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test database name"
|
||||
When I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test database name" "data activity editing" page
|
||||
And I expand all fieldsets
|
||||
Then the "Maximum grade" "field" should be disabled
|
||||
|
||||
@@ -25,25 +25,19 @@ Feature: Using the forum activities which support point scale
|
||||
|
||||
@javascript
|
||||
Scenario: Forum rescale grade should not be possible when users are graded
|
||||
Given I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
Given I am on the "Course 1" course page logged in as student1
|
||||
And I add a new discussion to "Test forum name" forum with:
|
||||
| Subject | Discussion subject |
|
||||
| Message | Test post in forum 1 |
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test forum name"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test forum name" "forum activity editing" page logged in as teacher1
|
||||
And I expand all fieldsets
|
||||
And I set the field "Ratings > Aggregate type" to "Count of ratings"
|
||||
And I set the field "Ratings > Type" to "Point"
|
||||
And I press "Save and return to course"
|
||||
And I follow "Test forum name"
|
||||
And I am on the "Test forum name" "forum activity" page
|
||||
And I follow "Discussion subject"
|
||||
And I set the field "rating" to "30"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test forum name"
|
||||
When I navigate to "Edit settings" in current page administration
|
||||
When I am on the "Test forum name" "forum activity editing" page
|
||||
And I expand all fieldsets
|
||||
Then the "Maximum grade" "field" should be disabled
|
||||
|
||||
@@ -25,27 +25,20 @@ Feature: Using the glossary activities which support point scale
|
||||
|
||||
@javascript
|
||||
Scenario: Glossary rescale grade should not be possible when users are graded
|
||||
Given I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test glossary name"
|
||||
Given I am on the "Test glossary name" "glossary activity" page logged in as student1
|
||||
And I press "Add a new entry"
|
||||
And I set the following fields to these values:
|
||||
| Concept | Testing score |
|
||||
| Definition | Scoring high on tests |
|
||||
And I press "Save changes"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test glossary name"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test glossary name" "glossary activity editing" page logged in as teacher1
|
||||
And I expand all fieldsets
|
||||
And I set the field "Ratings > Aggregate type" to "Count of ratings"
|
||||
And I set the field "Ratings > Type" to "Point"
|
||||
And I press "Save and return to course"
|
||||
And I follow "Test glossary name"
|
||||
And I am on the "Test glossary name" "glossary activity" page
|
||||
And I set the field "rating" to "50"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test glossary name"
|
||||
When I navigate to "Edit settings" in current page administration
|
||||
When I am on the "Test glossary name" "glossary activity editing" page
|
||||
And I expand all fieldsets
|
||||
Then the "Maximum grade" "field" should be disabled
|
||||
|
||||
@@ -25,9 +25,7 @@ Feature: Using the lesson activities which support point scale
|
||||
|
||||
@javascript
|
||||
Scenario: Lesson rescale grade should not be possible when users are graded
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test lesson name"
|
||||
Given I am on the "Test lesson name" "lesson activity" page logged in as teacher1
|
||||
And I follow "Add a question page"
|
||||
And I set the field "Select a question type" to "Numerical"
|
||||
And I press "Add a question page"
|
||||
@@ -40,15 +38,10 @@ Feature: Using the lesson activities which support point scale
|
||||
| id_jumpto_6 | Next page |
|
||||
And I press "Save page"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test lesson name"
|
||||
And I am on the "Test lesson name" "lesson activity" page logged in as student1
|
||||
And I set the field "Your answer" to "5"
|
||||
And I press "Submit"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test lesson name"
|
||||
When I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
|
||||
And I expand all fieldsets
|
||||
Then the "Maximum grade" "field" should be disabled
|
||||
|
||||
@@ -31,13 +31,10 @@ Feature: Using the activity grade form element
|
||||
| Scale | Disappointing, Good, Very good, Excellent |
|
||||
And I press "Save changes"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber |
|
||||
| forum | Test forum name | Test forum description | C1 | forum1 |
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test forum name"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test forum name" "forum activity editing" page logged in as teacher1
|
||||
And I set the following fields to these values:
|
||||
| Forum type | Standard forum for general use |
|
||||
| Aggregate type | Average of ratings |
|
||||
@@ -45,9 +42,7 @@ Feature: Using the activity grade form element
|
||||
| scale[modgrade_point] | 100 |
|
||||
| Group mode | No groups |
|
||||
And I press "Save and return to course"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test forum name"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test forum name" "forum activity editing" page
|
||||
When I expand all fieldsets
|
||||
Then I should not see "Some grades have already been awarded, so the grade type"
|
||||
And I set the field "scale[modgrade_type]" to "Scale"
|
||||
@@ -94,21 +89,17 @@ Feature: Using the activity grade form element
|
||||
| scale[modgrade_scale] | ABCDEF |
|
||||
| Group mode | No groups |
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test forum name"
|
||||
And I am on the "Test forum name" "forum activity" page logged in as student1
|
||||
And I click on "Add a new discussion topic" "link"
|
||||
And I set the following fields to these values:
|
||||
| Subject | Discussion subject |
|
||||
| Message | Discussion message |
|
||||
And I press "Post to forum"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test forum name"
|
||||
And I am on the "Test forum name" "forum activity" page logged in as teacher1
|
||||
And I follow "Discussion subject"
|
||||
And I set the field "rating" to "D"
|
||||
And I follow "Test forum name"
|
||||
And I am on the "Test forum name" "forum activity" page
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
When I expand all fieldsets
|
||||
Then I should see "Some grades have already been awarded, so the grade type and scale cannot be changed"
|
||||
@@ -143,8 +134,7 @@ Feature: Using the activity grade form element
|
||||
| Description | Test assignment description |
|
||||
| grade[modgrade_type] | Scale |
|
||||
| grade[modgrade_scale] | ABCDEF |
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment name"
|
||||
And I am on the "Test assignment name" "assign activity" page
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Grade" to "C"
|
||||
@@ -161,13 +151,10 @@ Feature: Using the activity grade form element
|
||||
|
||||
@javascript
|
||||
Scenario: Attempting to change the maximum grade when ratings exist
|
||||
Given I log in as "teacher1"
|
||||
And the following "activities" exist:
|
||||
Given the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber | section |
|
||||
| forum | Test forum name | Test forum description | C1 | forum1 | 1 |
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test forum name"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Test forum name" "forum activity editing" page logged in as teacher1
|
||||
And I set the following fields to these values:
|
||||
| Forum type | Standard forum for general use |
|
||||
| Aggregate type | Average of ratings |
|
||||
@@ -176,21 +163,17 @@ Feature: Using the activity grade form element
|
||||
| Group mode | No groups |
|
||||
And I press "Save and return to course"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test forum name"
|
||||
And I am on the "Test forum name" "forum activity" page logged in as student1
|
||||
And I click on "Add a new discussion topic" "link"
|
||||
And I set the following fields to these values:
|
||||
| Subject | Discussion subject |
|
||||
| Message | Discussion message |
|
||||
And I press "Post to forum"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test forum name"
|
||||
And I am on the "Test forum name" "forum activity" page logged in as teacher1
|
||||
And I follow "Discussion subject"
|
||||
And I set the field "rating" to "100"
|
||||
And I follow "Test forum name"
|
||||
And I am on the "Test forum name" "forum activity" page
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
When I expand all fieldsets
|
||||
Then I should see "You cannot change the type, as grades already exist for this item."
|
||||
@@ -198,13 +181,11 @@ Feature: Using the activity grade form element
|
||||
|
||||
@javascript
|
||||
Scenario: Attempting to change the maximum grade when no rescaling option has been chosen
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Assignment" to section "1" and I fill the form with:
|
||||
| Assignment name | Test assignment name |
|
||||
| Description | Test assignment description |
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test assignment name"
|
||||
Given the following "activity" exists:
|
||||
| course | C1 |
|
||||
| activity | assign |
|
||||
| name | Test assign name |
|
||||
And I am on the "Test assign name" "assign activity" page logged in as teacher1
|
||||
And I navigate to "View all submissions" in current page administration
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Grade out of 100" to "50"
|
||||
|
||||
+5
-4
@@ -897,9 +897,10 @@ function initialise_fullme() {
|
||||
$_SERVER['SERVER_PORT'] = 443; // Assume default ssl port for the proxy.
|
||||
}
|
||||
|
||||
// hopefully this will stop all those "clever" admins trying to set up moodle
|
||||
// with two different addresses in intranet and Internet
|
||||
if (!empty($CFG->reverseproxy) && $rurl['host'] === $wwwroot['host']) {
|
||||
// Hopefully this will stop all those "clever" admins trying to set up moodle
|
||||
// with two different addresses in intranet and Internet.
|
||||
// Port forwarding is still allowed!
|
||||
if (!empty($CFG->reverseproxy) && $rurl['host'] === $wwwroot['host'] && (empty($wwwroot['port']) || $rurl['port'] === $wwwroot['port'])) {
|
||||
print_error('reverseproxyabused', 'error');
|
||||
}
|
||||
|
||||
@@ -950,7 +951,7 @@ function setup_get_remote_url() {
|
||||
} else {
|
||||
$rurl['host'] = null;
|
||||
}
|
||||
$rurl['port'] = $_SERVER['SERVER_PORT'];
|
||||
$rurl['port'] = (int)$_SERVER['SERVER_PORT'];
|
||||
$rurl['path'] = $_SERVER['SCRIPT_NAME']; // Script path without slash arguments
|
||||
$rurl['scheme'] = (empty($_SERVER['HTTPS']) or $_SERVER['HTTPS'] === 'off' or $_SERVER['HTTPS'] === 'Off' or $_SERVER['HTTPS'] === 'OFF') ? 'http' : 'https';
|
||||
|
||||
|
||||
@@ -57,4 +57,33 @@ abstract class component_generator_base {
|
||||
*/
|
||||
public function reset() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current user during data generation.
|
||||
*
|
||||
* This should be avoided wherever possible, but in some situations underlying code will insert data as the current
|
||||
* user.
|
||||
*
|
||||
* @param stdClass $user
|
||||
*/
|
||||
protected function set_user(?stdClass $user = null): void {
|
||||
global $CFG, $DB;
|
||||
|
||||
if ($user === null) {
|
||||
$user = (object) [
|
||||
'id' => 0,
|
||||
'mnethostid' => $CFG->mnet_localhost_id,
|
||||
];
|
||||
} else {
|
||||
$user = clone($user);
|
||||
unset($user->description);
|
||||
unset($user->access);
|
||||
unset($user->preference);
|
||||
}
|
||||
|
||||
// Ensure session is empty, as it may contain caches and user-specific info.
|
||||
\core\session\manager::init_empty_session();
|
||||
|
||||
\core\session\manager::set_user($user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,9 @@ EOD;
|
||||
* @return component_generator_base or rather an instance of the appropriate subclass.
|
||||
*/
|
||||
public function get_plugin_generator($component) {
|
||||
// Note: This global is included so that generator have access to it.
|
||||
// CFG is widely used in require statements.
|
||||
global $CFG;
|
||||
list($type, $plugin) = core_component::normalize_component($component);
|
||||
$cleancomponent = $type . '_' . $plugin;
|
||||
if ($cleancomponent != $component) {
|
||||
|
||||
@@ -110,6 +110,46 @@ class core_adhoc_task_testcase extends advanced_testcase {
|
||||
\core\task\manager::adhoc_task_complete($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test queueing an adhoc task belonging to a component, where we set the task component accordingly
|
||||
*/
|
||||
public function test_queue_adhoc_task_for_component(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$task = new \mod_forum\task\refresh_forum_post_counts();
|
||||
$task->set_component('mod_test');
|
||||
|
||||
\core\task\manager::queue_adhoc_task($task);
|
||||
$this->assertDebuggingNotCalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test queueing an adhoc task belonging to a component, where we do not set the task component
|
||||
*/
|
||||
public function test_queue_task_for_component_without_set_component(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$task = new \mod_forum\task\refresh_forum_post_counts();
|
||||
|
||||
\core\task\manager::queue_adhoc_task($task);
|
||||
$this->assertDebuggingNotCalled();
|
||||
|
||||
// Assert the missing component was set.
|
||||
$this->assertEquals('mod_forum', $task->get_component());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test queueing an adhoc task belonging to an invalid component, where we do not set the task component
|
||||
*/
|
||||
public function test_queue_task_for_invalid_component_without_set_component(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$task = new \mod_fake\task\adhoc_component_task();
|
||||
|
||||
\core\task\manager::queue_adhoc_task($task);
|
||||
$this->assertDebuggingCalled('Component not set and the class namespace does not match a valid component (mod_fake).');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test empty set of adhoc tasks
|
||||
*/
|
||||
|
||||
@@ -240,8 +240,7 @@ Feature: Initials bar
|
||||
Given the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro | assignsubmission_onlinetext_enabled | assignsubmission_file_enabled |
|
||||
| assign | C1 | assign1 | TestAssignment | Test assignment description | 0 | 0 |
|
||||
And I am on the "assign1" "Activity" page logged in as "admin"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "assign1" "assign Activity editing" page logged in as "admin"
|
||||
And I expand all fieldsets
|
||||
And I set the field "Completion tracking" to "1"
|
||||
And I click on "Save and return to course" "button"
|
||||
|
||||
@@ -543,7 +543,9 @@ class behat_navigation extends behat_base {
|
||||
*
|
||||
* For pages belonging to core, the 'core > ' bit is omitted.
|
||||
*
|
||||
* @When I am on the :page page
|
||||
* @When /^I am on the (?<page>[^ "]*) page$/
|
||||
* @When /^I am on the "(?<page>[^"]*)" page$/
|
||||
*
|
||||
* @param string $page the component and page name.
|
||||
* E.g. 'Admin notifications' or 'core_user > Preferences'.
|
||||
* @throws Exception if the specified page cannot be determined.
|
||||
@@ -561,7 +563,11 @@ class behat_navigation extends behat_base {
|
||||
* but with the advantage that you go straight to the desired page, without
|
||||
* having to wait for the Dashboard to load.
|
||||
*
|
||||
* @When I am on the :page page logged in as :username
|
||||
* @When /^I am on the (?<page>[^ "]*) page logged in as (?<username>[^ "]*)$/
|
||||
* @When /^I am on the "(?<page>[^"]*)" page logged in as (?<username>[^ "]*)$/
|
||||
* @When /^I am on the (?<page>[^ "]*) page logged in as "(?<username>[^ "]*)"$/
|
||||
* @When /^I am on the "(?<page>[^"]*)" page logged in as "(?<username>[^ "]*)"$/
|
||||
*
|
||||
* @param string $page the type of page. E.g. 'Admin notifications' or 'core_user > Preferences'.
|
||||
* @param string $username the name of the user to log in as. E.g. 'admin'.
|
||||
* @throws Exception if the specified page cannot be determined.
|
||||
@@ -622,7 +628,11 @@ class behat_navigation extends behat_base {
|
||||
*
|
||||
* For pages belonging to core, the 'core > ' bit is omitted.
|
||||
*
|
||||
* @When I am on the :identifier :type page
|
||||
* @When /^I am on the (?<identifier>[^ "]*) (?<type>[^ "]*) page$/
|
||||
* @When /^I am on the "(?<identifier>[^"]*)" "(?<type>[^"]*)" page$/
|
||||
* @When /^I am on the (?<identifier>[^ "]*) "(?<type>[^"]*)" page$/
|
||||
* @When /^I am on the "(?<identifier>[^"]*)" (?<type>[^ "]*) page$/
|
||||
*
|
||||
* @param string $identifier identifies the particular page. E.g. 'Test quiz'.
|
||||
* @param string $type the component and page type. E.g. 'mod_quiz > View'.
|
||||
* @throws Exception if the specified page cannot be determined.
|
||||
@@ -640,7 +650,15 @@ class behat_navigation extends behat_base {
|
||||
* but with the advantage that you go straight to the desired page, without
|
||||
* having to wait for the Dashboard to load.
|
||||
*
|
||||
* @When I am on the :identifier :type page logged in as :username
|
||||
* @When /^I am on the (?<identifier>[^ "]*) (?<type>[^ "]*) page logged in as (?<username>[^ "]*)$/
|
||||
* @When /^I am on the "(?<identifier>[^"]*)" "(?<type>[^"]*)" page logged in as (?<username>[^ "]*)$/
|
||||
* @When /^I am on the (?<identifier>[^ "]*) "(?<type>[^"]*)" page logged in as (?<username>[^ "]*)$/
|
||||
* @When /^I am on the "(?<identifier>[^"]*)" (?<type>[^ "]*) page logged in as (?<username>[^ "]*)$/
|
||||
* @When /^I am on the (?<identifier>[^ "]*) (?<type>[^ "]*) page logged in as "(?<username>[^"]*)"$/
|
||||
* @When /^I am on the "(?<identifier>[^"]*)" "(?<type>[^"]*)" page logged in as "(?<username>[^"]*)"$/
|
||||
* @When /^I am on the (?<identifier>[^ "]*) "(?<type>[^"]*)" page logged in as "(?<username>[^"]*)"$/
|
||||
* @When /^I am on the "(?<identifier>[^"]*)" (?<type>[^ "]*) page logged in as "(?<username>[^"]*)"$/
|
||||
*
|
||||
* @param string $identifier identifies the particular page. E.g. 'Test quiz'.
|
||||
* @param string $type the component and page type. E.g. 'mod_quiz > View'.
|
||||
* @param string $username the name of the user to log in as. E.g. 'student'.
|
||||
@@ -697,10 +715,17 @@ class behat_navigation extends behat_base {
|
||||
* Convert page names to URLs for steps like 'When I am on the "[identifier]" "[page type]" page'.
|
||||
*
|
||||
* Recognised page names are:
|
||||
* | Page type | Identifier meaning | description |
|
||||
* | Category page | category idnumber | List of courses in that category. |
|
||||
* | Course | course shortname | Main course home pag |
|
||||
* | Activity | activity idnumber | Start page for that activity |
|
||||
* | Page type | Identifier meaning | description |
|
||||
* | Category | category idnumber | List of courses in that category. |
|
||||
* | Course | course shortname | Main course home pag |
|
||||
* | Activity | activity idnumber | Start page for that activity |
|
||||
* | Activity editing | activity idnumber | Edit settings page for that activity |
|
||||
* | [modname] Activity | activity name or idnumber | Start page for that activity |
|
||||
* | [modname] Activity editing | activity name or idnumber | Edit settings page for that activity |
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* When I am on the "Welcome to ECON101" "forum activity" page logged in as student1
|
||||
*
|
||||
* @param string $type identifies which type of page this is, e.g. 'Category page'.
|
||||
* @param string $identifier identifies the particular page, e.g. 'test-cat'.
|
||||
@@ -710,33 +735,70 @@ class behat_navigation extends behat_base {
|
||||
protected function resolve_core_page_instance_url(string $type, string $identifier): moodle_url {
|
||||
global $DB;
|
||||
|
||||
$type = strtolower($type);
|
||||
|
||||
switch ($type) {
|
||||
case 'Category page':
|
||||
$categoryid = $DB->get_field('course_categories', 'id', ['idnumber' => $identifier]);
|
||||
case 'category':
|
||||
$categoryid = $this->get_category_id($identifier);
|
||||
if (!$categoryid) {
|
||||
throw new Exception('The specified category with idnumber "' . $identifier . '" does not exist');
|
||||
}
|
||||
return new moodle_url('/course/category.php', ['id' => $categoryid]);
|
||||
return new moodle_url('/course/index.php', ['categoryid' => $categoryid]);
|
||||
|
||||
case 'Course':
|
||||
$courseid = $DB->get_field_select('course', 'id', 'shortname = ?', [$identifier], IGNORE_MISSING);
|
||||
case 'course':
|
||||
$courseid = $this->get_course_id($identifier);
|
||||
if (!$courseid) {
|
||||
throw new Exception('The specified course with shortname, fullname, or idnumber "' .
|
||||
$identifier . '" does not exist');
|
||||
}
|
||||
return new moodle_url('/course/view.php', ['id' => $courseid]);
|
||||
|
||||
case 'Activity':
|
||||
$cm = $DB->get_record('course_modules', ['idnumber' => $identifier], 'id, course', IGNORE_MISSING);
|
||||
case 'activity':
|
||||
$cm = $this->get_course_module_for_identifier($identifier);
|
||||
if (!$cm) {
|
||||
throw new Exception('The specified activity with idnumber "' . $identifier . '" does not exist');
|
||||
}
|
||||
$modinfo = get_fast_modinfo($cm->course);
|
||||
return $modinfo->cms[$cm->id]->url;
|
||||
return $cm->url;
|
||||
|
||||
default:
|
||||
throw new Exception('Unrecognised core page type "' . $type . '."');
|
||||
case 'activity editing':
|
||||
$cm = $this->get_course_module_for_identifier($identifier);
|
||||
if (!$cm) {
|
||||
throw new Exception('The specified activity with idnumber "' . $identifier . '" does not exist');
|
||||
}
|
||||
return new moodle_url('/course/modedit.php', [
|
||||
'update' => $cm->id,
|
||||
]);
|
||||
}
|
||||
|
||||
$parts = explode(' ', $type);
|
||||
if (count($parts) > 1) {
|
||||
if ($parts[1] === 'activity') {
|
||||
$modname = $parts[0];
|
||||
$cm = $this->get_cm_by_activity_name($modname, $identifier);
|
||||
|
||||
if (count($parts) == 2) {
|
||||
// View page.
|
||||
return new moodle_url($cm->url);
|
||||
}
|
||||
|
||||
if ($parts[2] === 'editing') {
|
||||
// Edit settings page.
|
||||
return new moodle_url('/course/modedit.php', ['update' => $cm->id]);
|
||||
}
|
||||
|
||||
if ($parts[2] === 'roles') {
|
||||
// Locally assigned roles page.
|
||||
return new moodle_url('/admin/roles/assign.php', ['contextid' => $cm->context->id]);
|
||||
}
|
||||
|
||||
if ($parts[2] === 'permissions') {
|
||||
// Permissions page.
|
||||
return new moodle_url('/admin/roles/permissions.php', ['contextid' => $cm->context->id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception('Unrecognised core page type "' . $type . '."');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,9 +12,7 @@ Feature: Any day / month / year combination in date form elements works ok.
|
||||
And the following "activity" exist:
|
||||
| activity | name | intro | course | idnumber |
|
||||
| assign | Assignment 01 | Assign activity to test some dates | C1 | assign01 |
|
||||
Given I am on the "C1" "Course" page logged in as "admin"
|
||||
And I follow "Assignment 01"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I am on the "Assignment 01" "assign activity editing" page logged in as admin
|
||||
And I expand all fieldsets
|
||||
And I set the field "Due date" to "<initial_date>"
|
||||
And I set the field "Due date" to "<final_date>"
|
||||
|
||||
@@ -31,12 +31,11 @@ Feature: Override permissions on a context
|
||||
Then "Add announcementsmod/forum:addnews" row "Prohibited" column of "permissions" table should contain "Student"
|
||||
|
||||
Scenario: Module capabilities overrides
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Forum" to section "1" and I fill the form with:
|
||||
| Forum name | Forum 1 |
|
||||
And I follow "Forum 1"
|
||||
And I navigate to "Permissions" in current page administration
|
||||
Given the following "activity" exists:
|
||||
| course | C1 |
|
||||
| activity | forum |
|
||||
| name | Forum 1 |
|
||||
And I am on the "Forum 1" "forum activity permissions" page logged in as admin
|
||||
When I click on "Allow" "icon" in the "mod/forum:addnews" "table_row"
|
||||
And I press "Student"
|
||||
Then "Add announcementsmod/forum:addnews" row "Roles with permission" column of "permissions" table should contain "Student"
|
||||
|
||||
+45
-93
@@ -262,7 +262,6 @@ class core_filelib_testcase extends advanced_testcase {
|
||||
public function test_curl_redirects() {
|
||||
global $CFG;
|
||||
|
||||
// Test full URL redirects.
|
||||
$testurl = $this->getExternalTestFileUrl('/test_redir.php');
|
||||
|
||||
$curl = new curl();
|
||||
@@ -273,6 +272,7 @@ class core_filelib_testcase extends advanced_testcase {
|
||||
$this->assertSame(2, $curl->info['redirect_count']);
|
||||
$this->assertSame('done', $contents);
|
||||
|
||||
// All redirects are emulated now. Enabling "emulateredirects" explicitly does not have effect.
|
||||
$curl = new curl();
|
||||
$curl->emulateredirects = true;
|
||||
$contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>2));
|
||||
@@ -282,6 +282,17 @@ class core_filelib_testcase extends advanced_testcase {
|
||||
$this->assertSame(2, $curl->info['redirect_count']);
|
||||
$this->assertSame('done', $contents);
|
||||
|
||||
// All redirects are emulated now. Attempting to disable "emulateredirects" explicitly causes warning.
|
||||
$curl = new curl();
|
||||
$curl->emulateredirects = false;
|
||||
$contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS' => 2));
|
||||
$response = $curl->getResponse();
|
||||
$this->assertDebuggingCalled('Attempting to disable emulated redirects has no effect any more!');
|
||||
$this->assertSame('200 OK', reset($response));
|
||||
$this->assertSame(0, $curl->get_errno());
|
||||
$this->assertSame(2, $curl->info['redirect_count']);
|
||||
$this->assertSame('done', $contents);
|
||||
|
||||
// This test was failing for people behind Squid proxies. Squid does not
|
||||
// fully support HTTP 1.1, so converts things to HTTP 1.0, where the name
|
||||
// of the status code is different.
|
||||
@@ -301,21 +312,6 @@ class core_filelib_testcase extends advanced_testcase {
|
||||
$this->assertSame('', $contents);
|
||||
|
||||
$curl = new curl();
|
||||
$curl->emulateredirects = true;
|
||||
$contents = $curl->get("$testurl?redir=3", array(), array('CURLOPT_FOLLOWLOCATION'=>0));
|
||||
$response = $curl->getResponse();
|
||||
$this->assertSame($responsecode302, reset($response));
|
||||
$this->assertSame(0, $curl->get_errno());
|
||||
$this->assertSame(302, $curl->info['http_code']);
|
||||
$this->assertSame('', $contents);
|
||||
|
||||
$curl = new curl();
|
||||
$contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>1));
|
||||
$this->assertSame(CURLE_TOO_MANY_REDIRECTS, $curl->get_errno());
|
||||
$this->assertNotEmpty($contents);
|
||||
|
||||
$curl = new curl();
|
||||
$curl->emulateredirects = true;
|
||||
$contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>1));
|
||||
$this->assertSame(CURLE_TOO_MANY_REDIRECTS, $curl->get_errno());
|
||||
$this->assertNotEmpty($contents);
|
||||
@@ -332,28 +328,6 @@ class core_filelib_testcase extends advanced_testcase {
|
||||
@unlink($tofile);
|
||||
|
||||
$curl = new curl();
|
||||
$curl->emulateredirects = true;
|
||||
$tofile = "$CFG->tempdir/test.html";
|
||||
@unlink($tofile);
|
||||
$fp = fopen($tofile, 'w');
|
||||
$result = $curl->get("$testurl?redir=1", array(), array('CURLOPT_FILE'=>$fp));
|
||||
$this->assertTrue($result);
|
||||
fclose($fp);
|
||||
$this->assertFileExists($tofile);
|
||||
$this->assertSame('done', file_get_contents($tofile));
|
||||
@unlink($tofile);
|
||||
|
||||
$curl = new curl();
|
||||
$tofile = "$CFG->tempdir/test.html";
|
||||
@unlink($tofile);
|
||||
$result = $curl->download_one("$testurl?redir=1", array(), array('filepath'=>$tofile));
|
||||
$this->assertTrue($result);
|
||||
$this->assertFileExists($tofile);
|
||||
$this->assertSame('done', file_get_contents($tofile));
|
||||
@unlink($tofile);
|
||||
|
||||
$curl = new curl();
|
||||
$curl->emulateredirects = true;
|
||||
$tofile = "$CFG->tempdir/test.html";
|
||||
@unlink($tofile);
|
||||
$result = $curl->download_one("$testurl?redir=1", array(), array('filepath'=>$tofile));
|
||||
@@ -363,6 +337,39 @@ class core_filelib_testcase extends advanced_testcase {
|
||||
@unlink($tofile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that redirects to blocked hosts are blocked.
|
||||
*/
|
||||
public function test_curl_blocked_redirect() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$testurl = $this->getExternalTestFileUrl('/test_redir.php');
|
||||
|
||||
// Block a host.
|
||||
// Note: moodle.com is the URL redirected to when test_redir.php has the param extdest=1 set.
|
||||
set_config('curlsecurityblockedhosts', 'moodle.com');
|
||||
|
||||
// Redirecting to a non-blocked host should resolve.
|
||||
$curl = new curl();
|
||||
$contents = $curl->get("{$testurl}?redir=2");
|
||||
$response = $curl->getResponse();
|
||||
$this->assertSame('200 OK', reset($response));
|
||||
$this->assertSame(0, $curl->get_errno());
|
||||
|
||||
// Redirecting to the blocked host should fail.
|
||||
$curl = new curl();
|
||||
$blockedstring = $curl->get_security()->get_blocked_url_string();
|
||||
$contents = $curl->get("{$testurl}?redir=1&extdest=1");
|
||||
$this->assertSame($blockedstring, $contents);
|
||||
$this->assertSame(0, $curl->get_errno());
|
||||
|
||||
// Redirecting to the blocked host after multiple successful redirects should also fail.
|
||||
$curl = new curl();
|
||||
$contents = $curl->get("{$testurl}?redir=3&extdest=1");
|
||||
$this->assertSame($blockedstring, $contents);
|
||||
$this->assertSame(0, $curl->get_errno());
|
||||
}
|
||||
|
||||
public function test_curl_relative_redirects() {
|
||||
// Test relative location redirects.
|
||||
$testurl = $this->getExternalTestFileUrl('/test_relative_redir.php');
|
||||
@@ -375,15 +382,6 @@ class core_filelib_testcase extends advanced_testcase {
|
||||
$this->assertSame(1, $curl->info['redirect_count']);
|
||||
$this->assertSame('done', $contents);
|
||||
|
||||
$curl = new curl();
|
||||
$curl->emulateredirects = true;
|
||||
$contents = $curl->get($testurl);
|
||||
$response = $curl->getResponse();
|
||||
$this->assertSame('200 OK', reset($response));
|
||||
$this->assertSame(0, $curl->get_errno());
|
||||
$this->assertSame(1, $curl->info['redirect_count']);
|
||||
$this->assertSame('done', $contents);
|
||||
|
||||
// Test different redirect types.
|
||||
$testurl = $this->getExternalTestFileUrl('/test_relative_redir.php');
|
||||
|
||||
@@ -396,24 +394,6 @@ class core_filelib_testcase extends advanced_testcase {
|
||||
$this->assertSame('done', $contents);
|
||||
|
||||
$curl = new curl();
|
||||
$curl->emulateredirects = true;
|
||||
$contents = $curl->get("$testurl?type=301");
|
||||
$response = $curl->getResponse();
|
||||
$this->assertSame('200 OK', reset($response));
|
||||
$this->assertSame(0, $curl->get_errno());
|
||||
$this->assertSame(1, $curl->info['redirect_count']);
|
||||
$this->assertSame('done', $contents);
|
||||
|
||||
$curl = new curl();
|
||||
$contents = $curl->get("$testurl?type=302");
|
||||
$response = $curl->getResponse();
|
||||
$this->assertSame('200 OK', reset($response));
|
||||
$this->assertSame(0, $curl->get_errno());
|
||||
$this->assertSame(1, $curl->info['redirect_count']);
|
||||
$this->assertSame('done', $contents);
|
||||
|
||||
$curl = new curl();
|
||||
$curl->emulateredirects = true;
|
||||
$contents = $curl->get("$testurl?type=302");
|
||||
$response = $curl->getResponse();
|
||||
$this->assertSame('200 OK', reset($response));
|
||||
@@ -430,24 +410,6 @@ class core_filelib_testcase extends advanced_testcase {
|
||||
$this->assertSame('done', $contents);
|
||||
|
||||
$curl = new curl();
|
||||
$curl->emulateredirects = true;
|
||||
$contents = $curl->get("$testurl?type=303");
|
||||
$response = $curl->getResponse();
|
||||
$this->assertSame('200 OK', reset($response));
|
||||
$this->assertSame(0, $curl->get_errno());
|
||||
$this->assertSame(1, $curl->info['redirect_count']);
|
||||
$this->assertSame('done', $contents);
|
||||
|
||||
$curl = new curl();
|
||||
$contents = $curl->get("$testurl?type=307");
|
||||
$response = $curl->getResponse();
|
||||
$this->assertSame('200 OK', reset($response));
|
||||
$this->assertSame(0, $curl->get_errno());
|
||||
$this->assertSame(1, $curl->info['redirect_count']);
|
||||
$this->assertSame('done', $contents);
|
||||
|
||||
$curl = new curl();
|
||||
$curl->emulateredirects = true;
|
||||
$contents = $curl->get("$testurl?type=307");
|
||||
$response = $curl->getResponse();
|
||||
$this->assertSame('200 OK', reset($response));
|
||||
@@ -462,16 +424,6 @@ class core_filelib_testcase extends advanced_testcase {
|
||||
$this->assertSame(0, $curl->get_errno());
|
||||
$this->assertSame(1, $curl->info['redirect_count']);
|
||||
$this->assertSame('done', $contents);
|
||||
|
||||
$curl = new curl();
|
||||
$curl->emulateredirects = true;
|
||||
$contents = $curl->get("$testurl?type=308");
|
||||
$response = $curl->getResponse();
|
||||
$this->assertSame('200 OK', reset($response));
|
||||
$this->assertSame(0, $curl->get_errno());
|
||||
$this->assertSame(1, $curl->info['redirect_count']);
|
||||
$this->assertSame('done', $contents);
|
||||
|
||||
}
|
||||
|
||||
public function test_curl_proxybypass() {
|
||||
|
||||
Vendored
+8
@@ -62,3 +62,11 @@ class scheduled_test3_task extends \core\task\scheduled_task {
|
||||
public function execute() {
|
||||
}
|
||||
}
|
||||
|
||||
namespace mod_fake\task;
|
||||
|
||||
class adhoc_component_task extends \core\task\adhoc_task {
|
||||
public function execute() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,6 +441,7 @@ class core_task_logmanager extends advanced_testcase {
|
||||
*/
|
||||
protected function get_test_adhoc_task() : \core\task\adhoc_task {
|
||||
$task = $this->getMockForAbstractClass(\core\task\adhoc_task::class);
|
||||
$task->set_component('core');
|
||||
|
||||
// Mock a lock on the task.
|
||||
$lock = $this->getMockBuilder(\core\lock\lock::class)
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
This files describes API changes in core libraries and APIs,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 3.11.2 ===
|
||||
* For security reasons, filelib has been updated so all requests now use emulated redirects.
|
||||
For this reason, manually disabling emulateredirects will no longer have any effect (and will generate a debugging message).
|
||||
|
||||
=== 3.11 ===
|
||||
* PHPUnit has been upgraded to 9.5 (see MDL-71036 for details).
|
||||
That comes with a few changes:
|
||||
|
||||
lib/yui/build/moodle-core-notification-ajaxexception/moodle-core-notification-ajaxexception-debug.js
Vendored
+2
-2
@@ -61,8 +61,8 @@ Y.extend(AJAXEXCEPTION, M.core.notification.info, {
|
||||
labelsep = M.util.get_string('labelsep', 'langconfig');
|
||||
this.get(BASE).addClass('moodle-dialogue-exception');
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h1 id="moodle-dialogue-' + this.get('COUNT') + '-header-text">'
|
||||
+ Y.Escape.html(config.name) + '</h1>',
|
||||
'<h5 id="moodle-dialogue-' + this.get('COUNT') + '-wrap-header-text">'
|
||||
+ Y.Escape.html(config.name) + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
content = Y.Node.create('<div class="moodle-ajaxexception" data-rel="fatalerror"></div>')
|
||||
.append(Y.Node.create('<div class="moodle-exception-message">' + Y.Escape.html(this.get('error')) + '</div>'))
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
YUI.add("moodle-core-notification-ajaxexception",function(d,e){var t,i;M.core=M.core||{},t="Moodle AJAX exception",i=function(e){e.name=e.name||M.util.get_string("error","moodle"),e.closeButton=!0,i.superclass.constructor.apply(this,[e])},d.extend(i,M.core.notification.info,{_keypress:null,initializer:function(e){var t,i=this,o=this.get("hideTimeoutDelay"),a=M.util.get_string("labelsep","langconfig");this.get("notificationBase").addClass("moodle-dialogue-exception"),this.setStdModContent(d.WidgetStdMod.HEADER,'<h1 id="moodle-dialogue-'+this.get("COUNT")+'-header-text">'+d.Escape.html(e.name)+"</h1>",d.WidgetStdMod.REPLACE),t=d.Node.create('<div class="moodle-ajaxexception" data-rel="fatalerror"></div>').append(d.Node.create('<div class="moodle-exception-message">'+d.Escape.html(this.get("error"))+"</div>")).append(d.Node.create('<div class="moodle-exception-param hidden param-debuginfo"><label>'+M.util.get_string("url","moodle")+a+"</label> "+this.get("reproductionlink")+"</div>")).append(d.Node.create('<div class="moodle-exception-param hidden param-debuginfo"><label>'+M.util.get_string("debuginfo","debug")+a+"</label> "+d.Escape.html(this.get("debuginfo"))+"</div>")).append(d.Node.create('<div class="moodle-exception-param hidden param-stacktrace"><label>'+M.util.get_string("stacktrace","debug")+a+"</label> <pre>"+d.Escape.html(this.get("stacktrace"))+"</pre></div>")),M.cfg.developerdebug&&t.all(".moodle-exception-param").removeClass("hidden"),this.setStdModContent(d.WidgetStdMod.BODY,t,d.WidgetStdMod.REPLACE),o&&(this._hideTimeout=setTimeout(function(){i.hide()},o)),this.after("visibleChange",this.visibilityChanged,this),this._keypress=d.on("key",this.hide,window,"down:13, 27",this),this.centerDialogue()},visibilityChanged:function(e){if("visible"===e.attrName&&e.prevVal&&!e.newVal){var t=this;this._keypress.detach(),setTimeout(function(){t.destroy()},1e3)}}},{NAME:t,CSS_PREFIX:"moodle-dialogue",ATTRS:{error:{validator:d.Lang.isString,value:M.util.get_string("unknownerror","moodle")},debuginfo:{value:null},stacktrace:{value:null},reproductionlink:{setter:function(e){return null!==e&&(e='<a href="'+(e=d.Escape.html(e))+'">'+e.replace(M.cfg.wwwroot,"")+"</a>"),e},value:null},hideTimeoutDelay:{validator:d.Lang.isNumber,value:null}}}),M.core.ajaxException=i},"@VERSION@",{requires:["moodle-core-notification-dialogue"]});
|
||||
YUI.add("moodle-core-notification-ajaxexception",function(d,e){var t,i;M.core=M.core||{},t="Moodle AJAX exception",i=function(e){e.name=e.name||M.util.get_string("error","moodle"),e.closeButton=!0,i.superclass.constructor.apply(this,[e])},d.extend(i,M.core.notification.info,{_keypress:null,initializer:function(e){var t,i=this,o=this.get("hideTimeoutDelay"),a=M.util.get_string("labelsep","langconfig");this.get("notificationBase").addClass("moodle-dialogue-exception"),this.setStdModContent(d.WidgetStdMod.HEADER,'<h5 id="moodle-dialogue-'+this.get("COUNT")+'-wrap-header-text">'+d.Escape.html(e.name)+"</h5>",d.WidgetStdMod.REPLACE),t=d.Node.create('<div class="moodle-ajaxexception" data-rel="fatalerror"></div>').append(d.Node.create('<div class="moodle-exception-message">'+d.Escape.html(this.get("error"))+"</div>")).append(d.Node.create('<div class="moodle-exception-param hidden param-debuginfo"><label>'+M.util.get_string("url","moodle")+a+"</label> "+this.get("reproductionlink")+"</div>")).append(d.Node.create('<div class="moodle-exception-param hidden param-debuginfo"><label>'+M.util.get_string("debuginfo","debug")+a+"</label> "+d.Escape.html(this.get("debuginfo"))+"</div>")).append(d.Node.create('<div class="moodle-exception-param hidden param-stacktrace"><label>'+M.util.get_string("stacktrace","debug")+a+"</label> <pre>"+d.Escape.html(this.get("stacktrace"))+"</pre></div>")),M.cfg.developerdebug&&t.all(".moodle-exception-param").removeClass("hidden"),this.setStdModContent(d.WidgetStdMod.BODY,t,d.WidgetStdMod.REPLACE),o&&(this._hideTimeout=setTimeout(function(){i.hide()},o)),this.after("visibleChange",this.visibilityChanged,this),this._keypress=d.on("key",this.hide,window,"down:13, 27",this),this.centerDialogue()},visibilityChanged:function(e){if("visible"===e.attrName&&e.prevVal&&!e.newVal){var t=this;this._keypress.detach(),setTimeout(function(){t.destroy()},1e3)}}},{NAME:t,CSS_PREFIX:"moodle-dialogue",ATTRS:{error:{validator:d.Lang.isString,value:M.util.get_string("unknownerror","moodle")},debuginfo:{value:null},stacktrace:{value:null},reproductionlink:{setter:function(e){return null!==e&&(e='<a href="'+(e=d.Escape.html(e))+'">'+e.replace(M.cfg.wwwroot,"")+"</a>"),e},value:null},hideTimeoutDelay:{validator:d.Lang.isNumber,value:null}}}),M.core.ajaxException=i},"@VERSION@",{requires:["moodle-core-notification-dialogue"]});
|
||||
Vendored
+2
-2
@@ -61,8 +61,8 @@ Y.extend(AJAXEXCEPTION, M.core.notification.info, {
|
||||
labelsep = M.util.get_string('labelsep', 'langconfig');
|
||||
this.get(BASE).addClass('moodle-dialogue-exception');
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h1 id="moodle-dialogue-' + this.get('COUNT') + '-header-text">'
|
||||
+ Y.Escape.html(config.name) + '</h1>',
|
||||
'<h5 id="moodle-dialogue-' + this.get('COUNT') + '-wrap-header-text">'
|
||||
+ Y.Escape.html(config.name) + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
content = Y.Node.create('<div class="moodle-ajaxexception" data-rel="fatalerror"></div>')
|
||||
.append(Y.Node.create('<div class="moodle-exception-message">' + Y.Escape.html(this.get('error')) + '</div>'))
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ Y.extend(ALERT, M.core.notification.info, {
|
||||
this.get(BASE).addClass('moodle-dialogue-confirm');
|
||||
this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE);
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h1 id="moodle-dialogue-' + this.get('COUNT') + '-header-text">' + this.get(TITLE) + '</h1>',
|
||||
'<h5 id="moodle-dialogue-' + this.get('COUNT') + '-wrap-header-text">' + this.get(TITLE) + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
|
||||
this._closeEvents.push(
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
YUI.add("moodle-core-notification-alert",function(o,t){var e,i;M.core=M.core||{},e="Moodle alert",i=function(t){t.closeButton=!1,i.superclass.constructor.apply(this,[t])},o.extend(i,M.core.notification.info,{_closeEvents:null,initializer:function(){var t,e,i;this._closeEvents=[],this.publish("complete"),t=o.Node.create('<input type="button" class="btn btn-primary" id="id_yuialertconfirm-'+this.get("COUNT")+'"value="'+this.get("yesLabel")+'" />'),e=o.Node.create('<div class="confirmation-dialogue"></div>').append(o.Node.create('<div class="confirmation-message">'+this.get("message")+"</div>")).append(o.Node.create('<div class="confirmation-buttons text-xs-right"></div>').append(t)),this.get("notificationBase").addClass("moodle-dialogue-confirm"),this.setStdModContent(o.WidgetStdMod.BODY,e,o.WidgetStdMod.REPLACE),this.setStdModContent(o.WidgetStdMod.HEADER,'<h1 id="moodle-dialogue-'+this.get("COUNT")+'-header-text">'+this.get("title")+"</h1>",o.WidgetStdMod.REPLACE),this._closeEvents.push(o.on("key",this.submit,window,"down:13",this),t.on("click",this.submit,this)),(i=this.get("boundingBox").one(".closebutton"))&&this._closeEvents.push(i.on("click",this.submit,this))},submit:function(){new o.EventHandle(this._closeEvents).detach(),this.fire("complete"),this.hide(),this.destroy()}},{NAME:e,CSS_PREFIX:"moodle-dialogue",ATTRS:{title:{validator:o.Lang.isString,value:"Alert"},message:{validator:o.Lang.isString,value:"Confirm"},yesLabel:{validator:o.Lang.isString,setter:function(t){return t=t||"OK"},value:"OK"}}}),M.core.alert=i},"@VERSION@",{requires:["moodle-core-notification-dialogue"]});
|
||||
YUI.add("moodle-core-notification-alert",function(o,t){var e,i;M.core=M.core||{},e="Moodle alert",i=function(t){t.closeButton=!1,i.superclass.constructor.apply(this,[t])},o.extend(i,M.core.notification.info,{_closeEvents:null,initializer:function(){var t,e,i;this._closeEvents=[],this.publish("complete"),t=o.Node.create('<input type="button" class="btn btn-primary" id="id_yuialertconfirm-'+this.get("COUNT")+'"value="'+this.get("yesLabel")+'" />'),e=o.Node.create('<div class="confirmation-dialogue"></div>').append(o.Node.create('<div class="confirmation-message">'+this.get("message")+"</div>")).append(o.Node.create('<div class="confirmation-buttons text-xs-right"></div>').append(t)),this.get("notificationBase").addClass("moodle-dialogue-confirm"),this.setStdModContent(o.WidgetStdMod.BODY,e,o.WidgetStdMod.REPLACE),this.setStdModContent(o.WidgetStdMod.HEADER,'<h5 id="moodle-dialogue-'+this.get("COUNT")+'-wrap-header-text">'+this.get("title")+"</h5>",o.WidgetStdMod.REPLACE),this._closeEvents.push(o.on("key",this.submit,window,"down:13",this),t.on("click",this.submit,this)),(i=this.get("boundingBox").one(".closebutton"))&&this._closeEvents.push(i.on("click",this.submit,this))},submit:function(){new o.EventHandle(this._closeEvents).detach(),this.fire("complete"),this.hide(),this.destroy()}},{NAME:e,CSS_PREFIX:"moodle-dialogue",ATTRS:{title:{validator:o.Lang.isString,value:"Alert"},message:{validator:o.Lang.isString,value:"Confirm"},yesLabel:{validator:o.Lang.isString,setter:function(t){return t=t||"OK"},value:"OK"}}}),M.core.alert=i},"@VERSION@",{requires:["moodle-core-notification-dialogue"]});
|
||||
+1
-1
@@ -71,7 +71,7 @@ Y.extend(ALERT, M.core.notification.info, {
|
||||
this.get(BASE).addClass('moodle-dialogue-confirm');
|
||||
this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE);
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h1 id="moodle-dialogue-' + this.get('COUNT') + '-header-text">' + this.get(TITLE) + '</h1>',
|
||||
'<h5 id="moodle-dialogue-' + this.get('COUNT') + '-wrap-header-text">' + this.get(TITLE) + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
|
||||
this._closeEvents.push(
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ Y.extend(CONFIRM, M.core.notification.info, {
|
||||
this.get(BASE).addClass('moodle-dialogue-confirm');
|
||||
this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE);
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h1 id="moodle-dialogue-' + this.get('COUNT') + '-header-text">' + this.get(TITLE) + '</h1>',
|
||||
'<h5 id="moodle-dialogue-' + this.get('COUNT') + '-wrap-header-text">' + this.get(TITLE) + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
|
||||
this._closeEvents.push(
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
YUI.add("moodle-core-notification-confirm",function(i,t){var e,o;M.core=M.core||{},e="Moodle confirmation dialogue",o=function(t){o.superclass.constructor.apply(this,[t])},i.extend(o,M.core.notification.info,{_closeEvents:null,_yesButton:null,_noButton:null,_question:null,initializer:function(){var t,e;this._closeEvents=[],this.publish("complete"),this.publish("complete-yes"),this.publish("complete-no"),this._yesButton=i.Node.create('<input type="button" class="btn btn-primary" id="id_yuiconfirmyes-'+this.get("COUNT")+'" value="'+this.get("yesLabel")+'" />'),this._noButton=i.Node.create('<input type="button" class="btn btn-secondary" id="id_yuiconfirmno-'+this.get("COUNT")+'" value="'+this.get("noLabel")+'" />'),this._question=i.Node.create('<div class="confirmation-message">'+this.get("question")+"</div>"),t=i.Node.create('<div class="confirmation-dialogue"></div>').append(this._question).append(i.Node.create('<div class="confirmation-buttons form-inline justify-content-around"></div>').append(this._yesButton).append(this._noButton)),this.get("notificationBase").addClass("moodle-dialogue-confirm"),this.setStdModContent(i.WidgetStdMod.BODY,t,i.WidgetStdMod.REPLACE),this.setStdModContent(i.WidgetStdMod.HEADER,'<h1 id="moodle-dialogue-'+this.get("COUNT")+'-header-text">'+this.get("title")+"</h1>",i.WidgetStdMod.REPLACE),this._closeEvents.push(i.on("key",this.submit,window,"down:27",this,!1),this._yesButton.on("click",this.submit,this,!0),this._noButton.on("click",this.submit,this,!1)),(e=this.get("boundingBox").one(".closebutton"))&&this._closeEvents.push(e.on("click",this.submit,this))},submit:function(t,e){new i.EventHandle(this._closeEvents).detach(),this.fire("complete",e),e?this.fire("complete-yes"):this.fire("complete-no"),this.hide(),this.destroy()}},{NAME:e,CSS_PREFIX:"moodle-dialogue",ATTRS:{yesLabel:{validator:i.Lang.isString,valueFn:function(){return M.util.get_string("yes","moodle")},setter:function(t){return this._yesButton&&this._yesButton.set("value",t),t}},noLabel:{validator:i.Lang.isString,valueFn:function(){return M.util.get_string("no","moodle")},setter:function(t){return this._noButton&&this._noButton.set("value",t),t}},title:{validator:i.Lang.isString,value:M.util.get_string("confirm","moodle")},question:{validator:i.Lang.isString,valueFn:function(){return M.util.get_string("areyousure","moodle")},setter:function(t){return this._question&&this._question.set("value",t),t}}}}),i.augment(o,i.EventTarget),M.core.confirm=o},"@VERSION@",{requires:["moodle-core-notification-dialogue"]});
|
||||
YUI.add("moodle-core-notification-confirm",function(i,t){var e,o;M.core=M.core||{},e="Moodle confirmation dialogue",o=function(t){o.superclass.constructor.apply(this,[t])},i.extend(o,M.core.notification.info,{_closeEvents:null,_yesButton:null,_noButton:null,_question:null,initializer:function(){var t,e;this._closeEvents=[],this.publish("complete"),this.publish("complete-yes"),this.publish("complete-no"),this._yesButton=i.Node.create('<input type="button" class="btn btn-primary" id="id_yuiconfirmyes-'+this.get("COUNT")+'" value="'+this.get("yesLabel")+'" />'),this._noButton=i.Node.create('<input type="button" class="btn btn-secondary" id="id_yuiconfirmno-'+this.get("COUNT")+'" value="'+this.get("noLabel")+'" />'),this._question=i.Node.create('<div class="confirmation-message">'+this.get("question")+"</div>"),t=i.Node.create('<div class="confirmation-dialogue"></div>').append(this._question).append(i.Node.create('<div class="confirmation-buttons form-inline justify-content-around"></div>').append(this._yesButton).append(this._noButton)),this.get("notificationBase").addClass("moodle-dialogue-confirm"),this.setStdModContent(i.WidgetStdMod.BODY,t,i.WidgetStdMod.REPLACE),this.setStdModContent(i.WidgetStdMod.HEADER,'<h5 id="moodle-dialogue-'+this.get("COUNT")+'-wrap-header-text">'+this.get("title")+"</h5>",i.WidgetStdMod.REPLACE),this._closeEvents.push(i.on("key",this.submit,window,"down:27",this,!1),this._yesButton.on("click",this.submit,this,!0),this._noButton.on("click",this.submit,this,!1)),(e=this.get("boundingBox").one(".closebutton"))&&this._closeEvents.push(e.on("click",this.submit,this))},submit:function(t,e){new i.EventHandle(this._closeEvents).detach(),this.fire("complete",e),e?this.fire("complete-yes"):this.fire("complete-no"),this.hide(),this.destroy()}},{NAME:e,CSS_PREFIX:"moodle-dialogue",ATTRS:{yesLabel:{validator:i.Lang.isString,valueFn:function(){return M.util.get_string("yes","moodle")},setter:function(t){return this._yesButton&&this._yesButton.set("value",t),t}},noLabel:{validator:i.Lang.isString,valueFn:function(){return M.util.get_string("no","moodle")},setter:function(t){return this._noButton&&this._noButton.set("value",t),t}},title:{validator:i.Lang.isString,value:M.util.get_string("confirm","moodle")},question:{validator:i.Lang.isString,valueFn:function(){return M.util.get_string("areyousure","moodle")},setter:function(t){return this._question&&this._question.set("value",t),t}}}}),i.augment(o,i.EventTarget),M.core.confirm=o},"@VERSION@",{requires:["moodle-core-notification-dialogue"]});
|
||||
+1
-1
@@ -104,7 +104,7 @@ Y.extend(CONFIRM, M.core.notification.info, {
|
||||
this.get(BASE).addClass('moodle-dialogue-confirm');
|
||||
this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE);
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h1 id="moodle-dialogue-' + this.get('COUNT') + '-header-text">' + this.get(TITLE) + '</h1>',
|
||||
'<h5 id="moodle-dialogue-' + this.get('COUNT') + '-wrap-header-text">' + this.get(TITLE) + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
|
||||
this._closeEvents.push(
|
||||
|
||||
+25
-15
@@ -43,7 +43,12 @@ var DIALOGUE_NAME = 'Moodle dialogue',
|
||||
MENUBAR_SELECTOR = '[role=menubar]',
|
||||
DOT = '.',
|
||||
HAS_ZINDEX = 'moodle-has-zindex',
|
||||
CAN_RECEIVE_FOCUS_SELECTOR = 'input:not([type="hidden"]), a[href], button, textarea, select, [tabindex]',
|
||||
CAN_RECEIVE_FOCUS_SELECTOR = 'input:not([type="hidden"]):not([disabled]):not([tabindex^="-"]),' +
|
||||
'a[href]:not([disabled]):not([tabindex^="-"]),' +
|
||||
'button:not([disabled]):not([tabindex^="-"]),' +
|
||||
'textarea:not([disabled]):not([tabindex^="-"]),' +
|
||||
'select:not([disabled]):not([tabindex^="-"]),' +
|
||||
'[tabindex]:not([disabled]):not([tabindex^="-"])',
|
||||
FORM_SELECTOR = 'form';
|
||||
|
||||
/**
|
||||
@@ -68,7 +73,7 @@ DIALOGUE = function(config) {
|
||||
'<div id="' + id + '" role="dialog" ' +
|
||||
'aria-labelledby="' + id + '-header-text" class="' + CSS_CLASSES.WRAP + '" aria-live="polite"></div>'
|
||||
)
|
||||
.append(Y.Node.create('<div id="' + id + '-header-text" class="' + CSS_CLASSES.HEADER + ' yui3-widget-hd"></div>'))
|
||||
.append(Y.Node.create('<div class="' + CSS_CLASSES.HEADER + ' yui3-widget-hd"></div>'))
|
||||
.append(Y.Node.create('<div class="' + CSS_CLASSES.BODY + ' yui3-widget-bd"></div>'))
|
||||
.append(Y.Node.create('<div class="' + CSS_CLASSES.FOOTER + ' yui3-widget-ft"></div>')));
|
||||
Y.one(document.body).append(config.notificationBase);
|
||||
@@ -133,6 +138,10 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
this.get('buttons').header[0].setAttribute('aria-label', title);
|
||||
}
|
||||
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h5 id="' + this.get('id') + '-wrap-header-text">' + this.get('headerContent') + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
|
||||
// Initialise the element cache.
|
||||
this._hiddenSiblings = [];
|
||||
|
||||
@@ -316,10 +325,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
Y.one(titlebar).setStyle('cursor', 'move');
|
||||
}
|
||||
}
|
||||
require(['core/local/aria/focuslock'], function(FocusLockManager) {
|
||||
// Trap focus to the current bounding box.
|
||||
FocusLockManager.trapFocus(this.get('boundingBox').getDOMNode());
|
||||
}.bind(this));
|
||||
|
||||
// Only do accessibility hiding for modals because the ARIA spec
|
||||
// says that all ARIA dialogues should be modal.
|
||||
@@ -410,9 +415,12 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
Math.floor(Y.one(document.body).get('winWidth')) < this.get('responsiveWidth');
|
||||
},
|
||||
|
||||
_focus: function() {
|
||||
this.focus();
|
||||
},
|
||||
|
||||
show: function() {
|
||||
var result = null,
|
||||
header = this.headerNode,
|
||||
content = this.bodyNode,
|
||||
focusSelector = this.get('focusOnShowSelector'),
|
||||
focusNode = null;
|
||||
@@ -429,16 +437,18 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
focusNode = this.get('boundingBox').one(focusSelector);
|
||||
}
|
||||
if (!focusNode) {
|
||||
// Fall back to the header or the content if no focus node was found yet.
|
||||
if (header && header !== '') {
|
||||
focusNode = header;
|
||||
} else if (content && content !== '') {
|
||||
focusNode = content;
|
||||
// Fall back to the first focusable element in the body of the dialogue if no focus node was found yet.
|
||||
if (content && content !== '') {
|
||||
focusNode = content.one(CAN_RECEIVE_FOCUS_SELECTOR);
|
||||
}
|
||||
}
|
||||
if (focusNode) {
|
||||
focusNode.focus();
|
||||
}
|
||||
require(['core/local/aria/focuslock'], function(FocusLockManager) {
|
||||
// Trap focus to the current bounding box.
|
||||
FocusLockManager.trapFocus(this.get('boundingBox').getDOMNode());
|
||||
if (focusNode) {
|
||||
focusNode.focus();
|
||||
}
|
||||
}.bind(this));
|
||||
return result;
|
||||
},
|
||||
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
+25
-15
@@ -43,7 +43,12 @@ var DIALOGUE_NAME = 'Moodle dialogue',
|
||||
MENUBAR_SELECTOR = '[role=menubar]',
|
||||
DOT = '.',
|
||||
HAS_ZINDEX = 'moodle-has-zindex',
|
||||
CAN_RECEIVE_FOCUS_SELECTOR = 'input:not([type="hidden"]), a[href], button, textarea, select, [tabindex]',
|
||||
CAN_RECEIVE_FOCUS_SELECTOR = 'input:not([type="hidden"]):not([disabled]):not([tabindex^="-"]),' +
|
||||
'a[href]:not([disabled]):not([tabindex^="-"]),' +
|
||||
'button:not([disabled]):not([tabindex^="-"]),' +
|
||||
'textarea:not([disabled]):not([tabindex^="-"]),' +
|
||||
'select:not([disabled]):not([tabindex^="-"]),' +
|
||||
'[tabindex]:not([disabled]):not([tabindex^="-"])',
|
||||
FORM_SELECTOR = 'form';
|
||||
|
||||
/**
|
||||
@@ -68,7 +73,7 @@ DIALOGUE = function(config) {
|
||||
'<div id="' + id + '" role="dialog" ' +
|
||||
'aria-labelledby="' + id + '-header-text" class="' + CSS_CLASSES.WRAP + '" aria-live="polite"></div>'
|
||||
)
|
||||
.append(Y.Node.create('<div id="' + id + '-header-text" class="' + CSS_CLASSES.HEADER + ' yui3-widget-hd"></div>'))
|
||||
.append(Y.Node.create('<div class="' + CSS_CLASSES.HEADER + ' yui3-widget-hd"></div>'))
|
||||
.append(Y.Node.create('<div class="' + CSS_CLASSES.BODY + ' yui3-widget-bd"></div>'))
|
||||
.append(Y.Node.create('<div class="' + CSS_CLASSES.FOOTER + ' yui3-widget-ft"></div>')));
|
||||
Y.one(document.body).append(config.notificationBase);
|
||||
@@ -133,6 +138,10 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
this.get('buttons').header[0].setAttribute('aria-label', title);
|
||||
}
|
||||
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h5 id="' + this.get('id') + '-wrap-header-text">' + this.get('headerContent') + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
|
||||
// Initialise the element cache.
|
||||
this._hiddenSiblings = [];
|
||||
|
||||
@@ -316,10 +325,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
Y.one(titlebar).setStyle('cursor', 'move');
|
||||
}
|
||||
}
|
||||
require(['core/local/aria/focuslock'], function(FocusLockManager) {
|
||||
// Trap focus to the current bounding box.
|
||||
FocusLockManager.trapFocus(this.get('boundingBox').getDOMNode());
|
||||
}.bind(this));
|
||||
|
||||
// Only do accessibility hiding for modals because the ARIA spec
|
||||
// says that all ARIA dialogues should be modal.
|
||||
@@ -410,9 +415,12 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
Math.floor(Y.one(document.body).get('winWidth')) < this.get('responsiveWidth');
|
||||
},
|
||||
|
||||
_focus: function() {
|
||||
this.focus();
|
||||
},
|
||||
|
||||
show: function() {
|
||||
var result = null,
|
||||
header = this.headerNode,
|
||||
content = this.bodyNode,
|
||||
focusSelector = this.get('focusOnShowSelector'),
|
||||
focusNode = null;
|
||||
@@ -429,16 +437,18 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
focusNode = this.get('boundingBox').one(focusSelector);
|
||||
}
|
||||
if (!focusNode) {
|
||||
// Fall back to the header or the content if no focus node was found yet.
|
||||
if (header && header !== '') {
|
||||
focusNode = header;
|
||||
} else if (content && content !== '') {
|
||||
focusNode = content;
|
||||
// Fall back to the first focusable element in the body of the dialogue if no focus node was found yet.
|
||||
if (content && content !== '') {
|
||||
focusNode = content.one(CAN_RECEIVE_FOCUS_SELECTOR);
|
||||
}
|
||||
}
|
||||
if (focusNode) {
|
||||
focusNode.focus();
|
||||
}
|
||||
require(['core/local/aria/focuslock'], function(FocusLockManager) {
|
||||
// Trap focus to the current bounding box.
|
||||
FocusLockManager.trapFocus(this.get('boundingBox').getDOMNode());
|
||||
if (focusNode) {
|
||||
focusNode.focus();
|
||||
}
|
||||
}.bind(this));
|
||||
return result;
|
||||
},
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -77,7 +77,7 @@ Y.extend(EXCEPTION, M.core.notification.info, {
|
||||
labelsep = M.util.get_string('labelsep', 'langconfig');
|
||||
this.get(BASE).addClass('moodle-dialogue-exception');
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h1 id="moodle-dialogue-' + config.COUNT + '-header-text">' + Y.Escape.html(config.name) + '</h1>',
|
||||
'<h5 id="moodle-dialogue-' + this.get('COUNT') + '-wrap-header-text">' + Y.Escape.html(config.name) + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
content = Y.Node.create('<div class="moodle-exception" data-rel="fatalerror"></div>')
|
||||
.append(Y.Node.create('<div class="moodle-exception-message">' + Y.Escape.html(this.get('message')) + '</div>'))
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
YUI.add("moodle-core-notification-exception",function(l,e){var i,a;M.core=M.core||{},i="Moodle exception",a=function(i){var e,t=l.mix({},i);t.width=t.width||M.cfg.developerdebug?Math.floor(l.one(document.body).get("winWidth")/3)+"px":null,t.closeButton=!0,e=["message","name","fileName","lineNumber","stack"],l.Array.each(e,function(e){t[e]=i[e]}),a.superclass.constructor.apply(this,[t])},l.extend(a,M.core.notification.info,{_hideTimeout:null,_keypress:null,initializer:function(e){var i,t=this,a=this.get("hideTimeoutDelay"),o=M.util.get_string("labelsep","langconfig");this.get("notificationBase").addClass("moodle-dialogue-exception"),this.setStdModContent(l.WidgetStdMod.HEADER,'<h1 id="moodle-dialogue-'+e.COUNT+'-header-text">'+l.Escape.html(e.name)+"</h1>",l.WidgetStdMod.REPLACE),i=l.Node.create('<div class="moodle-exception" data-rel="fatalerror"></div>').append(l.Node.create('<div class="moodle-exception-message">'+l.Escape.html(this.get("message"))+"</div>")).append(l.Node.create('<div class="moodle-exception-param hidden param-filename"><label>'+M.util.get_string("file","moodle")+o+"</label> "+l.Escape.html(this.get("fileName"))+"</div>")).append(l.Node.create('<div class="moodle-exception-param hidden param-linenumber"><label>'+M.util.get_string("line","debug")+o+"</label> "+l.Escape.html(this.get("lineNumber"))+"</div>")).append(l.Node.create('<div class="moodle-exception-param hidden param-stacktrace"><label>'+M.util.get_string("stacktrace","debug")+o+"</label> <pre>"+this.get("stack")+"</pre></div>")),M.cfg.developerdebug&&i.all(".moodle-exception-param").removeClass("hidden"),this.setStdModContent(l.WidgetStdMod.BODY,i,l.WidgetStdMod.REPLACE),a&&(this._hideTimeout=setTimeout(function(){t.hide()},a)),this.after("visibleChange",this.visibilityChanged,this),this._keypress=l.on("key",this.hide,window,"down:13,27",this),this.centerDialogue()},visibilityChanged:function(e){if("visible"===e.attrName&&e.prevVal&&!e.newVal){this._keypress&&this._keypress.detach();var i=this;setTimeout(function(){i.destroy()},1e3)}}},{NAME:i,CSS_PREFIX:"moodle-dialogue",ATTRS:{message:{value:""},name:{value:""},fileName:{value:""},lineNumber:{value:""},stack:{setter:function(e){var i,t=l.Escape.html(e).split("\n"),a=new RegExp("^(.+)@("+M.cfg.wwwroot+")?(.{0,75}).*:(\\d+)$");for(i in t)t[i]=t[i].replace(a,"<div class='stacktrace-line'>ln: $4</div><div class='stacktrace-file'>$3</div><div class='stacktrace-call'>$1</div>");return t.join("\n")},value:""},hideTimeoutDelay:{validator:l.Lang.isNumber,value:null}}}),M.core.exception=a},"@VERSION@",{requires:["moodle-core-notification-dialogue"]});
|
||||
YUI.add("moodle-core-notification-exception",function(l,e){var i,a;M.core=M.core||{},i="Moodle exception",a=function(i){var e,t=l.mix({},i);t.width=t.width||M.cfg.developerdebug?Math.floor(l.one(document.body).get("winWidth")/3)+"px":null,t.closeButton=!0,e=["message","name","fileName","lineNumber","stack"],l.Array.each(e,function(e){t[e]=i[e]}),a.superclass.constructor.apply(this,[t])},l.extend(a,M.core.notification.info,{_hideTimeout:null,_keypress:null,initializer:function(e){var i,t=this,a=this.get("hideTimeoutDelay"),o=M.util.get_string("labelsep","langconfig");this.get("notificationBase").addClass("moodle-dialogue-exception"),this.setStdModContent(l.WidgetStdMod.HEADER,'<h5 id="moodle-dialogue-'+this.get("COUNT")+'-wrap-header-text">'+l.Escape.html(e.name)+"</h5>",l.WidgetStdMod.REPLACE),i=l.Node.create('<div class="moodle-exception" data-rel="fatalerror"></div>').append(l.Node.create('<div class="moodle-exception-message">'+l.Escape.html(this.get("message"))+"</div>")).append(l.Node.create('<div class="moodle-exception-param hidden param-filename"><label>'+M.util.get_string("file","moodle")+o+"</label> "+l.Escape.html(this.get("fileName"))+"</div>")).append(l.Node.create('<div class="moodle-exception-param hidden param-linenumber"><label>'+M.util.get_string("line","debug")+o+"</label> "+l.Escape.html(this.get("lineNumber"))+"</div>")).append(l.Node.create('<div class="moodle-exception-param hidden param-stacktrace"><label>'+M.util.get_string("stacktrace","debug")+o+"</label> <pre>"+this.get("stack")+"</pre></div>")),M.cfg.developerdebug&&i.all(".moodle-exception-param").removeClass("hidden"),this.setStdModContent(l.WidgetStdMod.BODY,i,l.WidgetStdMod.REPLACE),a&&(this._hideTimeout=setTimeout(function(){t.hide()},a)),this.after("visibleChange",this.visibilityChanged,this),this._keypress=l.on("key",this.hide,window,"down:13,27",this),this.centerDialogue()},visibilityChanged:function(e){if("visible"===e.attrName&&e.prevVal&&!e.newVal){this._keypress&&this._keypress.detach();var i=this;setTimeout(function(){i.destroy()},1e3)}}},{NAME:i,CSS_PREFIX:"moodle-dialogue",ATTRS:{message:{value:""},name:{value:""},fileName:{value:""},lineNumber:{value:""},stack:{setter:function(e){var i,t=l.Escape.html(e).split("\n"),a=new RegExp("^(.+)@("+M.cfg.wwwroot+")?(.{0,75}).*:(\\d+)$");for(i in t)t[i]=t[i].replace(a,"<div class='stacktrace-line'>ln: $4</div><div class='stacktrace-file'>$3</div><div class='stacktrace-call'>$1</div>");return t.join("\n")},value:""},hideTimeoutDelay:{validator:l.Lang.isNumber,value:null}}}),M.core.exception=a},"@VERSION@",{requires:["moodle-core-notification-dialogue"]});
|
||||
+1
-1
@@ -77,7 +77,7 @@ Y.extend(EXCEPTION, M.core.notification.info, {
|
||||
labelsep = M.util.get_string('labelsep', 'langconfig');
|
||||
this.get(BASE).addClass('moodle-dialogue-exception');
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h1 id="moodle-dialogue-' + config.COUNT + '-header-text">' + Y.Escape.html(config.name) + '</h1>',
|
||||
'<h5 id="moodle-dialogue-' + this.get('COUNT') + '-wrap-header-text">' + Y.Escape.html(config.name) + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
content = Y.Node.create('<div class="moodle-exception" data-rel="fatalerror"></div>')
|
||||
.append(Y.Node.create('<div class="moodle-exception-message">' + Y.Escape.html(this.get('message')) + '</div>'))
|
||||
|
||||
+2
-2
@@ -31,8 +31,8 @@ Y.extend(AJAXEXCEPTION, M.core.notification.info, {
|
||||
labelsep = M.util.get_string('labelsep', 'langconfig');
|
||||
this.get(BASE).addClass('moodle-dialogue-exception');
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h1 id="moodle-dialogue-' + this.get('COUNT') + '-header-text">'
|
||||
+ Y.Escape.html(config.name) + '</h1>',
|
||||
'<h5 id="moodle-dialogue-' + this.get('COUNT') + '-wrap-header-text">'
|
||||
+ Y.Escape.html(config.name) + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
content = Y.Node.create('<div class="moodle-ajaxexception" data-rel="fatalerror"></div>')
|
||||
.append(Y.Node.create('<div class="moodle-exception-message">' + Y.Escape.html(this.get('error')) + '</div>'))
|
||||
|
||||
Vendored
+1
-1
@@ -41,7 +41,7 @@ Y.extend(ALERT, M.core.notification.info, {
|
||||
this.get(BASE).addClass('moodle-dialogue-confirm');
|
||||
this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE);
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h1 id="moodle-dialogue-' + this.get('COUNT') + '-header-text">' + this.get(TITLE) + '</h1>',
|
||||
'<h5 id="moodle-dialogue-' + this.get('COUNT') + '-wrap-header-text">' + this.get(TITLE) + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
|
||||
this._closeEvents.push(
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ Y.extend(CONFIRM, M.core.notification.info, {
|
||||
this.get(BASE).addClass('moodle-dialogue-confirm');
|
||||
this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE);
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h1 id="moodle-dialogue-' + this.get('COUNT') + '-header-text">' + this.get(TITLE) + '</h1>',
|
||||
'<h5 id="moodle-dialogue-' + this.get('COUNT') + '-wrap-header-text">' + this.get(TITLE) + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
|
||||
this._closeEvents.push(
|
||||
|
||||
+25
-15
@@ -13,7 +13,12 @@ var DIALOGUE_NAME = 'Moodle dialogue',
|
||||
MENUBAR_SELECTOR = '[role=menubar]',
|
||||
DOT = '.',
|
||||
HAS_ZINDEX = 'moodle-has-zindex',
|
||||
CAN_RECEIVE_FOCUS_SELECTOR = 'input:not([type="hidden"]), a[href], button, textarea, select, [tabindex]',
|
||||
CAN_RECEIVE_FOCUS_SELECTOR = 'input:not([type="hidden"]):not([disabled]):not([tabindex^="-"]),' +
|
||||
'a[href]:not([disabled]):not([tabindex^="-"]),' +
|
||||
'button:not([disabled]):not([tabindex^="-"]),' +
|
||||
'textarea:not([disabled]):not([tabindex^="-"]),' +
|
||||
'select:not([disabled]):not([tabindex^="-"]),' +
|
||||
'[tabindex]:not([disabled]):not([tabindex^="-"])',
|
||||
FORM_SELECTOR = 'form';
|
||||
|
||||
/**
|
||||
@@ -38,7 +43,7 @@ DIALOGUE = function(config) {
|
||||
'<div id="' + id + '" role="dialog" ' +
|
||||
'aria-labelledby="' + id + '-header-text" class="' + CSS_CLASSES.WRAP + '" aria-live="polite"></div>'
|
||||
)
|
||||
.append(Y.Node.create('<div id="' + id + '-header-text" class="' + CSS_CLASSES.HEADER + ' yui3-widget-hd"></div>'))
|
||||
.append(Y.Node.create('<div class="' + CSS_CLASSES.HEADER + ' yui3-widget-hd"></div>'))
|
||||
.append(Y.Node.create('<div class="' + CSS_CLASSES.BODY + ' yui3-widget-bd"></div>'))
|
||||
.append(Y.Node.create('<div class="' + CSS_CLASSES.FOOTER + ' yui3-widget-ft"></div>')));
|
||||
Y.one(document.body).append(config.notificationBase);
|
||||
@@ -103,6 +108,10 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
this.get('buttons').header[0].setAttribute('aria-label', title);
|
||||
}
|
||||
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h5 id="' + this.get('id') + '-wrap-header-text">' + this.get('headerContent') + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
|
||||
// Initialise the element cache.
|
||||
this._hiddenSiblings = [];
|
||||
|
||||
@@ -286,10 +295,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
Y.one(titlebar).setStyle('cursor', 'move');
|
||||
}
|
||||
}
|
||||
require(['core/local/aria/focuslock'], function(FocusLockManager) {
|
||||
// Trap focus to the current bounding box.
|
||||
FocusLockManager.trapFocus(this.get('boundingBox').getDOMNode());
|
||||
}.bind(this));
|
||||
|
||||
// Only do accessibility hiding for modals because the ARIA spec
|
||||
// says that all ARIA dialogues should be modal.
|
||||
@@ -380,9 +385,12 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
Math.floor(Y.one(document.body).get('winWidth')) < this.get('responsiveWidth');
|
||||
},
|
||||
|
||||
_focus: function() {
|
||||
this.focus();
|
||||
},
|
||||
|
||||
show: function() {
|
||||
var result = null,
|
||||
header = this.headerNode,
|
||||
content = this.bodyNode,
|
||||
focusSelector = this.get('focusOnShowSelector'),
|
||||
focusNode = null;
|
||||
@@ -399,16 +407,18 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
focusNode = this.get('boundingBox').one(focusSelector);
|
||||
}
|
||||
if (!focusNode) {
|
||||
// Fall back to the header or the content if no focus node was found yet.
|
||||
if (header && header !== '') {
|
||||
focusNode = header;
|
||||
} else if (content && content !== '') {
|
||||
focusNode = content;
|
||||
// Fall back to the first focusable element in the body of the dialogue if no focus node was found yet.
|
||||
if (content && content !== '') {
|
||||
focusNode = content.one(CAN_RECEIVE_FOCUS_SELECTOR);
|
||||
}
|
||||
}
|
||||
if (focusNode) {
|
||||
focusNode.focus();
|
||||
}
|
||||
require(['core/local/aria/focuslock'], function(FocusLockManager) {
|
||||
// Trap focus to the current bounding box.
|
||||
FocusLockManager.trapFocus(this.get('boundingBox').getDOMNode());
|
||||
if (focusNode) {
|
||||
focusNode.focus();
|
||||
}
|
||||
}.bind(this));
|
||||
return result;
|
||||
},
|
||||
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ Y.extend(EXCEPTION, M.core.notification.info, {
|
||||
labelsep = M.util.get_string('labelsep', 'langconfig');
|
||||
this.get(BASE).addClass('moodle-dialogue-exception');
|
||||
this.setStdModContent(Y.WidgetStdMod.HEADER,
|
||||
'<h1 id="moodle-dialogue-' + config.COUNT + '-header-text">' + Y.Escape.html(config.name) + '</h1>',
|
||||
'<h5 id="moodle-dialogue-' + this.get('COUNT') + '-wrap-header-text">' + Y.Escape.html(config.name) + '</h5>',
|
||||
Y.WidgetStdMod.REPLACE);
|
||||
content = Y.Node.create('<div class="moodle-exception" data-rel="fatalerror"></div>')
|
||||
.append(Y.Node.create('<div class="moodle-exception-message">' + Y.Escape.html(this.get('message')) + '</div>'))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user