summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--modules-available/exams/lang/de/template-tags.json12
-rw-r--r--modules-available/exams/lang/en/template-tags.json12
-rw-r--r--modules-available/exams/page.inc.php2
-rw-r--r--modules-available/exams/templates/page-add-edit-exam.html209
4 files changed, 170 insertions, 65 deletions
diff --git a/modules-available/exams/lang/de/template-tags.json b/modules-available/exams/lang/de/template-tags.json
index 0fbaf0a1..d52a3199 100644
--- a/modules-available/exams/lang/de/template-tags.json
+++ b/modules-available/exams/lang/de/template-tags.json
@@ -10,6 +10,7 @@
"lang_begin": "Beginn",
"lang_begin_date": "Beginn Datum",
"lang_begin_time": "Uhrzeit",
+ "lang_checkLocationSelectionHint": "Stellen Sie sicher, dass die gew\u00fcnschte(n) Pr\u00fcfungsveranstaltung(en) in ihrer Raumbeschr\u00e4nkung mit den hier ausgew\u00e4hlten R\u00e4umen \u00fcbereinstimmen.",
"lang_comfirmGlobalExam": "Wollen Sie wirklich eine globale Pr\u00fcfung definieren? Im gew\u00e4hlten Zeitraum werden s\u00e4mtliche R\u00e4ume in den Pr\u00fcfungsmodus geschaltet.",
"lang_dateTime": "Datum\/Uhrzeit",
"lang_deleteConfirmation": "Wirklich l\u00f6schen?",
@@ -18,7 +19,12 @@
"lang_end": "Ende",
"lang_end_date": "Ende Datum",
"lang_end_time": "Uhrzeit",
+ "lang_examEndAfterLectureEnd": "Der gew\u00e4hlte Klausurzeitraum endet sp\u00e4ter, als die gew\u00e4hlte Veranstaltung endet.",
+ "lang_examEndBeforeLectureEnd": "Der gew\u00e4hlte Klausurzeitraum endet fr\u00fcher, als die gew\u00e4hlte Veranstaltung endet.",
"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_examStartAfterLectureEnd": "Der gew\u00e4hlte Klausurzeitraum beginnt, nachdem die gew\u00e4hlte Veranstaltung endet, oder kurz vor derem Ende.",
+ "lang_examStartAfterLectureStart": "Der gew\u00e4hlte Klausurzeitraum beginnt sp\u00e4ter, als die gew\u00e4hlte Veranstaltung startet.",
+ "lang_examStartBeforeLectureStart": "Der gew\u00e4hlte Klausurzeitraum beginnt, bevor die gew\u00e4hlte Veranstaltung startet. Ein Autostart der Veranstaltung wird fehlschlagen.",
"lang_global": "Global",
"lang_headingAddExam": "Zeitraum hinzuf\u00fcgen",
"lang_headingAllExamLectures": "Ausstehende Pr\u00fcfungsveranstaltungen (30 Tage)",
@@ -27,12 +33,16 @@
"lang_headingMain": "bwLehrpool Pr\u00fcfungsmodus",
"lang_id": "ID",
"lang_lectureName": "Veranstaltungsname",
- "lang_lectureOutOfRange": "Achtung: Der oben angegebene Zeitraum ist k\u00fcrzer als die Dauer der Veranstaltung",
+ "lang_lectureNotForLocation": "Diese Veranstaltung findet nicht im oben ausgew\u00e4hlten Raum statt",
+ "lang_lectureTimespan": "Dauer der Veranstaltung",
"lang_location": "Raum\/Ort",
"lang_locationInfo": "W\u00e4hlen Sie hier die R\u00e4ume und Orte aus, die w\u00e4hrend des unten ausgew\u00e4hlten Zeitraums in den Pr\u00fcfungsmodus versetzt werden. Wenn sie hier keine Auswahl treffen, werden alle R\u00e4ume in den Pr\u00fcfungsmodus versetzt.",
"lang_locations": "R\u00e4ume\/Orte",
"lang_moreThanOneDay": "Mehr als ein Tag",
"lang_noDescription": "Keine Beschreibung",
"lang_none": "(Keine)",
+ "lang_sanityCheck": "Plausibilit\u00e4tspr\u00fcfung",
+ "lang_startAfterEnd": "Ende liegt vor Start",
+ "lang_startOrEndInvalid": "Start- oder Endzeitpunkt ung\u00fcltig",
"lang_timeFrame": "Zeitraum"
} \ 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 52173740..3359a28a 100644
--- a/modules-available/exams/lang/en/template-tags.json
+++ b/modules-available/exams/lang/en/template-tags.json
@@ -10,6 +10,7 @@
"lang_begin": "Begin",
"lang_begin_date": "Begin Date",
"lang_begin_time": "Time",
+ "lang_checkLocationSelectionHint": "Make sure that the according lecture(s) will have their location restrictions set accordingly.",
"lang_comfirmGlobalExam": "Do you really want to create a global exam? Every single room will be set to lecture mode during the selected time period.",
"lang_dateTime": "Date\/Time",
"lang_deleteConfirmation": "Are you sure?",
@@ -18,7 +19,12 @@
"lang_end": "End",
"lang_end_date": "End Date",
"lang_end_time": "Time",
+ "lang_examEndAfterLectureEnd": "Specified exam interval ends after selected lecture ends.",
+ "lang_examEndBeforeLectureEnd": "Specified exam interval ends before selected lecture ends.",
"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_examStartAfterLectureEnd": "Specified exam interval starts after selected lecture ends (or shortly before it ends).",
+ "lang_examStartAfterLectureStart": "Specified exam interval starts after selected lecture starts.",
+ "lang_examStartBeforeLectureStart": "Specified exam interval starts before selected lecture starts. (Auto-)starting the lecture before it's valid will fail.",
"lang_global": "Global",
"lang_headingAddExam": "Add Exam Period",
"lang_headingAllExamLectures": "Upcoming Lectures Marked As Exams (30 Days)",
@@ -27,12 +33,16 @@
"lang_headingMain": "bwLehrpool Exam Mode",
"lang_id": "ID",
"lang_lectureName": "Lecture name",
- "lang_lectureOutOfRange": "Hint: The exam period given above is shorter than the duration of the given lecture",
+ "lang_lectureNotForLocation": "This lecture is not visible at the location selected above",
+ "lang_lectureTimespan": "Lecture time span",
"lang_location": "Room\/Location",
"lang_locationInfo": "Select the rooms and locations you want to enable the exam mode in. Selecting nothing at all means that all clients will boot into exam mode during the given time period.",
"lang_locations": "Rooms\/Locations",
"lang_moreThanOneDay": "More than one day",
"lang_noDescription": "No description",
"lang_none": "(None)",
+ "lang_sanityCheck": "Sanity check",
+ "lang_startAfterEnd": "Start lies after end",
+ "lang_startOrEndInvalid": "Start oder end is invalid",
"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 aec13c32..b19c156e 100644
--- a/modules-available/exams/page.inc.php
+++ b/modules-available/exams/page.inc.php
@@ -79,7 +79,7 @@ class Page_Exams extends Page
protected function readLectures()
{
$tmp = Database::simpleQuery(
- "SELECT lectureid, Group_Concat(locationid) as lids, displayname, starttime, endtime, isenabled, firstname, lastname, email " .
+ "SELECT lectureid, Group_Concat(locationid) as lids, islocationprivate, 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 " .
diff --git a/modules-available/exams/templates/page-add-edit-exam.html b/modules-available/exams/templates/page-add-edit-exam.html
index a45cbac2..eb77e751 100644
--- a/modules-available/exams/templates/page-add-edit-exam.html
+++ b/modules-available/exams/templates/page-add-edit-exam.html
@@ -10,6 +10,8 @@
<form class="form" method="POST" action="?do=exams" id="tolleform">
+ <!-- fake button to prevent return from messing things up -->
+ <button type="submit" hidden onclick="return false"></button>
<div class="panel panel-default">
<div class="panel-heading"><label for="locations">{{lang_location}}</label></div>
<div class="panel-body">
@@ -23,6 +25,7 @@
{{/locations}}
</select>
</div>
+ {{lang_checkLocationSelectionHint}}
</div>
</div>
@@ -77,10 +80,10 @@
</div>
</div>
- <div class="panel">
- <div class="panel-body">
- {{lang_duration}}: <span id="exam-duration">-</span>
- </div>
+ <div>
+ {{lang_duration}}: <span id="exam-duration">-</span>
+ <span class="hidden" id="txt-invalid">{{lang_startOrEndInvalid}}</span>
+ <span class="hidden" id="txt-reverse">{{lang_startAfterEnd}}</span>
</div>
</div>
</div>
@@ -88,29 +91,43 @@
<div class="panel panel-default">
<div class="panel-heading"><label for="lecturelist">{{lang_autoStartLecture}}</label></div>
<div class="panel-body">
- <div class="row form-group">
- <div class="form-group col-xs-12">
- <p><i>{{lang_autoStartInfo}}</i></p>
- <div class="input-group">
- <span class="input-group-addon">
- <span class="glyphicon glyphicon-pencil"></span>
- </span>
- <select class="form-control" id="lecturelist" name="lectureid">
- <option value="">{{lang_none}}</option>
- {{#lectures}}
- <option data-from="{{starttime}}" data-to="{{endtime}}" value="{{lectureid}}" {{selected}} >{{displayname}}</option>
- {{/lectures}}
- </select>
- </div>
+ <div class="form-group">
+ <p><i>{{lang_autoStartInfo}}</i></p>
+ <div class="input-group">
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-pencil"></span>
+ </span>
+ <select class="form-control" id="lecturelist" name="lectureid">
+ <option value="">{{lang_none}}</option>
+ {{#lectures}}
+ <option data-from="{{starttime}}" data-to="{{endtime}}"
+ {{#islocationprivate}}data-locations="{{lids}}"{{/islocationprivate}}
+ value="{{lectureid}}" {{selected}} >{{displayname}}</option>
+ {{/lectures}}
+ </select>
</div>
- <div class="form-group col-xs-12">
- <div class="checkbox"><input id="autologin" type="checkbox" name="autologin" value="demo" class="form-control" {{#exam.autologin}}checked{{/exam.autologin}}><label for="autologin">{{lang_autoLogin}}</label></div>
- <p><i>{{lang_autoLoginInfo}}</i></p>
+ <b id="sanity-check" class="slx-smallspace collapse">{{lang_sanityCheck}}</b>
+ <div id="warn-range" class="collapse">
+ <div class="text-warning">
+ <div class="item start-before-lecture-start text-danger">{{lang_examStartBeforeLectureStart}}</div>
+ <div class="item start-after-lecture-start">{{lang_examStartAfterLectureStart}}</div>
+ <div class="item start-after-lecture-end text-danger">{{lang_examStartAfterLectureEnd}}</div>
+ <div class="item end-before-lecture-end">{{lang_examEndBeforeLectureEnd}}</div>
+ <div class="item end-after-lecture-end">{{lang_examEndAfterLectureEnd}}</div>
+ </div>
+ {{lang_lectureTimespan}}:
+ <span class="lecture-range"></span>
</div>
- <div class="col-xs-12" id="lecture-info">
- -
+ <div id="warn-locations" class="text-danger collapse">
+ {{lang_lectureNotForLocation}}:
+ <span class="locname"></span>
</div>
</div>
+ <div class="slx-space"></div>
+ <div class="form-group">
+ <div class="checkbox"><input id="autologin" type="checkbox" name="autologin" value="demo" class="form-control" {{#exam.autologin}}checked{{/exam.autologin}}><label for="autologin">{{lang_autoLogin}}</label></div>
+ <p><i>{{lang_autoLoginInfo}}</i></p>
+ </div>
</div>
</div>
@@ -126,7 +143,7 @@
<input type="hidden" name="examid" value="{{exam.examid}}">
<div class="text-right" style="margin-bottom: 20px">
<button type="button" id="cancelButton" class="btn btn-default" style="margin-right: 10px">{{lang_cancel}}</button>
- <button type="button" onclick="checkGlobalExam()" id="saveButton" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-disk"></span> {{lang_save}}</button>
+ <button type="button" id="saveButton" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-disk"></span> {{lang_save}}</button>
</div>
<div class ="modal fade" id="confirmGlobalModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
@@ -149,14 +166,6 @@
<script type="application/javascript"><!--
-function checkGlobalExam() {
- if ($('#locations option:selected').length === 0 && $('#locations option').length > 1) {
- $("#confirmGlobalModal").modal();
- } else {
- $('#tolleform').submit();
- }
-}
-
document.addEventListener("DOMContentLoaded", function () {
moment.locale(LANG);
@@ -175,7 +184,8 @@ document.addEventListener("DOMContentLoaded", function () {
$('.datepicker').datepicker(dateSettings);
$('.timepicker2').timepicker(timeSettings);
- $('#locations').multiselect({numberDisplayed: 1});
+ var $locations = $('#locations');
+ $locations.multiselect({numberDisplayed: 1});
var start_date = $('#starttime_date');
var start_time = $('#starttime_time');
@@ -197,54 +207,129 @@ document.addEventListener("DOMContentLoaded", function () {
}
});
+ var examStart, examEnd;
+
var startEndChanged = 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('-');
+ examStart = slxMoment(start_date.val() + ' ' + start_time.val(), 'YYYY-MM-DD H:mm');
+ examEnd = slxMoment(end_date.val() + ' ' + end_time.val(), 'YYYY-MM-DD H:mm');
+ if (!examStart.isValid() || !examEnd.isValid()) {
+ rspan.text($('#txt-invalid').text()).addClass('text-danger');
return;
}
- rspan.text(slxMoment.duration(ed.diff(sd)).humanize());
- // Lecture selection
- $('#lecturelist option').each(function (idx, elem) {
- var e = $(elem);
- var from = e.data('from');
- var to = e.data('to');
- if (!from || !to)
+ var diff = examEnd.diff(examStart);
+ if (diff <= 0) {
+ rspan.text($('#txt-reverse').text()).addClass('text-danger');
+ return;
+ }
+ rspan.text(slxMoment.duration(diff).humanize()).removeClass('text-danger');
+ updateLectureInfo();
+ };
+
+ var $timespanWarning = $('#warn-range');
+ var $locationWarning = $('#warn-locations');
+ var updateLectureInfo = function() {
+ (function() {
+ var $sel = $('#lecturelist option:selected');
+ var lectureStart = $sel.data('from');
+ var lectureEnd = $sel.data('to');
+ if (!lectureStart || !lectureEnd) {
+ $timespanWarning.hide();
return;
- from = slxMoment.unix(from);
- to = slxMoment.unix(to);
- if (from.isBefore(sd) || to.isAfter(ed)) {
- e.css('color', '#999');
- e.data('inrange', false)
+ }
+ lectureStart = slxMoment.unix(lectureStart);
+ lectureEnd = slxMoment.unix(lectureEnd);
+ var diff;
+ var warnings = [];
+ if (examStart.isBefore(lectureStart)) {
+ warnings.push('.start-before-lecture-start');
+ }
+ if (lectureEnd.diff(lectureStart, 'hours') >= 12) {
+ // Lecture is longer than 12 hours -- only consider exam start/end date outside range
+ if (lectureEnd.diff(examStart, 'minutes') < 15) { // Start after end, or very close to end
+ warnings.push('.start-after-lecture-end');
+ }
+ if (examEnd.diff(lectureEnd, 'minutes') > 15) {
+ warnings.push('.end-after-lecture-end');
+ }
} else {
- e.css('color', '');
- e.data('inrange', true);
+ // Lecture is shorter than 12 hours -- assume it's the actual timespan of the exam
+ if (examStart.diff(lectureStart, 'minutes') > 30) {
+ warnings.push('.start-after-lecture-start');
+ }
+ diff = examEnd.diff(lectureEnd, 'minutes');
+ if (diff > 15) {
+ warnings.push('.end-after-lecture-end');
+ } else if (diff < -30) {
+ warnings.push('.end-before-lecture-end');
+ }
}
- });
- updateLectureInfo();
+ if (warnings.length === 0) {
+ $timespanWarning.hide();
+ return;
+ }
+ $timespanWarning.find('.item').hide();
+ for (var i = 0; i < warnings.length; ++i) {
+ $timespanWarning.find(warnings[i]).show();
+ }
+ $timespanWarning.find('.lecture-range').text(slxMoment.unix($sel.data('from'))
+ .format('LLL') + ' – ' + slxMoment.unix($sel.data('to')).format('LLL'));
+ $timespanWarning.show();
+ })();
+ showHeading();
+ };
+ var updateLocationsInfo = function() {
+ (function() {
+ var selectedLocs = $locations.val();
+ var lecLocs = $('#lecturelist option:selected').data('locations');
+ if (!lecLocs || lecLocs.length === 0 || !selectedLocs) {
+ $locationWarning.hide();
+ return;
+ }
+ lecLocs = lecLocs.split(',');
+ if (!$.isArray(selectedLocs)) {
+ selectedLocs = [selectedLocs];
+ }
+ for (var i = 0; i < selectedLocs.length; ++i) {
+ if (lecLocs.indexOf(selectedLocs[i]) === -1) {
+ $locationWarning.find('.locname').text($locations.find('option[value="' + selectedLocs[i] + '"]').text());
+ $locationWarning.show();
+ return;
+ }
+ }
+ $locationWarning.hide();
+ })();
+ showHeading();
};
- var updateLectureInfo = function() {
- var sel = $('#lecturelist option:selected');
- if (sel.val() === '' || sel.data('inrange')) {
- $('#lecture-info').text('-');
+ $('#saveButton').click(function () {
+ if ($('#locations option:selected').length === 0 && $('#locations option').length > 1) {
+ $("#confirmGlobalModal").modal();
} else {
- $('#lecture-info').text('{{lang_lectureOutOfRange}} (' + slxMoment.unix(sel.data('from')).format('YYYY-MM-DD H:mm') + ' - ' + slxMoment.unix(sel.data('to')).format('YYYY-MM-DD H:mm') + ')');
+ $('#tolleform').submit();
+ }
+ });
+
+ var $sanity = $('#sanity-check');
+ var showHeading = function() {
+ if ($locationWarning.is(':visible') || $timespanWarning.is(':visible')) {
+ $sanity.show();
+ } else {
+ $sanity.hide();
}
};
+ startEndChanged();
+ updateLocationsInfo();
start_date.change(startEndChanged);
start_time.change(startEndChanged);
end_date.change(startEndChanged);
end_time.change(startEndChanged);
- $('#lecturelist').change(updateLectureInfo);
+ $('#lecturelist').change(updateLectureInfo).change(updateLocationsInfo);
+ $locations.change(updateLocationsInfo);
$("#cancelButton").click(function () {
- window.location.replace("?do=exams");
+ window.history.back();
});
- startEndChanged();
-
}, false);
// --></script>