Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
d8b83cd
fix mod savepoint
May 28, 2026
fd4bd35
Sync with Learnweb's centralized pull request template
github-actions[bot] Jul 31, 2026
caee5c6
Activity information for Moodle 5.2 activity chooser
lucaboesch Jan 11, 2026
a0a4a78
Support course overview, resolves #146.
lucaboesch Dec 28, 2025
3067d80
Tint monologo icon with dark grey as standard.
lucaboesch Apr 14, 2025
4ad7d22
Make some Scheduler layouts more beautiful.
lucaboesch Jul 26, 2026
f5037fb
Keep attended appointments in calendar events
jochen-hanisch Aug 5, 2026
678e466
Merge branch 'pr-80' into dev-branch-merge-prs
Aug 14, 2026
8c05024
Merge pr-82: Make some scheduler layouts more beautiful
Aug 14, 2026
74dccf1
Merge PR 76: Add activity information
Aug 14, 2026
6284a9a
Minimal adjustment of wording to be broader
Aug 14, 2026
b4d9c0a
Remove the limited width again, because it makes the tab placement ju…
Aug 14, 2026
0d54526
more space between checkbox and student name
Aug 16, 2026
175ded3
remove stale var from PR #62
Aug 16, 2026
fae0893
add new line. Resolves PR #66
Aug 17, 2026
249cda5
Merge PR #63: Monologo icon change
Aug 17, 2026
59ecf8a
Merge PR #83: Keep attended appointments in calendar events- #83
Aug 18, 2026
5b16216
Making the change from PR #83 optional in settings
Aug 18, 2026
8b7ead0
Merge PR 70: Support course overview
Aug 18, 2026
c220aa5
fix the course overview for scheduler. Use bookingrouping instead of …
Aug 18, 2026
357b226
Add test for overview fix
Aug 18, 2026
e0b90c1
Revert the changes for the styling because they will be addressed in …
Aug 18, 2026
8b8c031
Merge PR 73
Aug 18, 2026
034f391
codechecker
Aug 18, 2026
3e7f5d6
Make some Scheduler layouts more beautiful.
lucaboesch Jul 26, 2026
e18cd9b
Merge changes from #82
Aug 26, 2026
38cd7af
Update the db. Add field bookinggroupid.
Sep 23, 2026
8005d89
Store the booking group on appointments
Sep 23, 2026
9b5e393
Handle overlapping group booking independently
Sep 23, 2026
e0e4fd2
Backup preservation
Sep 23, 2026
96bc149
Testing
Sep 23, 2026
77f8c91
Fix indentation.
Sep 23, 2026
001c90f
phpcs fixes
Oct 1, 2026
7b47efa
db update and mod form
Oct 1, 2026
7906740
add automatic course group creation and membership synch
Oct 8, 2026
052f1e4
test
Oct 8, 2026
4c1a3d9
backup
Oct 8, 2026
4c8eeeb
phpcs
Oct 8, 2026
ed3b903
update parameter list
Oct 8, 2026
bdf456c
fix
Oct 9, 2026
30dc405
fix docblock for tests
Oct 9, 2026
538923b
additional tests
Oct 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
> **Note:** Please fill out all relevant sections and remove irrelevant ones.
### 🔀 Purpose of this PR:

- [ ] Fixes a bug
- [ ] Updates for a new Moodle version
- [ ] Adds a new feature of functionality
- [ ] Improves or enhances existing features
- [ ] Refactoring: restructures code for better performance or maintainability
- [ ] Testing: add missing or improve existing tests
- [ ] Miscellaneous: code cleaning (without functional changes), documentation, configuration, ...

---

### 📝 Description:

Please describe the purpose of this PR in a few sentences.

- What feature or bug does it address?
- Why is this change or addition necessary?
- What is the expected behavior after the change?

---

### 📋 Checklist

Please confirm the following (check all that apply):

- [ ] I have `phpunit` and/or `behat` tests that cover my changes or additions.
- [ ] Code passes the code checker without errors and warnings.
- [ ] Code passes the moodle-ci/cd pipeline on all supported Moodle versions or the ones the plugin supports.
- [ ] Code does not have `var_dump()` or `var_export` or any other debugging statements (or commented out code) that
should not appear on the productive branch.
- [ ] Code only uses language strings instead of hard-coded strings.
- [ ] If there are changes in the database: I updated/created the necessary upgrade steps in `db/upgrade.php` and
updated the `version.php`.
- [ ] If there are changes in javascript: I build new `.min` files with the `grunt amd` command.
- [ ] If it is a Moodle update PR: I read the release notes, updated the `version.php` and the `CHANGES.md`.
I ran all tests thoroughly checking for errors. I checked if bootstrap had any changes/deprecations that require
changes in the plugins UI.

---

### 🔍 Related Issues

- Related to #[IssueNumber]

---

### 🧾📸🌐 Additional Information (like screenshots, documentation, links, etc.)

Any other relevant information.

---
2 changes: 1 addition & 1 deletion amd/build/delselected.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion amd/build/delselected.min.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion amd/src/delselected.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
*/
export const SELECTORS = {
DELACTION: 'div.commandbar a#delselected',
SELECTBOX: 'table#slotmanager input.slotselect',
SELECTBOX: 'table#slotmanager input.slotselect.form-check-input.mt-0',
};

/**
Expand Down
8 changes: 5 additions & 3 deletions backup/moodle2/backup_scheduler_stepslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,19 @@ protected function define_structure() {
'scale', 'gradingstrategy', 'bookingrouping', 'usenotes',
'usebookingform', 'bookinginstructions', 'bookinginstructionsformat',
'usestudentnotes', 'requireupload', 'uploadmaxfiles', 'uploadmaxsize',
'usecaptcha', 'timemodified', ]);
'usecaptcha', 'timemodified', 'groupcreation', ]);

$slots = new backup_nested_element('slots');

$slot = new backup_nested_element('slot', ['id'], [
'starttime', 'duration', 'teacherid', 'appointmentlocation',
'timemodified', 'notes', 'notesformat', 'exclusivity',
'emaildate', 'hideuntil', ]);
'emaildate', 'hideuntil', 'coursegroupid', ]);

$appointments = new backup_nested_element('appointments');

$appointment = new backup_nested_element('appointment', ['id'], [
'studentid', 'attended', 'grade',
'studentid', 'bookinggroupid', 'attended', 'grade',
'appointmentnote', 'appointmentnoteformat', 'teachernote', 'teachernoteformat',
'studentnote', 'studentnoteformat', 'timecreated', 'timemodified', ]);

Expand All @@ -86,7 +86,9 @@ protected function define_structure() {

if ($userinfo) {
$slot->annotate_ids('user', 'teacherid');
$slot->annotate_ids('group', 'coursegroupid');
$appointment->annotate_ids('user', 'studentid');
$appointment->annotate_ids('group', 'bookinggroupid');
}

// Define file annotations.
Expand Down
10 changes: 9 additions & 1 deletion backup/moodle2/restore_scheduler_stepslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ protected function process_scheduler($data) {
$data->bookingrouping = $this->get_mappingid('grouping', $data->bookingrouping);
}

$data->groupcreation = (int) ($data->groupcreation ?? 0);

// Insert the scheduler record.
$newitemid = $DB->insert_record('scheduler', $data);
// Immediately after inserting "activity" record, call this.
Expand All @@ -106,9 +108,12 @@ protected function process_scheduler_slot($data) {
$data->timemodified = $this->apply_date_offset($data->timemodified);
$data->emaildate = $this->apply_date_offset($data->emaildate);
$data->hideuntil = $this->apply_date_offset($data->hideuntil);

$data->teacherid = $this->get_mappingid('user', $data->teacherid);

$data->coursegroupid = !empty($data->coursegroupid)
? $this->get_mappingid('group', $data->coursegroupid, 0)
: 0;

$newitemid = $DB->insert_record('scheduler_slots', $data);
$this->set_mapping('scheduler_slot', $oldid, $newitemid, true);
}
Expand All @@ -131,6 +136,9 @@ protected function process_scheduler_appointment($data) {

$data->studentid = $this->get_mappingid('user', $data->studentid);

$data->bookinggroupid = !empty($data->bookinggroupid)
? $this->get_mappingid('group', $data->bookinggroupid, 0) : 0;

$newitemid = $DB->insert_record('scheduler_appointment', $data);
$this->set_mapping('scheduler_appointment', $oldid, $newitemid, true);
}
Expand Down
113 changes: 113 additions & 0 deletions classes/course_group_helper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Helpers for synchronizing generated course groups with slot bookings.
*
* @package mod_scheduler
* @copyright 2026 L. Herfeldt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

namespace mod_scheduler;
/**
* Updates generated course group memberships from individual slot bookings.
*/
class course_group_helper {
/**
* Adds individually booked students to the slot's generated course group.
*
* @param model\slot $slot
* @return void
* @throws \coding_exception
* @throws \dml_exception
*/
public static function add_booked_students(\mod_scheduler\model\slot $slot): void {
global $CFG;
global $DB;

$scheduler = $slot->get_scheduler();
$mode = (int) $scheduler->groupcreation;

// Only process automatic group creation modes.
if (!in_array($mode, [1, 2], true)) {
return;
}

// Slot has to allow multiple bookers (groupslot).
if (!$slot->is_groupslot()) {
return;
}

$studentids = $DB->get_fieldset_select(
'scheduler_appointment',
'DISTINCT studentid',
'slotid = :slotid AND bookinggroupid = 0',
['slotid' => $slot->id]
);

if (!$studentids) {
return;
}

require_once($CFG->dirroot . '/mod/scheduler/locallib.php');
require_once($CFG->dirroot . '/group/lib.php');

$groupid = \scheduler_create_coursegroup($slot);
foreach ($studentids as $studentid) {
\groups_add_member($groupid, $studentid);
}
}

/**
* Removes individually booked students from the slot's generated course group.
*
* @param model\slot $slot
* @param \stdClass $appointment
* @return void
* @throws \dml_exception
*/
public static function remove_booked_students(\mod_scheduler\model\slot $slot, \stdClass $appointment): void {
global $CFG, $DB;

// Existing group bookings do not control generated group memberships.
if ((int) $appointment->bookinggroupid !== 0) {
return;
}

$scheduler = $slot->get_scheduler();
$mode = (int) $scheduler->groupcreation;

// Only process automatic group creation modes.
if (!in_array($mode, [1, 2], true)) {
return;
}

$groupid = $DB->get_field(
'scheduler_slots',
'coursegroupid',
['id' => $slot->id]
);

if (!$groupid || !$DB->record_exists('groups', ['id' => $groupid, 'courseid' => $scheduler->course])) {
return;
}

require_once($CFG->dirroot . '/group/lib.php');

\groups_remove_member($groupid, (int) $appointment->studentid);
}
}
Loading
Loading