summaryrefslogtreecommitdiffstats
path: root/modules-available/exams
diff options
context:
space:
mode:
authorSimon Rettberg2016-07-12 16:54:36 +0200
committerSimon Rettberg2016-07-12 16:54:36 +0200
commitd072920a676743dd4ba9d4cc4af080eee9809a01 (patch)
treed5df552bd3878d8704447593cffedd24ffb8f90b /modules-available/exams
parent[exams] Fix layout/type (diff)
downloadslx-admin-d072920a676743dd4ba9d4cc4af080eee9809a01.tar.gz
slx-admin-d072920a676743dd4ba9d4cc4af080eee9809a01.tar.xz
slx-admin-d072920a676743dd4ba9d4cc4af080eee9809a01.zip
[exams] Show list of upcomfing exam lectures; allow adding exam period based on upcoming exam
Diffstat (limited to 'modules-available/exams')
-rw-r--r--modules-available/exams/install.inc.php5
-rw-r--r--modules-available/exams/lang/de/messages.json11
-rw-r--r--modules-available/exams/lang/de/module.json8
-rw-r--r--modules-available/exams/lang/de/template-tags.json18
-rw-r--r--modules-available/exams/lang/en/messages.json1
-rw-r--r--modules-available/exams/lang/en/template-tags.json12
-rw-r--r--modules-available/exams/page.inc.php173
-rw-r--r--modules-available/exams/templates/page-add-edit-exam.html56
-rw-r--r--modules-available/exams/templates/page-exams-vis.html52
-rw-r--r--modules-available/exams/templates/page-exams.html139
-rw-r--r--modules-available/exams/templates/page-main-heading.html3
-rw-r--r--modules-available/exams/templates/page-upcoming-lectures.html34
12 files changed, 342 insertions, 170 deletions
diff --git a/modules-available/exams/install.inc.php b/modules-available/exams/install.inc.php
index 0cb1af8e..18be0be6 100644
--- a/modules-available/exams/install.inc.php
+++ b/modules-available/exams/install.inc.php
@@ -4,6 +4,7 @@ $res = array();
$res[] = tableCreate('exams', '
`examid` int(11) NOT NULL AUTO_INCREMENT,
+ `lectureid` char(36) CHARACTER SET ascii COLLATE ascii_bin DEFAULT NULL
`starttime` int(11) NOT NULL,
`endtime` int(11) NOT NULL,
`description` varchar(500) DEFAULT NULL,
@@ -24,6 +25,10 @@ if (Database::exec("ALTER TABLE exams ADD INDEX `idx_daterange` ( `starttime` ,
$res[] = UPDATE_DONE;
}
+if (!tableHasColumn('exams', 'lectureid')) {
+ Database::exec("ALTER TABLE `exams` ADD `lectureid` CHAR(36) CHARACTER SET ascii COLLATE ascii_bin NULL DEFAULT NULL AFTER `examid`");
+}
+
Database::exec("ALTER TABLE `exams` CHANGE `description` `description` varchar(500) DEFAULT NULL");
if (in_array(UPDATE_DONE, $res)) {
diff --git a/modules-available/exams/lang/de/messages.json b/modules-available/exams/lang/de/messages.json
index eda4257e..574cca93 100644
--- a/modules-available/exams/lang/de/messages.json
+++ b/modules-available/exams/lang/de/messages.json
@@ -3,10 +3,11 @@
"end-before-start": "Endzeitpunkt liegt vor Startzeitpunkt!",
"endtime-invalid": "Ung\u00fcltige Endzeit: {{0}}",
"error-while-saving-changes": "Fehler beim Speichern der \u00c4nderungen",
- "exam-added-success": "Klausurzeitraum erfolgreich hinzugef\u00fcgt",
- "exam-deleted-success": "Klausurzeitraum erfolgreich gel\u00f6scht",
- "exam-not-added": "Klausurzeitraum konnte nicht hinzugef\u00fcgt werden",
- "exam-not-deleted-error": "Klausurzeitraum konnte nicht gel\u00f6scht werden",
- "invalid-exam-id": "Ung\u00fcltige Klausur-ID: {{0}}",
+ "exam-added-success": "Pr\u00fcfungszeitraum erfolgreich hinzugef\u00fcgt",
+ "exam-deleted-success": "Pr\u00fcfungszeitraum erfolgreich gel\u00f6scht",
+ "exam-not-added": "Pr\u00fcfungszeitraum konnte nicht hinzugef\u00fcgt werden",
+ "exam-not-deleted-error": "Pr\u00fcfungszeitraum konnte nicht gel\u00f6scht werden",
+ "invalid-exam-id": "Ung\u00fcltige Pr\u00fcfungs-ID: {{0}}",
+ "no-upcoming-lecture-exams": "Keine anstehenden Veranstaltungen, die als Pr\u00fcfung markiert sind",
"starttime-invalid": "Ung\u00fcltige Startzeit: {{0}}"
} \ No newline at end of file
diff --git a/modules-available/exams/lang/de/module.json b/modules-available/exams/lang/de/module.json
index 226810fd..4d9fd954 100644
--- a/modules-available/exams/lang/de/module.json
+++ b/modules-available/exams/lang/de/module.json
@@ -1,6 +1,6 @@
{
- "module_name": "Klausurmodus",
- "title_add-exam": "Klausur hinzuf\u00fcgen",
- "title_edit-exam": "Klausur bearbeiten",
- "warning_lecture_is_not_enabled": "Warnung: Diese Vorlesung ist nicht vom Dozenten aktiviert"
+ "module_name": "Pr\u00fcfungsmodus",
+ "title_add-exam": "Pr\u00fcfungszeitraum hinzuf\u00fcgen",
+ "title_edit-exam": "Pr\u00fcfungszeitraum bearbeiten",
+ "warning_lecture_is_not_enabled": "Warnung: Diese Veranstaltung ist nicht vom Dozenten aktiviert worden"
} \ No newline at end of file
diff --git a/modules-available/exams/lang/de/template-tags.json b/modules-available/exams/lang/de/template-tags.json
index 9b9225de..797cc371 100644
--- a/modules-available/exams/lang/de/template-tags.json
+++ b/modules-available/exams/lang/de/template-tags.json
@@ -1,17 +1,27 @@
{
"lang_actions": "Aktionen",
- "lang_addExam": "Klausurzeitraum hinzuf\u00fcgen",
- "lang_allExamPeriods": "Alle Klausurzeitr\u00e4ume",
+ "lang_addExam": "Zeitraum hinzuf\u00fcgen",
+ "lang_addingBasedOnLecture": "F\u00fcge neuen Pr\u00fcfungszeitraum basierend auf vorhandener Veranstaltung an",
+ "lang_allExamPeriods": "Alle Pr\u00fcfungszeitr\u00e4ume",
"lang_begin": "Beginn",
"lang_begin_date": "Beginn Datum",
"lang_begin_time": "Uhrzeit",
"lang_deleteConfirmation": "Wirklich l\u00f6schen?",
"lang_description": "Beschreibung",
- "lang_editExam": "Klausurzeitraum bearbeiten",
+ "lang_duration": "Dauer",
+ "lang_editExam": "Zeitraum bearbeiten",
"lang_end": "Ende",
"lang_end_date": "Ende Datum",
"lang_end_time": "Uhrzeit",
+ "lang_examModeDescription": "Hier k\u00f6nnen Sie bwLehrpool-R\u00e4ume zeitgesteuert in den Pr\u00fcfungsmodus versetzen. Im Pr\u00fcfungsmodus ist das Client-System st\u00e4rker abgeriegelt, sodass es sich zum Schreiben von E-Pr\u00fcfungen eignet. Nach dem Ein- bzw. Ausschalten des Pr\u00fcfungsmodus ist es notwendig, die Rechner in den betroffenen R\u00e4umen neuzustarten.",
+ "lang_global": "Global",
+ "lang_headingAllExamLectures": "Liste ausstehender Pr\u00fcfungsveranstaltungen",
+ "lang_headingGraphicalOverview": "Grafische Darstellung",
+ "lang_headingMain": "bwLehrpool Pr\u00fcfungsmodus",
"lang_id": "ID",
+ "lang_lectureName": "Veranstaltungsname",
"lang_location": "Raum\/Ort",
- "lang_locations": "R\u00e4ume\/Orte"
+ "lang_locations": "R\u00e4ume\/Orte",
+ "lang_noDescription": "Keine Beschreibung",
+ "lang_timeFrame": "Zeitraum"
} \ No newline at end of file
diff --git a/modules-available/exams/lang/en/messages.json b/modules-available/exams/lang/en/messages.json
index a62cc489..f07ffba1 100644
--- a/modules-available/exams/lang/en/messages.json
+++ b/modules-available/exams/lang/en/messages.json
@@ -8,5 +8,6 @@
"exam-not-added": "Exam period was not added",
"exam-not-deleted-error": "Exam period was not deleted",
"invalid-exam-id": "Invalid exam id: {{0}}",
+ "no-upcoming-lecture-exams": "No upcoming lectures which are marked as exam",
"starttime-invalid": "Invalid start time: {{0}}"
} \ No newline at end of file
diff --git a/modules-available/exams/lang/en/template-tags.json b/modules-available/exams/lang/en/template-tags.json
index 9c49c8a3..2e5abeff 100644
--- a/modules-available/exams/lang/en/template-tags.json
+++ b/modules-available/exams/lang/en/template-tags.json
@@ -1,17 +1,27 @@
{
"lang_actions": "Actions",
"lang_addExam": "Add exam period",
+ "lang_addingBasedOnLecture": "Adding exam period based on lecture",
"lang_allExamPeriods": "All exam periods",
"lang_begin": "Begin",
"lang_begin_date": "Begin Date",
"lang_begin_time": "Time",
"lang_deleteConfirmation": "Are you sure?",
"lang_description": "Description",
+ "lang_duration": "Duration",
"lang_editExam": "Edit Exam Period",
"lang_end": "End",
"lang_end_date": "End Date",
"lang_end_time": "Time",
+ "lang_examModeDescription": "Here you can define time spans during which selected rooms will be set to exam mode. In exam mode, the client computers are more locked down than usual so it is suitable for writing electronic exams.",
+ "lang_global": "Global",
+ "lang_headingAllExamLectures": "Upcoming lectures marked as exams",
+ "lang_headingGraphicalOverview": "Graphical overview",
+ "lang_headingMain": "bwLehrpool exam mode",
"lang_id": "ID",
+ "lang_lectureName": "Lecture name",
"lang_location": "Room\/Location",
- "lang_locations": "Rooms\/Locations"
+ "lang_locations": "Rooms\/Locations",
+ "lang_noDescription": "No description",
+ "lang_timeFrame": "Time frame"
} \ No newline at end of file
diff --git a/modules-available/exams/page.inc.php b/modules-available/exams/page.inc.php
index d0d2255f..d6b1ccea 100644
--- a/modules-available/exams/page.inc.php
+++ b/modules-available/exams/page.inc.php
@@ -12,12 +12,14 @@ class Page_Exams extends Page
/** if examid is set, also add a column 'selected' **/
- protected function readLocations($examid = null)
+ protected function readLocations($examidOrLocations = null)
{
- if ($examid == null) {
+ if ($examidOrLocations == null) {
$active = 0;
+ } elseif (is_array($examidOrLocations)) {
+ $active = $examidOrLocations;
} else {
- $tmp = Database::simpleQuery("SELECT locationid FROM exams_x_location WHERE examid= :examid", compact('examid'));
+ $tmp = Database::simpleQuery("SELECT locationid FROM exams_x_location WHERE examid= :examid", array('examid' => $examidOrLocations));
$active = array();
while ($row = $tmp->fetch(PDO::FETCH_ASSOC)) {
$active[] = (int)$row['locationid'];
@@ -29,7 +31,7 @@ class Page_Exams extends Page
protected function readExams()
{
$tmp = Database::simpleQuery("SELECT examid, starttime, endtime, description, GROUP_CONCAT(locationid) AS locationids, "
- . "GROUP_CONCAT(locationname) AS locationnames FROM exams "
+ . "GROUP_CONCAT(locationname SEPARATOR ', ') AS locationnames FROM exams "
. "NATURAL LEFT JOIN exams_x_location NATURAL LEFT JOIN location GROUP BY examid");
while ($exam = $tmp->fetch(PDO::FETCH_ASSOC)) {
$this->exams[] = $exam;
@@ -39,20 +41,15 @@ class Page_Exams extends Page
protected function readLectures()
{
$tmp = Database::simpleQuery(
- "SELECT lectureid, locationid, displayname, starttime, endtime, isenabled " .
+ "SELECT lectureid, Group_Concat(locationid) as lids, displayname, starttime, endtime, isenabled, firstname, lastname, email " .
"FROM sat.lecture " .
+ "INNER JOIN sat.user ON (user.userid = lecture.ownerid) " .
"NATURAL LEFT JOIN sat.lecture_x_location " .
- "WHERE isexam <> 0 AND starttime < :rangeMax AND endtime > :rangeMin",
+ "WHERE isexam <> 0 AND starttime < :rangeMax AND endtime > :rangeMin " .
+ "GROUP BY lectureid",
['rangeMax' => $this->rangeMax, 'rangeMin' => $this->rangeMin]);
while ($lecture = $tmp->fetch(PDO::FETCH_ASSOC)) {
- if (is_null($lecture['locationid'])) {
- foreach ($this->locations as $location) {
- $lecture['locationid'] = $location['locationid'];
- $this->lectures[] = $lecture;
- }
- } else {
- $this->lectures[] = $lecture;
- }
+ $this->lectures[] = $lecture;
}
}
@@ -81,8 +78,8 @@ class Page_Exams extends Page
$locationids = explode(',', $e['locationids']);
if ($locationids[0] == 0) {
$locationids = [];
- foreach($this->locations as $l) {
- $locationids[] = $l['locationid'];
+ foreach($this->locations as $location) {
+ $locationids[] = $location['locationid'];
}
}
foreach ($locationids as $locationid) {
@@ -98,20 +95,28 @@ class Page_Exams extends Page
}
}
/* add the lectures */
+ $allLocationIds = array_map(function($loc) { return $loc['locationid']; }, $this->locations);
$i = 2;
- foreach ($this->lectures as $l) {
- $mark = '<span class="' . ($l['isenabled'] ? '' : 'glyphicon glyphicon-exclamation-sign') . '"></span>';
- $out[] = [
- 'id' => $l['lectureid'] . '/' . $l['locationid'],
- 'content' => htmlspecialchars($l['displayname']) . $mark,
- 'title' => $l['isenabled'] ? '' : Dictionary::translate('warning_lecture_is_not_enabled'),
- 'start' => intval($l['starttime']) * 1000,
- 'end' => intval($l['endtime']) * 1000,
- 'group' => $l['locationid'],
- 'className' => $l['isenabled'] ? '' : 'disabled',
- 'editable' => false,
- 'subgroup' => $i++,
- ];
+ foreach ($this->lectures as $lecture) {
+ $mark = '<span class="' . ($lecture['isenabled'] ? '' : 'glyphicon glyphicon-exclamation-sign') . '"></span>';
+ if (empty($lecture['lids'])) {
+ $locations = $allLocationIds;
+ } else {
+ $locations = explode(',', $lecture['lids']);
+ }
+ foreach ($locations as $location) {
+ $out[] = [
+ 'id' => $lecture['lectureid'] . '/' . $location,
+ 'content' => htmlspecialchars($lecture['displayname']) . $mark,
+ 'title' => $lecture['isenabled'] ? '' : Dictionary::translate('warning_lecture_is_not_enabled'),
+ 'start' => intval($lecture['starttime']) * 1000,
+ 'end' => intval($lecture['endtime']) * 1000,
+ 'group' => $location,
+ 'className' => $lecture['isenabled'] ? '' : 'disabled',
+ 'editable' => false,
+ 'subgroup' => $i++,
+ ];
+ }
}
return json_encode($out);
@@ -136,7 +141,7 @@ class Page_Exams extends Page
$now = time();
foreach ($this->exams as $exam) {
if ($exam['endtime'] < $now) {
- $exam['rowClass'] = 'gray';
+ $exam['rowClass'] = 'text-muted';
$exam['btnClass'] = 'btn-success';
$exam['liesInPast'] = true;
} else {
@@ -149,6 +154,42 @@ class Page_Exams extends Page
return $out;
}
+ protected function makeLectureExamList()
+ {
+ $out = [];
+ $now = time();
+ $cutoff = strtotime('+ 5 day');
+ foreach ($this->lectures as $lecture) {
+ if ($lecture['endtime'] < $now || $lecture['starttime'] > $cutoff)
+ continue;
+ $entry = $lecture;
+ if (!$lecture['isenabled']) {
+ $entry['class'] = 'text-muted';
+ }
+ $entry['starttime_s'] = date('Y-m-d H:i', $lecture['starttime']);
+ $entry['endtime_s'] = date('Y-m-d H:i', $lecture['endtime']);
+ $duration = $lecture['endtime'] - $lecture['starttime'];
+ if ($duration < 86400) {
+ $entry['duration_s'] = gmdate('H:i', $duration);
+ }
+ $out[] = $entry;
+ }
+ return $out;
+ }
+
+ protected function makeEditFromArray($source)
+ {
+ if (!isset($source['description']) && isset($source['displayname'])) {
+ $source['description'] = $source['displayname'];
+ }
+ return [
+ 'starttime_date' => date('Y-m-d', $source['starttime']),
+ 'starttime_time' => date('H:i', $source['starttime']),
+ 'endtime_date' => date('Y-m-d', $source['endtime']),
+ 'endtime_time' => date('H:i', $source['endtime'])
+ ] + $source;
+ }
+
private function isDateSane($time)
{
return ($time >= $this->rangeMin && $time <= $this->rangeMax);
@@ -249,6 +290,10 @@ class Page_Exams extends Page
$this->readLocations();
$this->readLectures();
+ } elseif ($this->action === 'add') {
+
+ $this->readLectures();
+
} elseif ($this->action === 'edit') {
$examid = Request::get('examid', 0, 'int');
@@ -258,6 +303,7 @@ class Page_Exams extends Page
Util::redirect('?do=exams');
}
$this->readLocations($examid);
+ $this->readLectures();
} elseif ($this->action === 'save') {
@@ -288,32 +334,59 @@ class Page_Exams extends Page
protected function doRender()
{
if ($this->action === "show") {
- Render::setTitle("All Exams");
- Render::addTemplate('page-exams',
- ['exams' => $this->makeExamsForTemplate(),
- 'exams_json' => $this->makeItemsForVis(),
- 'rooms_json' => $this->makeGroupsForVis(),
- 'vis_begin' => strtotime('-5 minute') * 1000,
- 'vis_end' => strtotime('+2 day') * 1000,
- 'vis_min_date' => $this->rangeMin * 1000,
- 'vis_max_date' => $this->rangeMax * 1000,
- 'axis_label' => (count($this->locations) > 5 ? 'both' : 'bottom')
+
+ // General title and description
+ Render::addTemplate('page-main-heading');
+ // List of defined exam periods
+ Render::addTemplate('page-exams', [
+ 'exams' => $this->makeExamsForTemplate()
+ ]);
+ // List of upcoming lectures marked as exam
+ $upcoming = $this->makeLectureExamList();
+ if (empty($upcoming)) {
+ Message::addInfo('no-upcoming-lecture-exams');
+ } else {
+ Render::addTemplate('page-upcoming-lectures', [
+ 'pending_lectures' => $upcoming
]);
+ }
+ // Vis.js timeline
+ Render::addTemplate('page-exams-vis', [
+ 'exams_json' => $this->makeItemsForVis(),
+ 'rooms_json' => $this->makeGroupsForVis(),
+ 'vis_begin' => strtotime('-5 minute') * 1000,
+ 'vis_end' => strtotime('+2 day') * 1000,
+ 'vis_min_date' => $this->rangeMin * 1000,
+ 'vis_max_date' => $this->rangeMax * 1000,
+ 'axis_label' => (count($this->locations) > 5 ? 'both' : 'bottom'),
+ 'utc_offset' => date('P')
+ ]);
+
} elseif ($this->action === "add") {
+
Render::setTitle(Dictionary::translate('title_add-exam'));
- $this->readLocations();
- Render::addTemplate('page-add-edit-exam', ['locations' => $this->locations]);
+ $data = [];
+ $baseLecture = Request::any('lectureid', false, 'string');
+ $locations = null;
+ if ($baseLecture !== false) {
+ foreach ($this->lectures as $lecture) {
+ if ($lecture['lectureid'] === $baseLecture) {
+ $data['exam'] = $this->makeEditFromArray($lecture);
+ $locations = explode(',', $lecture['lids']);
+ break;
+ }
+ }
+ }
+ $this->readLocations($locations);
+ $data['locations'] = $this->locations;
+ Render::addTemplate('page-add-edit-exam', $data);
+
} elseif ($this->action === 'edit') {
+
Render::setTitle(Dictionary::translate('title_edit-exam'));
- $exam = [
- 'examid' => $this->currentExam['examid'],
- 'starttime_date' => date('Y-m-d', $this->currentExam['starttime']),
- 'starttime_time' => date('H:i', $this->currentExam['starttime']),
- 'endtime_date' => date('Y-m-d', $this->currentExam['endtime']),
- 'endtime_time' => date('H:i', $this->currentExam['endtime']),
- 'description' => $this->currentExam['description']
- ];
+ $exam = $this->makeEditFromArray($this->currentExam);
Render::addTemplate('page-add-edit-exam', ['exam' => $exam, 'locations' => $this->locations]);
+
}
}
diff --git a/modules-available/exams/templates/page-add-edit-exam.html b/modules-available/exams/templates/page-add-edit-exam.html
index d112095f..3f0ef372 100644
--- a/modules-available/exams/templates/page-add-edit-exam.html
+++ b/modules-available/exams/templates/page-add-edit-exam.html
@@ -4,6 +4,9 @@
{{^exam.examid}}
<h1>{{lang_addExam}}</h1>
{{/exam.examid}}
+{{#exam.displayname}}
+<div class="alert alert-info">{{lang_addingBasedOnLecture}}:<br><b>{{exam.displayname}}</b></div>
+{{/exam.displayname}}
<form class="form" method="POST" action="?do=exams" id="tolleform">
<div class="form-group">
@@ -64,6 +67,13 @@
</div>
</div>
</div>
+
+ <div class="panel">
+ <div class="panel-body">
+ {{lang_duration}}: <span id="exam-duration">-</span>
+ </div>
+ </div>
+
<div class="row form-group">
<div class="form-group col-xs-12">
<label for="description">{{lang_description}}</label>
@@ -80,6 +90,8 @@
<script type="application/javascript"><!--
document.addEventListener("DOMContentLoaded", function () {
var filename = "modules/bootstrap_datepicker/lang/bootstrap-datepicker." + LANG + ".js";
+ moment.locale(LANG);
+ var slxMoment = moment;
$.getScript(filename)
.always(function () {
@@ -87,38 +99,58 @@ document.addEventListener("DOMContentLoaded", function () {
var dateSettings = {
format: 'yyyy-mm-dd',
weekStart: 1,
- startDate: 'today',
todayHighlight: true,
language: LANG
};
- var timeSettings = {
+ var timeSettings = {
showSeconds: false,
showMeridian: false,
minuteStep: 5,
appendWidgetTo: 'body'
};
- $('.datepicker').datepicker(dateSettings);
- $('.timepicker2').timepicker(timeSettings);
+ $('.datepicker').datepicker(dateSettings);
+ $('.timepicker2').timepicker(timeSettings);
- $('#locations').multiselect({numberDisplayed: 1});
+ showDuration();
+ });
+ $('#locations').multiselect({numberDisplayed: 1});
- });
+ var start_date = $('#starttime_date');
+ var start_time = $('#starttime_time');
+ var end_date = $('#endtime_date');
+ var end_time = $('#endtime_time');
+ var rspan = $('#exam-duration');
- $('#starttime_date').focusout(function () {
- var start = $('#starttime_date').val();
- var end = $('#endtime_date').val();
+ start_date.focusout(function () {
+ var start = start_date.val();
+ var end = end_date.val();
var ok = end.length === 0;
if (!ok) {
- var ms = moment(start, 'YYYY-MM-DD');
- var me = moment(end, 'YYYY-MM-DD');
+ var ms = slxMoment(start, 'YYYY-MM-DD');
+ var me = slxMoment(end, 'YYYY-MM-DD');
ok = !me.isValid() || me.isBefore(ms);
}
if (ok) {
- $('#endtime_date').val(start);
+ end_date.val(start);
}
});
+ var showDuration = function () {
+ var sd = slxMoment(start_date.val() + ' ' + start_time.val(), 'YYYY-MM-DD H:mm');
+ var ed = slxMoment(end_date.val() + ' ' + end_time.val(), 'YYYY-MM-DD H:mm');
+ if (!sd.isValid() || !ed.isValid()) {
+ rspan.text('-');
+ return;
+ }
+ rspan.text(slxMoment.duration(ed.diff(sd)).humanize());
+ }
+
+ start_date.change(showDuration);
+ start_time.change(showDuration);
+ end_date.change(showDuration);
+ end_time.change(showDuration);
+
}, false);
// --></script>
diff --git a/modules-available/exams/templates/page-exams-vis.html b/modules-available/exams/templates/page-exams-vis.html
new file mode 100644
index 00000000..e347900b
--- /dev/null
+++ b/modules-available/exams/templates/page-exams-vis.html
@@ -0,0 +1,52 @@
+<h2>{{lang_headingGraphicalOverview}}</h2>
+
+<div id="timeline" class="slx-space"></div>
+
+<script type="application/javascript"><!--
+
+
+function itemOrderFun(a, b) {
+ return a.content.localeCompare(b.content);
+}
+
+function groupOrderFun(a, b) {
+ var s = a.sortIndex - b.sortIndex;
+ if (s != 0) return s;
+ return itemOrderFun(a, b);
+}
+
+var slxTimeline;
+
+document.addEventListener("DOMContentLoaded", function () {
+ var container = document.getElementById('timeline');
+ var groups_plain = {{{rooms_json}}};
+ var items_plain = {{{exams_json}}};
+ var groups = new vis.DataSet(groups_plain);
+ var items = new vis.DataSet(items_plain);
+
+ var language = window.navigator.userLanguage || window.navigator.language;
+
+ var options = {
+ 'start' : {{vis_begin}},
+ 'end' : {{vis_end}},
+ 'stack' : false,
+ 'editable': false,
+ 'min' : {{vis_min_date}},
+ 'max' : {{vis_max_date}},
+ 'zoomMin': 6 * 3600 * 1000,
+ 'zoomMax': 2 * 86400 * 1000,
+ 'order' : itemOrderFun,
+ 'groupOrder': groupOrderFun,
+ 'locale' : language,
+ 'moment' : function(date) { return vis.moment(date).utcOffset('{{utc_offset}}'); },
+ 'orientation': { 'axis': '{{axis_label}}' }
+};
+
+ slxTimeline = new vis.Timeline(container, items, groups, options);
+}, false);
+
+function slxShow(st, et) {
+ slxTimeline.setWindow(st * 1000, et * 1000);
+}
+
+// --></script>
diff --git a/modules-available/exams/templates/page-exams.html b/modules-available/exams/templates/page-exams.html
index 5fa4c0c5..184a69e3 100644
--- a/modules-available/exams/templates/page-exams.html
+++ b/modules-available/exams/templates/page-exams.html
@@ -1,96 +1,47 @@
-<div class="container-fluid">
- <h1>{{lang_allExamPeriods}}</h1>
-
- <div class="row">
- <table class="table">
- <tr>
- <th>{{lang_id}}</th>
- <th>{{lang_locations}}</th>
- <th>{{lang_begin}}</th>
- <th>{{lang_end}}</th>
- <th>{{lang_actions}}</th>
- </tr>
- {{#exams}}
- <tr class="{{rowClass}}">
- <td>{{examid}}</td>
- <td>
- {{locationnames}}
- <div class="small">{{description}}</div>
- </td>
- <td class="slx-nowrap">{{starttime_s}}</td>
- <td class="slx-nowrap">{{endtime_s}}</td>
- <td class="slx-nowrap">
- <form method="POST" action="?do=exams&action=delete" {{^liesInPast}}onsubmit="return confirm('{{lang_deleteConfirmation}}');"{{/liesInPast}} >
- {{^liesInPast}}
- <span onclick="slxShow({{starttime}}, {{endtime}})" class="btn btn-default btn-sm"><span class="glyphicon glyphicon-eye-open"></span></span>
- {{/liesInPast}}
- <a href="?do=exams&action=edit&examid={{examid}}" class="btn btn-default btn-sm" >{{lang_edit}}</a>
- <input type="hidden" name="token" value="{{token}}">
- <input type="hidden" name="examid" value="{{examid}}">
- <button class="btn {{btnClass}} btn-sm">{{lang_delete}}</button>
- </form>
- </td>
- </tr>
- {{/exams}}
-
- </table>
- <div class="btn-toolbar" role="toolbar">
- <div class="btn-group" role="group">
- <a href="?do=exams&action=add" class="btn btn-success">{{lang_addExam}}</a>
- </div>
- </div>
- </div>
-
-
- <div class="row" style="margin-top: 2em">
- <div id="timeline"></div>
- </div>
+<h2>{{lang_allExamPeriods}}</h2>
+
+<div class="slx-space">
+ <table class="table">
+ <tr>
+ <th>{{lang_id}}</th>
+ <th>{{lang_locations}}</th>
+ <th>{{lang_begin}}</th>
+ <th>{{lang_end}}</th>
+ <th>{{lang_actions}}</th>
+ </tr>
+ {{#exams}}
+ <tr class="{{rowClass}}">
+ <td>{{examid}}</td>
+ <td>
+ {{locationnames}}
+ {{^locationnames}}
+ <i>{{lang_global}}</i>
+ {{/locationnames}}
+ <div class="small">
+ {{description}}
+ {{^description}}
+ <i>{{lang_noDescription}}</i>
+ {{/description}}
+ </div>
+ </td>
+ <td class="slx-nowrap">{{starttime_s}}</td>
+ <td class="slx-nowrap">{{endtime_s}}</td>
+ <td class="slx-nowrap">
+ <form method="POST" action="?do=exams&action=delete" {{^liesInPast}}onsubmit="return confirm('{{lang_deleteConfirmation}}');"{{/liesInPast}} >
+ {{^liesInPast}}
+ <a onclick="slxShow({{starttime}}, {{endtime}})" class="btn btn-default btn-sm"><span class="glyphicon glyphicon-eye-open"></span></a>
+ {{/liesInPast}}
+ <a href="?do=exams&action=edit&examid={{examid}}" class="btn btn-default btn-sm" >{{lang_edit}}</a>
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="examid" value="{{examid}}">
+ <button class="btn {{btnClass}} btn-sm">{{lang_delete}}</button>
+ </form>
+ </td>
+ </tr>
+ {{/exams}}
+ </table>
</div>
-<script type="application/javascript"><!--
-
-
-function itemOrderFun(a, b) {
- return a.content.localeCompare(b.content);
-}
-
-function groupOrderFun(a, b) {
- var s = a.sortIndex - b.sortIndex;
- if (s != 0) return s;
- return itemOrderFun(a, b);
-}
-var slxTimeline;
-
-document.addEventListener("DOMContentLoaded", function () {
- var container = document.getElementById('timeline');
- var groups_plain = {{{rooms_json}}};
- var items_plain = {{{exams_json}}};
- var groups = new vis.DataSet(groups_plain);
- var items = new vis.DataSet(items_plain);
-
- var language = window.navigator.userLanguage || window.navigator.language;
-
- var options = {
- 'start' : {{vis_begin}},
- 'end' : {{vis_end}},
- 'stack' : false,
- 'editable': false,
- 'min' : {{vis_min_date}},
- 'max' : {{vis_max_date}},
- 'zoomMin': 6 * 3600 * 1000,
- 'zoomMax': 2 * 86400 * 1000,
- 'order' : itemOrderFun,
- 'groupOrder': groupOrderFun,
- 'locale' : language,
- 'moment' : function(date) { return vis.moment(date).utc(); },
- 'orientation': { 'axis': '{{axis_label}}' }
- };
-
- slxTimeline = new vis.Timeline(container, items, groups, options);
-}, false);
-
-function slxShow(st, et) {
- slxTimeline.setWindow(st * 1000, et * 1000);
-}
-
-// --></script>
+<div class="btn-group" role="group">
+ <a href="?do=exams&action=add" class="btn btn-success">{{lang_addExam}}</a>
+</div>
diff --git a/modules-available/exams/templates/page-main-heading.html b/modules-available/exams/templates/page-main-heading.html
new file mode 100644
index 00000000..87b92a20
--- /dev/null
+++ b/modules-available/exams/templates/page-main-heading.html
@@ -0,0 +1,3 @@
+<h1>{{lang_headingMain}}</h1>
+
+<p>{{lang_examModeDescription}}</p> \ No newline at end of file
diff --git a/modules-available/exams/templates/page-upcoming-lectures.html b/modules-available/exams/templates/page-upcoming-lectures.html
new file mode 100644
index 00000000..4a62bc29
--- /dev/null
+++ b/modules-available/exams/templates/page-upcoming-lectures.html
@@ -0,0 +1,34 @@
+<h2>{{lang_headingAllExamLectures}}</h2>
+
+<div class="slx-space">
+ <table class="table">
+ <tr>
+ <th>{{lang_lectureName}}</th>
+ <th>{{lang_timeFrame}}</th>
+ <th>{{lang_actions}}</th>
+ </tr>
+ {{#pending_lectures}}
+ <tr>
+ <td>
+ {{displayname}}
+ <div class="small">
+ <a href="mailto:{{email}}">{{lastname}}, {{firstname}}</a>
+ </div>
+ </td>
+ <td width="30%" class="text-nowrap">
+ {{starttime_s}} &ensp; {{endtime_s}}
+ <div class="small">{{duration_s}}</div>
+ </td>
+ <td width="20%">
+ <div class="pull-right text-nowrap">
+ <a class="btn btn-sm btn-default" role="button" onclick="slxShow({{starttime}}, {{endtime}})"><span class="glyphicon glyphicon-eye-open"></span></a>
+ <a href="?do=exams&amp;action=add&amp;lectureid={{lectureid}}" class="btn btn-sm btn-default" role="button">
+ <span class="glyphicon glyphicon-plus-sign"></span>
+ <span class="hidden-sm">{{lang_addExam}}</span>
+ </a>
+ </div>
+ </td>
+ </tr>
+ {{/pending_lectures}}
+ </table>
+</div> \ No newline at end of file