summaryrefslogtreecommitdiffstats
path: root/modules-available/exams/page.inc.php
diff options
context:
space:
mode:
authorSimon Rettberg2026-01-15 11:21:11 +0100
committerSimon Rettberg2026-01-15 11:21:11 +0100
commit60dbee86166915bc24d70782132eb38aa5cfb6db (patch)
tree48b4d784e0112ed70cd31d17ff0b6074f0f66396 /modules-available/exams/page.inc.php
parent[dozmod] Disable checkboxes *after* submit (diff)
downloadslx-admin-60dbee86166915bc24d70782132eb38aa5cfb6db.tar.gz
slx-admin-60dbee86166915bc24d70782132eb38aa5cfb6db.tar.xz
slx-admin-60dbee86166915bc24d70782132eb38aa5cfb6db.zip
[exams] Add check and warning for colliding exams
If two (or more) exams share at least one location and their start/end times overlap, display a warning to the user.
Diffstat (limited to 'modules-available/exams/page.inc.php')
-rw-r--r--modules-available/exams/page.inc.php52
1 files changed, 50 insertions, 2 deletions
diff --git a/modules-available/exams/page.inc.php b/modules-available/exams/page.inc.php
index f42711a2..2bd7a215 100644
--- a/modules-available/exams/page.inc.php
+++ b/modules-available/exams/page.inc.php
@@ -224,8 +224,51 @@ class Page_Exams extends Page
{
$out = [];
$now = time();
+
+ // Pre-compute collisions: exams overlap in time and share at least one location
+ $collisionFlags = [];
+ $parsedExams = [];
if (is_array($this->exams)) {
- foreach ($this->exams as $exam) {
+ foreach ($this->exams as $idx => $exam) {
+ IF ($exam['endtime'] < $now)
+ continue; // Don't care about past exams
+ // Parse location IDs: treat NULL as global (0) to be consistent with permission checks
+ if (empty($exam['locationids'])) {
+ $lids = Location::getAllLocationIds(0);
+ } else {
+ $lids = array_map(function($val) {
+ return Location::getAllLocationIds($val, true);
+ }, explode(',', (string)$exam['locationids']));
+ $lids = array_unique(array_merge(...$lids));
+ }
+ $parsedExams[$idx] = [
+ 'start' => (int)$exam['starttime'],
+ 'end' => (int)$exam['endtime'],
+ 'lids' => $lids,
+ ];
+ $collisionFlags[$idx] = false;
+ }
+
+ $indices = array_keys($parsedExams);
+ $cnt = count($indices);
+ for ($i = 0; $i < $cnt; $i++) {
+ for ($j = $i + 1; $j < $cnt; $j++) {
+ $a = $parsedExams[$indices[$i]];
+ $b = $parsedExams[$indices[$j]];
+ // Time overlap if startA < endB and startB < endA
+ if ($a['start'] < $b['end'] && $b['start'] < $a['end']) {
+ // Location overlap
+ if (!empty(array_intersect($a['lids'], $b['lids']))) {
+ $collisionFlags[$indices[$i]] = true;
+ $collisionFlags[$indices[$j]] = true;
+ }
+ }
+ }
+ }
+
+ // Build output, attaching visual classes and collision flag
+ $numCollisions = 0;
+ foreach ($this->exams as $idx => $exam) {
if ($exam['endtime'] < $now) {
$exam['rowClass'] = 'text-muted';
$exam['btnClass'] = 'btn-default';
@@ -238,10 +281,15 @@ class Page_Exams extends Page
}
$exam['starttime_s'] = date('Y-m-d H:i', $exam['starttime']);
$exam['endtime_s'] = date('Y-m-d H:i', $exam['endtime']);
+ if ($collisionFlags[$idx] ?? false) {
+ $exam['rowClass'] = 'danger slx-bold';
+ $exam['collision'] = true;
+ $numCollisions++;
+ }
$out[] = $exam;
}
}
- return ['exams' => $out];
+ return ['exams' => $out, 'collisions' => $numCollisions];
}
protected function makeLectureExamList(): array