(?:[^"]|\\")*)" scheduler and I fill the form with:$/
+ * phpcs:enable
*
* @param int $daysahead
* @param int $time
diff --git a/tests/behat/duplicate_slots.feature b/tests/behat/duplicate_slots.feature
new file mode 100644
index 00000000..c6138680
--- /dev/null
+++ b/tests/behat/duplicate_slots.feature
@@ -0,0 +1,54 @@
+@mod @mod_scheduler @duplicateslots
+Feature: Optionally copy slots when duplicating a Scheduler
+ In order to reuse a schedule without copying bookings
+ As a teacher
+ I can choose whether duplication includes existing slots
+
+ Background:
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | teacher1 | Teacher | One | teacher1@example.com |
+ | student1 | Student | One | student1@example.com |
+ And the following "courses" exist:
+ | fullname | shortname | category |
+ | Course 1 | C1 | 0 |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | teacher1 | C1 | editingteacher |
+ | student1 | C1 | student |
+ And the following "activities" exist:
+ | activity | name | intro | course | idnumber |
+ | scheduler | Test scheduler | n | C1 | scheduler1 |
+ And the following "mod_scheduler > slots" exist:
+ | scheduler | starttime | duration | teacher | location | student |
+ | scheduler1 | ##+5 days 10am## | 30 | teacher1 | Test office | student1 |
+
+ Scenario: Copy slots without copying bookings
+ Given I am on the "scheduler1" Activity page logged in as "teacher1"
+ And I should see "Test office" in the "slotmanager" "table"
+ And I should see "Student One" in the "slotmanager" "table"
+ When I navigate to "Settings" in current page administration
+ And I expand all fieldsets
+ And I set the field "copyslotsonduplicate" to "Yes"
+ And I press "Save and display"
+ And I am on the "C1" course page
+ And I turn editing mode on
+ And I duplicate "Test scheduler" activity
+ And I should see "Test scheduler (copy)"
+ And I follow "Test scheduler (copy)"
+ Then I should see "Test office" in the "slotmanager" "table"
+ And I should not see "Student One" in the "slotmanager" "table"
+
+ Scenario: Duplicate without copying slots
+ Given I am on the "scheduler1" Activity page logged in as "teacher1"
+ And I should see "Test office" in the "slotmanager" "table"
+ When I navigate to "Settings" in current page administration
+ And I expand all fieldsets
+ And I set the field "copyslotsonduplicate" to "No"
+ And I press "Save and display"
+ And I am on the "C1" course page
+ And I turn editing mode on
+ And I duplicate "Test scheduler" activity
+ And I should see "Test scheduler (copy)"
+ And I follow "Test scheduler (copy)"
+ Then "slotmanager" "table" should not exist
diff --git a/tests/behat/groupmode.feature b/tests/behat/groupmode.feature
index 21049857..b65d88e3 100644
--- a/tests/behat/groupmode.feature
+++ b/tests/behat/groupmode.feature
@@ -76,7 +76,7 @@ Feature: Users can only see their own groups if the scheduler is in group mode
Scenario: Editing teachers can see all slots and all groups
When I am on the "schedulerNone" Activity page logged in as "edteacher1"
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
Then I should see "Editingteacher 1" in the "slotmanager" "table"
And I should see "Nonedteacher 1" in the "slotmanager" "table"
@@ -86,7 +86,7 @@ Feature: Users can only see their own groups if the scheduler is in group mode
And I should see "Student 6" in the "studentstoschedule" "table"
When I am on the "schedulerVis" Activity page
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
Then I should see "Visible groups"
And the "Visible groups" select box should contain "All participants"
@@ -108,7 +108,7 @@ Feature: Users can only see their own groups if the scheduler is in group mode
And I should see "Nonedteacher 1" in the "slotmanager" "table"
When I am on the "schedulerSep" Activity page
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
Then I should see "Separate groups"
And the "Separate groups" select box should contain "All participants"
@@ -145,7 +145,7 @@ Feature: Users can only see their own groups if the scheduler is in group mode
# i.e., groups A and B.
# Students outside any group should not be visible.
When I am on the "schedulerSep" Activity page
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "My appointments"
Then I should see "Group mode: Separate groups"
And I should see "Only students in Group A, Group B can book"
@@ -158,7 +158,7 @@ Feature: Users can only see their own groups if the scheduler is in group mode
Scenario: Nonediting teachers can see groups only if allowed by the group mode
When I am on the "schedulerNone" Activity page logged in as neteacher1
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "My appointments"
Then I should see "6 students still need to make an appointment"
When I follow "All appointments"
@@ -166,7 +166,7 @@ Feature: Users can only see their own groups if the scheduler is in group mode
And I should see "Nonedteacher 1" in the "slotmanager" "table"
When I am on the "schedulerVis" Activity page
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "My appointments"
Then I should see "2 students still need to make an appointment"
When I follow "All appointments"
@@ -190,7 +190,7 @@ Feature: Users can only see their own groups if the scheduler is in group mode
And I should see "Nonedteacher 1" in the "slotmanager" "table"
When I am on the "schedulerSep" Activity page
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "My appointments"
Then I should see "2 students still need to make an appointment"
When I follow "All appointments"
@@ -218,7 +218,7 @@ Feature: Users can only see their own groups if the scheduler is in group mode
# i.e., group B (and C).
# Students in group 1 and outside any group should not be visible.
When I am on the "schedulerSep" Activity page
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "My appointments"
Then I should see "Group mode: Separate groups"
And I should see "Only students in Group B, Group C can book"
@@ -233,19 +233,19 @@ Feature: Users can only see their own groups if the scheduler is in group mode
# neteacher2 sees no students for scheduling in group mode, since he's not member of a group
When I am on the "schedulerNone" Activity page logged in as neteacher2
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "My appointments"
Then I should see "6 students still need to make an appointment"
When I am on the "schedulerVis" Activity page
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "My appointments"
Then I should see "No students available for scheduling"
And I should see "Group mode: Visible groups"
And I should see "students cannot book appointments with you"
When I am on the "schedulerSep" Activity page
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "My appointments"
Then I should see "No students available for scheduling"
And I should see "Group mode: Separate groups"
diff --git a/tests/behat/groupscheduling.feature b/tests/behat/groupscheduling.feature
index c8737341..2631dab6 100644
--- a/tests/behat/groupscheduling.feature
+++ b/tests/behat/groupscheduling.feature
@@ -1,4 +1,4 @@
-@mod @mod_scheduler
+@mod @mod_scheduler @groupscheduling
Feature: Entire groups can be booked into slots at once
In order to allow booking of entire groups
As a teacher
@@ -101,8 +101,8 @@ Feature: Entire groups can be booked into slots at once
And I should see "2 students still need to make an appointment"
And I should not see "Group A1" in the "groupstoschedule" "table"
And I should see "Group A2" in the "groupstoschedule" "table"
- And I should not see "Group B1" in the "groupstoschedule" "table"
- And I should not see "Group B2" in the "groupstoschedule" "table"
+ And I should see "Group B1" in the "groupstoschedule" "table"
+ And I should see "Group B2" in the "groupstoschedule" "table"
Scenario: Students can book their entire group into a slot
Given I am logged in as edteacher1
@@ -132,6 +132,96 @@ Feature: Entire groups can be booked into slots at once
And I should see "2 students still need to make an appointment"
And I should not see "Group A1" in the "groupstoschedule" "table"
And I should see "Group A2" in the "groupstoschedule" "table"
+ And I should see "Group B1" in the "groupstoschedule" "table"
+ And I should see "Group B2" in the "groupstoschedule" "table"
+ And I log out
+
+ Scenario: Overlapping groups can book separate appointments
+ Given I am logged in as edteacher1
+ And I add a slot 5 days ahead at 800 in "schedulerNone" scheduler and I fill the form with:
+ | Location | Large office |
+ | exclusivity | 5 |
+ And I add a slot 5 days ahead at 900 in "schedulerNone" scheduler and I fill the form with:
+ | Location | Large office |
+ | exclusivity | 5 |
+ And I log out
+
+ When I am on the "schedulerNone" Activity page logged in as student1
+ And I select "Group A1" from the "appointgroup" singleselect
+ And I click on "Book slot" "button" in the "8:00 AM" "table_row"
+ Then I should see "Large office" in the "8:00 AM" "table_row"
+
+ When I select "Group B1" from the "appointgroup" singleselect
+ Then "Book slot" "button" should exist in the "9:00 AM" "table_row"
+ When I click on "Book slot" "button" in the "9:00 AM" "table_row"
+ Then I should see "Large office" in the "9:00 AM" "table_row"
+ And I should see "Large office" in the "8:00 AM" "table_row"
+ And I should see "You cannot book further appointments in this scheduler."
+
+ When I select "Myself" from the "appointgroup" singleselect
+ Then I should see "You cannot book further appointments in this scheduler."
+ And "Book slot" "button" should not exist
+
+ When I select "Group A1" from the "appointgroup" singleselect
+ And I click on "Cancel booking" "button" in the "8:00 AM" "table_row"
+ And I log out
+
+ When I am on the "schedulerNone" Activity page logged in as edteacher1
+ Then I should see "Group A1" in the "groupstoschedule" "table"
And I should not see "Group B1" in the "groupstoschedule" "table"
- And I should not see "Group B2" in the "groupstoschedule" "table"
+ And I should not see "Student 1" in the "8:00 AM" "table_row"
+ And I should not see "Student 2" in the "8:00 AM" "table_row"
+ And I should see "Student 1" in the "9:00 AM" "table_row"
+ And I should see "Student 3" in the "9:00 AM" "table_row"
+
+ Scenario: Group bookings turned off should should not show appoint group selection to students
+ Given I am logged in as edteacher1
+ And I am on the "schedulerNone" Activity page logged in as edteacher1
+ And I navigate to "Settings" in current page administration
+ And I set the following fields to these values:
+ | Booking in groups | No |
+ And I click on "Save and return to course" "button"
+ And I add 8 slots 5 days ahead in "schedulerNone" scheduler and I fill the form with:
+ | Location | Large office |
+ | exclusivity | 5 |
+ And I log out
+
+ When I am on the "schedulerNone" Activity page logged in as student1
+ Then "select[name='appointgroup']" "css_element" should not exist
+ And "Book slot" "button" should exist
+
+ Scenario: Group bookings allowed and allowed booking type is "individual and group bookings" should show "Myself" and group selection to students
+ Given I am logged in as edteacher1
+ And I am on the "schedulerNone" Activity page logged in as edteacher1
+ And I navigate to "Settings" in current page administration
+ And I set the following fields to these values:
+ | Booking in groups | Yes, for all groups |
+ | Allowed booking type | Individual and group bookings |
+ And I click on "Save and return to course" "button"
+ And I add 8 slots 5 days ahead in "schedulerNone" scheduler and I fill the form with:
+ | Location | Large office |
+ | exclusivity | 5 |
+ And I log out
+
+ When I am on the "schedulerNone" Activity page logged in as student1
+ Then the "appointgroup" select box should contain "Myself"
+ And the "appointgroup" select box should contain "Group A1"
+ And the "appointgroup" select box should contain "Group B1"
+
+ Scenario: Group bookings allowed and allowed booking type is "group bookings only" should show group selection to students but not "Myself"
+ Given I am logged in as edteacher1
+ And I am on the "schedulerNone" Activity page logged in as edteacher1
+ And I navigate to "Settings" in current page administration
+ And I set the following fields to these values:
+ | Booking in groups | Yes, for all groups |
+ | Allowed booking type | Group bookings only |
+ And I click on "Save and return to course" "button"
+ And I add 8 slots 5 days ahead in "schedulerNone" scheduler and I fill the form with:
+ | Location | Large office |
+ | exclusivity | 5 |
And I log out
+
+ When I am on the "schedulerNone" Activity page logged in as student1
+ Then the "appointgroup" select box should not contain "Myself"
+ And the "appointgroup" select box should contain "Group A1"
+ And the "appointgroup" select box should contain "Group B1"
diff --git a/tests/behat/notes.feature b/tests/behat/notes.feature
index 0c68ddec..f6c812ca 100644
--- a/tests/behat/notes.feature
+++ b/tests/behat/notes.feature
@@ -29,7 +29,7 @@ Feature: Teachers can write notes on slots and appointments
Scenario: Teachers can enter slot notes and appointment notes for others to see
When I am on the "scheduler1" Activity page logged in as "edteacher1"
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
And I click on "Edit" "link" in the "4:00 AM" "table_row"
And I set the following fields to these values:
@@ -47,7 +47,7 @@ Feature: Teachers can write notes on slots and appointments
And I log out
When I am on the "scheduler1" Activity page logged in as "edteacher1"
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
And I click on "Student 1" "text" in the "4:00 AM" "table_row"
Then I should see ", 4:00 AM" in the "Date and time" "table_row"
@@ -76,7 +76,7 @@ Feature: Teachers can write notes on slots and appointments
And I log out
When I am on the "scheduler1" Activity page logged in as "edteacher1"
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
And I click on "Student 1" "text" in the "4:00 AM" "table_row"
And I set the following fields to these values:
diff --git a/tests/behat/overview_report.feature b/tests/behat/overview_report.feature
new file mode 100644
index 00000000..024d37fb
--- /dev/null
+++ b/tests/behat/overview_report.feature
@@ -0,0 +1,97 @@
+@javascript @mod @mod_scheduler @overview_report
+Feature: Testing overview integration in scheduler activity
+ In order to summarize the scheduler activity
+ As a user
+ I need to be able to see the scheduler activity overview
+
+ Background:
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | manager1 | Manager | 1 | manager1@example.com |
+ | teacher1 | Teacher | 1 | teacher1@example.com |
+ | student1 | Student | 1 | student1@example.com |
+ | student2 | Student | 2 | student2@example.com |
+ | student3 | Student | 3 | student3@example.com |
+ And the following "courses" exist:
+ | fullname | shortname | category |
+ | Course 1 | C1 | 0 |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | teacher1 | C1 | editingteacher |
+ | student1 | C1 | student |
+ | student2 | C1 | student |
+ | student3 | C1 | student |
+ And the following "groups" exist:
+ | name | course | idnumber |
+ | Group 1 | C1 | G1 |
+ | Group 2 | C1 | G2 |
+ And the following "group members" exist:
+ | user | group |
+ | student1 | G1 |
+ | student2 | G2 |
+ And the following "groupings" exist:
+ | name | course | idnumber |
+ | Grouping 1 | C1 | GG1 |
+ And the following "grouping groups" exist:
+ | grouping | group |
+ | GG1 | G1 |
+ And the following "system role assigns" exist:
+ | user | role |
+ | manager1 | manager |
+ And the following "activities" exist:
+ | activity | name | intro | course | idnumber | groupmode | schedulermode | maxbookings | guardtime |
+ | scheduler | Test scheduler | n | C1 | scheduler1 | 0 | oneonly | 1 | 172800 |
+ And the following "mod_scheduler > slots" exist:
+ | scheduler | starttime | duration | teacher | exclusivity | student | hideuntil |
+ # Slot 1 is available to only 1 student and is not yet booked
+ | scheduler1 | ##+5 days 1:00am## | 45 | teacher1 | 1 | | |
+ # Slot 2 is available to only 1 student and is already booked
+ | scheduler1 | ##+5 days 2:00am## | 45 | teacher1 | 1 | student3 | |
+ # Slot 3 is a group slot that is empty
+ | scheduler1 | ##+5 days 3:00am## | 45 | teacher1 | 3 | | |
+ # Slot 4 is a group slot that is partially booked
+ | scheduler1 | ##+5 days 4:00am## | 45 | teacher1 | 2 | student3 | |
+ # Slot 5 is an unlimited group slot that is empty
+ | scheduler1 | ##+5 days 5:00am## | 45 | teacher1 | 0 | | |
+ # Slot 6 is an unlimited group slot that is partially booked
+ | scheduler1 | ##+5 days 6:00am## | 45 | teacher1 | 0 | student3 | |
+ # Slot 7 is not yet available to students
+ | scheduler1 | ##+5 days 7:00am## | 45 | teacher1 | 0 | | ##now +2years## |
+ # Slot 8 is no longer available since the it's too close in the future
+ | scheduler1 | ##tomorrow 8:00am## | 45 | teacher1 | 0 | | |
+
+ Scenario: The Scheduler activity index redirect to the activities overview
+ Given the site is running Moodle version 5.0 or higher
+ When I am on the "C1" "course > activities > scheduler" page logged in as "admin"
+ Then I should see "Name" in the "scheduler_overview_collapsible" "region"
+ And I should see "Actions" in the "scheduler_overview_collapsible" "region"
+ And I should see "Test scheduler"
+
+ Scenario: View a group scheduler in the activities overview
+ Given the site is running Moodle version 5.0 or higher
+ And the following "activities" exist:
+ | activity | name | intro | course | idnumber | groupmode |
+ | scheduler | Group test scheduler | n | C1 | schedulerVis | 2 |
+ And I log in as "teacher1"
+ And I add 5 slots 11 days ahead in "schedulerVis" scheduler and I fill the form with:
+ | Location | Here |
+ When I am on the "C1" "course > activities > scheduler" page logged in as "admin"
+ Then I should see "An overview of all activities in the course"
+ And I should see "Name" in the "scheduler_overview_collapsible" "region"
+ And I should see "Actions" in the "scheduler_overview_collapsible" "region"
+ And I should see "Group test scheduler"
+
+ Scenario: Use the selected grouping in the activities overview
+ Given the site is running Moodle version 5.0 or higher
+ And the following config values are set as admin:
+ | groupscheduling | 1 | mod_scheduler |
+ And the following "activities" exist:
+ | activity | name | intro | course | idnumber | groupmode |
+ | scheduler | Group test scheduler | n | C1 | schedulerVis | 0 |
+ And I am on the "schedulerVis" "scheduler activity editing" page logged in as "admin"
+ And I set the field "Booking in groups" to "Yes, in grouping Grouping 1"
+ And I press "Save and return to course"
+ When I am on the "C1" "course > activities > scheduler" page
+ Then I should see "Group test scheduler"
+ And I should see "1 of 1 groups"
+ And I should not see "2 of 2 groups"
diff --git a/tests/behat/studentdata.feature b/tests/behat/studentdata.feature
index 2330caf6..8f409c8c 100644
--- a/tests/behat/studentdata.feature
+++ b/tests/behat/studentdata.feature
@@ -62,7 +62,7 @@ Feature: Student-supplied data
And I log out
When I am on the "scheduler1" Activity page logged in as "teacher1"
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "My appointments"
Then I should see "Student 1" in the "3:00 AM" "table_row"
And I should see "Student 2" in the "4:00 AM" "table_row"
diff --git a/tests/behat/teacherpermissions.feature b/tests/behat/teacherpermissions.feature
index 5fa44025..6fb24905 100644
--- a/tests/behat/teacherpermissions.feature
+++ b/tests/behat/teacherpermissions.feature
@@ -38,7 +38,7 @@ Feature: Teachers can edit other teacher's appointments only by permission
Scenario: Editing teachers edit all appointments, nonediting teachers only their own
When I am on the "scheduler1" Activity page logged in as "edteacher1"
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
Then I should see "Student 1" in the "3:00 AM" "table_row"
And "seen[]" "checkbox" should exist in the "3:00 AM" "table_row"
@@ -58,7 +58,7 @@ Feature: Teachers can edit other teacher's appointments only by permission
And I log out
When I am on the "scheduler1" Activity page logged in as "neteacher1"
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
Then I should see "Student 1" in the "3:00 AM" "table_row"
And "seen[]" "checkbox" should not exist in the "3:00 AM" "table_row"
@@ -76,7 +76,7 @@ Feature: Teachers can edit other teacher's appointments only by permission
Then the field "Attended" matches value "1"
When I am on the "scheduler1" Activity page
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
And I click on "Student 3" "text" in the "5:00 AM" "table_row"
Then the "Attended" "checkbox" should be disabled
@@ -93,7 +93,7 @@ Feature: Teachers can edit other teacher's appointments only by permission
And I log out
When I am on the "scheduler1" Activity page logged in as "neteacher1"
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
Then I should see "Student 1" in the "3:00 AM" "table_row"
And "seen[]" "checkbox" should exist in the "3:00 AM" "table_row"
@@ -109,7 +109,7 @@ Feature: Teachers can edit other teacher's appointments only by permission
Then the field "Attended" matches value "1"
When I am on the "scheduler1" Activity page
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
When I click on "Student 3" "text" in the "5:00 AM" "table_row"
Then the "Attended" "checkbox" should be enabled
@@ -130,7 +130,7 @@ Feature: Teachers can edit other teacher's appointments only by permission
And I log out
When I am on the "scheduler1" Activity page logged in as "neteacher1"
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
Then I should see "Student 1" in the "3:00 AM" "table_row"
And "seen[]" "checkbox" should not exist in the "3:00 AM" "table_row"
@@ -146,7 +146,7 @@ Feature: Teachers can edit other teacher's appointments only by permission
Then the field "Grade" matches value "42"
When I am on the "scheduler1" Activity page
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
And I click on "Student 3" "text" in the "5:00 AM" "table_row"
Then the "grade" "field" should be enabled
@@ -168,7 +168,7 @@ Feature: Teachers can edit other teacher's appointments only by permission
And I log out
When I am on the "scheduler1" Activity page logged in as "neteacher1"
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
Then I should see "Student 1" in the "3:00 AM" "table_row"
And "seen[]" "checkbox" should not exist in the "3:00 AM" "table_row"
@@ -187,7 +187,7 @@ Feature: Teachers can edit other teacher's appointments only by permission
And I should see "notes-confid"
When I am on the "scheduler1" Activity page
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "All appointments"
And I click on "Student 3" "text" in the "5:00 AM" "table_row"
Then "grade" "field" should not exist
diff --git a/tests/behat/tutorappointments.feature b/tests/behat/tutorappointments.feature
index 12d908b4..c8d02627 100644
--- a/tests/behat/tutorappointments.feature
+++ b/tests/behat/tutorappointments.feature
@@ -184,7 +184,7 @@ Feature: Booking of appointments with individual tutors per group
And I log out
When I am on the "scheduler1" Activity page logged in as "manager1"
- And I follow "Statistics"
+ And I follow "Overview"
And I follow "My appointments"
Then "slotmanager" "table" should not exist
And I should see "No students available for scheduling"
diff --git a/tests/behat/viewslots.feature b/tests/behat/viewslots.feature
index f5cfe1a0..219f7818 100644
--- a/tests/behat/viewslots.feature
+++ b/tests/behat/viewslots.feature
@@ -14,7 +14,7 @@ Feature: Students viewing slots available for booking
| student3 | Student | 3 | student3@example.com |
And the following "courses" exist:
| fullname | shortname | category |
- | Course 1 | C1 | 0 |
+ | Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
@@ -148,3 +148,33 @@ Feature: Students viewing slots available for booking
And I should not see "8:00 AM" in the "slotbookertable" "table"
And I log out
+
+ Scenario: Book slot button appears for student only when meeting was booked
+ Given I am on the "scheduler1" Activity page logged in as teacher1
+ And I click on "Add slots" "link"
+ And I follow "Add single slot"
+ And I set the following fields to these values:
+ | starttime[day] | 1 |
+ | starttime[month] | April |
+ | starttime[year] | 2050 |
+ | duration | 30 |
+ | Location | Online test meeting |
+ | Meeting link | https://example.com/test-meeting |
+ When I click on "Save changes" "button"
+ Then I should see "1 slot added"
+ And "Join online meeting" "link" should exist
+ And I log out
+
+ When I am on the "scheduler1" Activity page logged in as "student1"
+ Then I should see "Online test meeting"
+ And "Join online meeting" "link" should not exist
+
+ When I click on "Book slot" "button" in the "Online test meeting" "table_row"
+ Then "Cancel booking" "button" should exist
+ And "Join online meeting" "link" should exist
+ And "a[href='https://example.com/test-meeting']" "css_element" should exist
+
+ When I click on "Cancel booking" "button"
+ Then "Book slot" "button" should exist in the "Online test meeting" "table_row"
+ And "Join online meeting" "link" should not exist
+ And I log out
diff --git a/tests/scheduler_test.php b/tests/scheduler_test.php
index 8cf646e3..532e6fea 100644
--- a/tests/scheduler_test.php
+++ b/tests/scheduler_test.php
@@ -565,4 +565,103 @@ public function test_bookable_appointments(): void {
$sid = $this->create_data_for_bookable_appointments('onetime', 1, 5 * DAYSECS, $studid, [2], []);
$this->assert_bookable_appointments(0, 0, $sid, $studid);
}
+
+ /**
+ * Tests group booking detection for overlapping groups.
+ *
+ * @return void
+ * @throws \coding_exception
+ * @covers \mod_scheduler\model\scheduler::has_slots_booked_for_group
+ * @covers \mod_scheduler\model\scheduler::get_groups_for_scheduling
+ */
+ public function test_has_slots_booked_for_group(): void {
+ $student1 = $this->create_student();
+ $student2 = $this->create_student();
+
+ $groupa = $this->getDataGenerator()->create_group([
+ 'courseid' => $this->courseid,
+ 'name' => 'Group A',
+ ]);
+ $groupb = $this->getDataGenerator()->create_group([
+ 'courseid' => $this->courseid,
+ 'name' => 'Group B',
+ ]);
+
+ // Student 1 belongs to both groups.
+ $this->getDataGenerator()->create_group_member([
+ 'groupid' => $groupa->id,
+ 'userid' => $student1,
+ ]);
+ $this->getDataGenerator()->create_group_member([
+ 'groupid' => $groupb->id,
+ 'userid' => $student1,
+ ]);
+
+ // Student 2 only to one.
+ $this->getDataGenerator()->create_group_member([
+ 'groupid' => $groupa->id,
+ 'userid' => $student2,
+ ]);
+
+ $scheduler = scheduler::load_by_id($this->schedulerid);
+ $scheduler->bookingrouping = 0;
+ $scheduler->save();
+ $teacherid = $this->getDataGenerator()->create_user()->id;
+
+ // Individual booking does not count as a booking for groups.
+ $individualslot = $scheduler->create_slot();
+ $individualslot->teacherid = $teacherid;
+ $individualslot->starttime = time() + DAYSECS;
+ $individualslot->duration = 10;
+
+ $individualappointment = $individualslot->create_appointment();
+ $individualappointment->studentid = $student1;
+ $individualslot->save();
+
+ $this->assertFalse($scheduler->has_slots_booked_for_group($groupa->id));
+ $this->assertFalse($scheduler->has_slots_booked_for_group($groupb->id));
+
+ $groupsforscheduling = $scheduler->get_groups_for_scheduling();
+ $this->assertArrayHasKey($groupa->id, $groupsforscheduling);
+ $this->assertArrayHasKey($groupb->id, $groupsforscheduling);
+
+ // Book Group A.
+ $groupslot = $scheduler->create_slot();
+ $groupslot->teacherid = $teacherid;
+ $groupslot->starttime = time() + 2 * DAYSECS;
+ $groupslot->duration = 10;
+
+ foreach ([$student1, $student2 ] as $studentid) {
+ $appointment = $groupslot->create_appointment();
+ $appointment->studentid = $studentid;
+ $appointment->bookinggroupid = $groupa->id;
+ }
+
+ $groupslot->save();
+
+ $groupsforscheduling = $scheduler->get_groups_for_scheduling();
+ $this->assertArrayNotHasKey($groupa->id, $groupsforscheduling);
+ $this->assertArrayHasKey($groupb->id, $groupsforscheduling);
+
+ $this->assertTrue($scheduler->has_slots_booked_for_group($groupa->id));
+ $this->assertFalse($scheduler->has_slots_booked_for_group($groupb->id));
+
+ // Book group B separately.
+ $secondgroupslot = $scheduler->create_slot();
+ $secondgroupslot->teacherid = $teacherid;
+ $secondgroupslot->starttime = time() + 3 * DAYSECS;
+ $secondgroupslot->duration = 10;
+
+ $appointment = $secondgroupslot->create_appointment();
+ $appointment->studentid = $student1;
+ $appointment->bookinggroupid = $groupb->id;
+ $secondgroupslot->save();
+
+ $groupsforscheduling = $scheduler->get_groups_for_scheduling();
+ $this->assertArrayNotHasKey($groupa->id, $groupsforscheduling);
+ $this->assertArrayNotHasKey($groupb->id, $groupsforscheduling);
+
+ $this->assertTrue($scheduler->has_slots_booked_for_group($groupa->id));
+ $this->assertTrue($scheduler->has_slots_booked_for_group($groupb->id));
+ }
}
diff --git a/tests/slot_test.php b/tests/slot_test.php
index 36e73796..5af00395 100644
--- a/tests/slot_test.php
+++ b/tests/slot_test.php
@@ -294,6 +294,204 @@ public function test_calendar_events(): void {
$this->assert_event_absent($this->teacherid, $newstart);
}
+ /**
+ * Test that attended appointments are removed from calendars by default.
+ *
+ * @covers \mod_scheduler\model\scheduler::load_by_id
+ */
+ public function test_attended_appointments_are_removed_from_calendar_by_default(): void {
+
+ set_config('keepattendedincalendar', 0, 'mod_scheduler');
+
+ $scheduler = scheduler::load_by_id($this->schedulerid);
+ $slot = slot::load_by_id($this->slotid, $scheduler);
+
+ $appointment = $slot->get_appointment($this->appointmentids[0]);
+ $appointment->attended = 1;
+ $slot->save();
+
+ $this->assert_event_absent($this->students[0], $slot->starttime);
+ }
+
+ /**
+ * Test that attended appointments remain visible in calendars.
+ *
+ * @covers \mod_scheduler\model\scheduler::load_by_id
+ */
+ public function test_attended_appointments_keep_calendar_events(): void {
+
+ set_config('keepattendedincalendar', 1, 'mod_scheduler');
+
+ $scheduler = scheduler::load_by_id($this->schedulerid);
+ $slot = slot::load_by_id($this->slotid, $scheduler);
+
+ $appointment = $slot->get_appointment($this->appointmentids[0]);
+ $appointment->attended = 1;
+ $slot->save();
+
+ $this->assert_event_exists($this->students[0], $slot->starttime, "Meeting with your Teacher");
+ $this->assert_event_exists($this->teacherid, $slot->starttime, "Meeting with your Students");
+ }
+
+ /**
+ * Test creating and reusing a course group for a slot.
+ *
+ * @return void
+ * @throws \dml_exception
+ * @throws \moodle_exception
+ * @covers ::scheduler_create_coursegroup
+ */
+ public function test_creates_coursegroup(): void {
+ global $DB;
+ $this->setAdminUser();
+ $scheduler = scheduler::load_by_id($this->schedulerid);
+ $slot = slot::load_by_id($this->slotid, $scheduler);
+
+ // Create a group.
+ $groupid = \scheduler_create_coursegroup($slot);
+ $group = $DB->get_record('groups', ['id' => $groupid], '*', MUST_EXIST);
+
+ $this->assertEquals($this->courseid, $group->courseid);
+ $this->assertNotEmpty($group->name);
+ $this->assertEquals($groupid, $slot->coursegroupid);
+ $this->assertEquals($groupid, $DB->get_field('scheduler_slots', 'coursegroupid', ['id' => $this->slotid]));
+ $this->assertEquals(1, $DB->count_records('groups', ['courseid' => $this->courseid]));
+
+ // Rename the group.
+ $group->name = 'Renamed group';
+ \groups_update_group($group);
+
+ $slot = slot::load_by_id($this->slotid, $scheduler);
+ $reuseid = \scheduler_create_coursegroup($slot);
+
+ $this->assertEquals($groupid, $reuseid);
+ $this->assertEquals(1, $DB->count_records('groups', ['courseid' => $this->courseid]));
+ $this->assertEquals('Renamed group', $DB->get_field('groups', 'name', ['id' => $groupid]));
+ }
+
+ /**
+ * Cancellation removes a member and slot deletion should preserve the group.
+ *
+ * @return void
+ * @throws \dml_exception
+ * @covers \mod_scheduler\course_group_helper::add_booked_students
+ * @covers \mod_scheduler\course_group_helper::remove_booked_students
+ * @covers \mod_scheduler\event\booking_added::create_from_slot
+ * @covers \mod_scheduler\event\booking_removed::create_from_slot
+ * @covers \mod_scheduler\observer::booking_added
+ * @covers \mod_scheduler\observer::booking_removed
+ * @covers \mod_scheduler\model\slot::delete
+ */
+ public function test_cancellation_and_slot_deletion_group_memberships(): void {
+ global $DB;
+
+ $this->setAdminUser();
+
+ foreach ($this->students as $studentid) {
+ $this->getDataGenerator()->enrol_user($studentid, $this->courseid, 'student');
+ }
+
+ $scheduler = scheduler::load_by_id($this->schedulerid);
+ $scheduler->groupcreation = 2;
+ $scheduler->save();
+
+ $slot = slot::load_by_id($this->slotid, $scheduler);
+ $slot->exclusivity = 3;
+ $slot->save();
+
+ \mod_scheduler\event\booking_added::create_from_slot($slot)->trigger();
+ $groupid = $DB->get_field('scheduler_slots', 'coursegroupid', ['id' => $this->slotid], MUST_EXIST);
+
+ foreach ($this->students as $studentid) {
+ $this->assertTrue(\groups_is_member($groupid, $studentid));
+ }
+
+ // Cancel the first student's appointment.
+ $appointment = $slot->get_student_appointment($this->students[0]);
+ $appointmentdata = $appointment->get_data();
+
+ $slot->remove_appointment($appointment);
+ $slot->save();
+
+ \mod_scheduler\event\booking_removed::create_from_slot($slot, $appointmentdata)->trigger();
+
+ $this->assertFalse(\groups_is_member($groupid, $this->students[0]));
+ $this->assertTrue(\groups_is_member($groupid, $this->students[1]));
+ $this->assertTrue(\groups_is_member($groupid, $this->students[2]));
+
+ // Deleting slot must preserve group and remaining members.
+
+ $slot->delete();
+ $this->assertFalse($DB->record_exists('scheduler_slots', ['id' => $this->slotid]));
+ $this->assertTrue($DB->record_exists('groups', ['id' => $groupid]));
+ $this->assertTrue(\groups_is_member($groupid, $this->students[1]));
+ $this->assertTrue(\groups_is_member($groupid, $this->students[2]));
+ }
+
+ /**
+ * Tests creating a course group when a group slot is added.
+ *
+ * @return void
+ * @throws \coding_exception
+ * @throws \dml_exception
+ * @covers ::scheduler_create_coursegroup
+ * @covers \mod_scheduler\event\slot_added::create_from_slot
+ * @covers \mod_scheduler\observer::slot_added
+ */
+ public function test_slot_added_event_creates_coursegroup(): void {
+ global $DB;
+
+ $this->setAdminUser();
+ $scheduler = scheduler::load_by_id($this->schedulerid);
+ $scheduler->groupcreation = 1;
+ $scheduler->save();
+
+ $slot = slot::load_by_id($this->slotid, $scheduler);
+ $slot->exclusivity = 3;
+ $slot->save();
+
+ \mod_scheduler\event\slot_added::create_from_slot($slot)->trigger();
+
+ $groupid = $DB->get_field('scheduler_slots', 'coursegroupid', ['id' => $this->slotid], MUST_EXIST);
+
+ $this->assertNotEmpty($groupid);
+ $this->assertTrue($DB->record_exists('groups', ['id' => $groupid, 'courseid' => $this->courseid]));
+ }
+
+ /**
+ * Tests that an existing group booking does not create another course group.
+ *
+ * @return void
+ * @throws \coding_exception
+ * @throws \dml_exception
+ * @covers \mod_scheduler\course_group_helper::add_booked_students
+ * @covers \mod_scheduler\event\booking_added::create_from_slot
+ * @covers \mod_scheduler\observer::booking_added
+ */
+ public function test_group_bookings_does_not_create_coursegroup(): void {
+ global $DB;
+
+ $this->setAdminUser();
+ $scheduler = scheduler::load_by_id($this->schedulerid);
+ $scheduler->groupcreation = 2;
+ $scheduler->save();
+
+ $slot = slot::load_by_id($this->slotid, $scheduler);
+ $slot->exclusivity = 3;
+ $slot->save();
+
+ $bookedgroup = $this->getDataGenerator()->create_group(['courseid' => $this->courseid]);
+
+ $DB->set_field('scheduler_appointment', 'bookinggroupid', $bookedgroup->id, ['id' => $this->slotid]);
+
+ \mod_scheduler\event\slot_added::create_from_slot($slot)->trigger();
+
+ $coursegroupid = $DB->get_field('scheduler_slots', 'coursegroupid', ['id' => $this->slotid], MUST_EXIST);
+
+ $this->assertEquals(0, $coursegroupid);
+ $this->assertEquals(1, $DB->count_records('groups', ['courseid' => $this->courseid]));
+ }
+
/**
* Assert that a calendar event exists in the DB.
*
diff --git a/version.php b/version.php
index d8c555c6..f871c06d 100644
--- a/version.php
+++ b/version.php
@@ -29,7 +29,7 @@
*/
$plugin->component = 'mod_scheduler'; // Full name of the plugin (used for diagnostics).
-$plugin->version = 2026080400; // The current module version (Date: YYYYMMDDXX).
+$plugin->version = 2026102300; // The current module version (Date: YYYYMMDDXX).
$plugin->release = 'v5.2-r1';
$plugin->requires = 2024100712; // Requires Moodle 4.5.
$plugin->supported = [500, 502];
diff --git a/view.php b/view.php
index e2d6b0fa..9a148640 100644
--- a/view.php
+++ b/view.php
@@ -64,6 +64,13 @@
$subpage = optional_param('subpage', $defaultsubpage, PARAM_ALPHA);
+if (
+ $action === 'export' ||
+ $action === 'viewstatistics' ||
+ $action === 'addsession'
+) {
+ $PAGE->add_body_class('limitedwidth');
+}
// Print the page header.
diff --git a/viewstatistics.php b/viewstatistics.php
index 6fb8165f..8d1a4e5c 100644
--- a/viewstatistics.php
+++ b/viewstatistics.php
@@ -121,17 +121,17 @@ function byname($a, $b) {
$allattendees = ($attendees) ? count($attendees) : 0;
$str = '' . get_string('attendable', 'scheduler') . '
';
- $str .= '' . get_string('attendablelbl', 'scheduler') . ': ' . $allattendees . '
';
+ $str .= '' . get_string('attendablelbl', 'scheduler') . ': ' . $allattendees . '
';
$str .= '' . get_string('attended', 'scheduler') . '
';
- $str .= '' . get_string('attendedlbl', 'scheduler') . ': ' . $attended . '
';
+ $str .= '' . get_string('attendedlbl', 'scheduler') . ': ' . $attended . '
';
$str .= '' . get_string('unattended', 'scheduler') . '
';
- $str .= '' . get_string('registeredlbl', 'scheduler') . ': ' . $registered . '
';
+ $str .= '' . get_string('registeredlbl', 'scheduler') . ': ' . $registered . '
';
$str .= '' . get_string('unregisteredlbl', 'scheduler') . ': ' .
- ($allattendees - $registered - $attended) . '
';
+ ($allattendees - $registered - $attended) . '
';
$str .= '' . get_string('availableslots', 'scheduler') . '
';
- $str .= '' . get_string('availableslotsowned', 'scheduler') . ': ' . $freeowned . '
';
+ $str .= '' . get_string('availableslotsowned', 'scheduler') . ': ' . $freeowned . '
';
$str .= '' . get_string('availableslotsnotowned', 'scheduler') . ': ' . $freenotowned . '
';
- $str .= '' . get_string('availableslotsall', 'scheduler') . ': ' . ($freeowned + $freenotowned) . '
';
+ $str .= '' . get_string('availableslotsall', 'scheduler') . ': ' . ($freeowned + $freenotowned) . '
';
echo $OUTPUT->box($str);
@@ -141,6 +141,7 @@ function byname($a, $b) {
if (!empty($attendees)) {
$table = new html_table();
+ $table->attributes['class'] = 'table generaltable table-reboot table-hover table-striped';
$table->head = [get_string('student', 'scheduler'), get_string('duration', 'scheduler')];
$table->align = ['LEFT', 'CENTER'];
$table->width = '70%';
@@ -193,6 +194,7 @@ function byname($a, $b) {
$sql .= " GROUP BY s.teacherid";
if ($statrecords = $DB->get_records_sql($sql, $params)) {
$table = new html_table();
+ $table->attributes['class'] = 'table generaltable table-reboot table-hover table-striped';
$table->width = '70%';
$table->head = [s($scheduler->get_teacher_name()), get_string('cumulatedduration', 'scheduler')];
$table->align = ['LEFT', 'CENTER'];
@@ -230,6 +232,7 @@ function byname($a, $b) {
$sql .= " GROUP BY s.starttime ORDER BY groupsize DESC";
if ($groupslots = $DB->get_records_sql($sql, $params)) {
$table = new html_table();
+ $table->attributes['class'] = 'table generaltable table-reboot table-hover table-striped';
$table->head = [get_string('duration', 'scheduler'), get_string('appointments', 'scheduler')];
$table->align = ['LEFT', 'CENTER'];
$table->width = '70%';
@@ -276,6 +279,7 @@ function byname($a, $b) {
ORDER BY groupsize DESC";
if ($groupslots = $DB->get_records_sql($sql, $params)) {
$table = new html_table();
+ $table->attributes['class'] = 'table generaltable table-reboot table-hover table-striped';
$table->head = [get_string('groupsize', 'scheduler'), get_string('occurrences', 'scheduler'),
get_string('cumulatedduration', 'scheduler'), ];
$table->align = ['LEFT', 'CENTER', 'CENTER'];