From 71d3d4efb61d9171b5a5ec1120c1aa3cdd572366 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 28 Jul 2016 17:30:44 +0200 Subject: [exams] Add autostart lecture feature --- .../exams/baseconfig/getconfig.inc.php | 5 +- modules-available/exams/inc/exams.inc.php | 7 ++- modules-available/exams/lang/de/template-tags.json | 6 +++ modules-available/exams/lang/en/template-tags.json | 6 +++ modules-available/exams/page.inc.php | 32 +++++++---- .../exams/templates/page-add-edit-exam.html | 62 +++++++++++++++++++--- modules-available/exams/templates/page-exams.html | 11 ++-- 7 files changed, 107 insertions(+), 22 deletions(-) (limited to 'modules-available/exams') diff --git a/modules-available/exams/baseconfig/getconfig.inc.php b/modules-available/exams/baseconfig/getconfig.inc.php index d26a20a9..2776d3a8 100644 --- a/modules-available/exams/baseconfig/getconfig.inc.php +++ b/modules-available/exams/baseconfig/getconfig.inc.php @@ -2,7 +2,10 @@ if (isset($configVars["SLX_LOCATIONS"])) { $locationIds = explode(' ', $configVars["SLX_LOCATIONS"]); - if (Exams::isInExamMode($locationIds)) { + if (Exams::isInExamMode($locationIds, $lectureId)) { $configVars['SLX_EXAM'] = 'yes'; + if (strlen($lectureId) > 0) { + $configVars['SLX_EXAM_START'] = $lectureId; + } } } diff --git a/modules-available/exams/inc/exams.inc.php b/modules-available/exams/inc/exams.inc.php index f781fc1e..e95a9392 100644 --- a/modules-available/exams/inc/exams.inc.php +++ b/modules-available/exams/inc/exams.inc.php @@ -7,7 +7,7 @@ class Exams * @param int[] of location ids. must bot be an associative array. * @return: bool true iff for any of the given location ids an exam is scheduled. **/ - public static function isInExamMode($locationIds) + public static function isInExamMode($locationIds, &$lectureId) { if (!is_array($locationIds)) { $locationIds = array($locationIds); @@ -15,9 +15,12 @@ class Exams return false; } $l = str_repeat(',?', count($locationIds)); - $res = Database::queryFirst("SELECT examid FROM exams" + $res = Database::queryFirst("SELECT lectureid FROM exams" . " INNER JOIN exams_x_location USING (examid)" . " WHERE UNIX_TIMESTAMP() BETWEEN starttime AND endtime AND locationid IN (0$l) LIMIT 1", $locationIds); + if ($res !== false) { + $lectureId = $res['lectureid']; + } return $res !== false; } diff --git a/modules-available/exams/lang/de/template-tags.json b/modules-available/exams/lang/de/template-tags.json index 797cc371..22f777ad 100644 --- a/modules-available/exams/lang/de/template-tags.json +++ b/modules-available/exams/lang/de/template-tags.json @@ -3,6 +3,9 @@ "lang_addExam": "Zeitraum hinzuf\u00fcgen", "lang_addingBasedOnLecture": "F\u00fcge neuen Pr\u00fcfungszeitraum basierend auf vorhandener Veranstaltung an", "lang_allExamPeriods": "Alle Pr\u00fcfungszeitr\u00e4ume", + "lang_autoStartInfo": "W\u00e4hlen Sie hier optional eine Veranstaltung, die automatisch nach dem Booten der Rechner auf den Clients gestartet werden soll. Die Studierenden sehen keine Loginmaske und keinen vmChooser. Dadurch ist der Benutzer nicht angemeldet, und es steht u.a. kein Home-Verzeichnis zur Verf\u00fcgung. Diese Funktion eignet sich somit nur, wenn in der Pr\u00fcfung keine Autentifizierung des Benutzers notwendig ist, oder diese anderweitig innerhalb der VM-Sitzung umgesetzt wird, z.B. indem sich der Pr\u00fcfling gesondert auf einer Lernplattform im Browser anmeldet. Beachten Sie au\u00dferdem, dass die gew\u00e4hlte Veranstaltung zum Pr\u00fcfungszeitraum durch den Dozenten aktiviert sein muss. Wird keine Veranstaltung f\u00fcr den Autostart ausgew\u00e4hlt, sieht der Pr\u00fcfling nach wie vor eine Anmeldemaske und anschlie\u00dfend den vmChooser, in dem dann jedoch ausschlie\u00dflich als Pr\u00fcfung markierte Veranstaltungen aufgelistet werden.", + "lang_autoStartLecture": "Automatisch zu startende Veranstaltung", + "lang_autostart": "Autostart-Veranstaltung", "lang_begin": "Beginn", "lang_begin_date": "Beginn Datum", "lang_begin_time": "Uhrzeit", @@ -20,8 +23,11 @@ "lang_headingMain": "bwLehrpool Pr\u00fcfungsmodus", "lang_id": "ID", "lang_lectureName": "Veranstaltungsname", + "lang_lectureOutOfRange": "Achtung: Start- bzw. Endzeitpunkt der Veranstaltung liegen au\u00dferhalb des oben angegebenen Zeitraums", "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_noDescription": "Keine Beschreibung", + "lang_none": "(Keine)", "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 2e5abeff..b5ab87ae 100644 --- a/modules-available/exams/lang/en/template-tags.json +++ b/modules-available/exams/lang/en/template-tags.json @@ -3,6 +3,9 @@ "lang_addExam": "Add exam period", "lang_addingBasedOnLecture": "Adding exam period based on lecture", "lang_allExamPeriods": "All exam periods", + "lang_autoStartInfo": "Here you can select a course that will be launched automatically after the client finishes booting up. This will skip the login prompt and vmChooser screen, and directly start the selected couse. In this case, no personalization like mapping of home directories can happen in the VM, so this is mostly useful if the exam relies on other means of authentication, e.g. logging in to an LMS system where results need to be submitted.", + "lang_autoStartLecture": "Automatically launched course", + "lang_autostart": "Autorun lecture", "lang_begin": "Begin", "lang_begin_date": "Begin Date", "lang_begin_time": "Time", @@ -20,8 +23,11 @@ "lang_headingMain": "bwLehrpool exam mode", "lang_id": "ID", "lang_lectureName": "Lecture name", + "lang_lectureOutOfRange": "Hint: Start or end date of given lecture lies outside of exam period given above", "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_noDescription": "No description", + "lang_none": "(None)", "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 d6b1ccea..e70fc3c7 100644 --- a/modules-available/exams/page.inc.php +++ b/modules-available/exams/page.inc.php @@ -30,9 +30,12 @@ class Page_Exams extends Page protected function readExams() { - $tmp = Database::simpleQuery("SELECT examid, starttime, endtime, description, GROUP_CONCAT(locationid) AS locationids, " - . "GROUP_CONCAT(locationname SEPARATOR ', ') AS locationnames FROM exams " - . "NATURAL LEFT JOIN exams_x_location NATURAL LEFT JOIN location GROUP BY examid"); + $tmp = Database::simpleQuery("SELECT e.examid, l.displayname AS lecturename, e.starttime, e.endtime, e.description, GROUP_CONCAT(exl.locationid) AS locationids, " + . "GROUP_CONCAT(loc.locationname SEPARATOR ', ') AS locationnames FROM exams e " + . "NATURAL LEFT JOIN exams_x_location exl " + . "NATURAL LEFT JOIN location loc " + . "LEFT JOIN sat.lecture l USING (lectureid) " + . "GROUP BY examid "); while ($exam = $tmp->fetch(PDO::FETCH_ASSOC)) { $this->exams[] = $exam; } @@ -211,7 +214,8 @@ class Page_Exams extends Page $examid = Request::post('examid', 0, 'int'); $starttime = strtotime(Request::post('starttime_date') . " " . Request::post('starttime_time')); $endtime = strtotime(Request::post('endtime_date') . " " . Request::post('endtime_time')); - $description = Request::post('description'); + $description = Request::post('description', '', 'string'); + $lectureid = Request::post('lectureid', '', 'string'); if (!$this->isDateSane($starttime)) { Message::addError('starttime-invalid', Request::post('starttime_date') . " " . Request::post('starttime_time')); Util::redirect('?do=exams'); @@ -227,8 +231,8 @@ class Page_Exams extends Page if ($examid === 0) { // No examid given, is add - $res = Database::exec("INSERT INTO exams(starttime, endtime, description) VALUES(:starttime, :endtime, :description);", - compact('starttime', 'endtime', 'description')) !== false; + $res = Database::exec("INSERT INTO exams(lectureid, starttime, endtime, description) VALUES(:lectureid, :starttime, :endtime, :description);", + compact('lectureid', 'starttime', 'endtime', 'description')) !== false; $exam_id = Database::lastInsertId(); foreach ($locationids as $lid) { @@ -251,8 +255,8 @@ class Page_Exams extends Page } /* update fields */ - $res = Database::exec("UPDATE exams SET starttime = :starttime, endtime = :endtime, description = :description WHERE examid = :examid", - compact('starttime', 'endtime', 'description', 'examid')) !== false; + $res = Database::exec("UPDATE exams SET lectureid = :lectureid, starttime = :starttime, endtime = :endtime, description = :description WHERE examid = :examid", + compact('lectureid', 'starttime', 'endtime', 'description', 'examid')) !== false; /* drop all connections and reconnect to rooms */ $res = $res && Database::exec("DELETE FROM exams_x_location WHERE examid = :examid", compact('examid')) !== false; /* reconnect */ @@ -369,14 +373,17 @@ class Page_Exams extends Page $baseLecture = Request::any('lectureid', false, 'string'); $locations = null; if ($baseLecture !== false) { - foreach ($this->lectures as $lecture) { + foreach ($this->lectures as &$lecture) { if ($lecture['lectureid'] === $baseLecture) { $data['exam'] = $this->makeEditFromArray($lecture); $locations = explode(',', $lecture['lids']); + $lecture['selected'] = 'selected'; break; } } + unset($lecture); } + $data['lectures'] = $this->lectures; $this->readLocations($locations); $data['locations'] = $this->locations; Render::addTemplate('page-add-edit-exam', $data); @@ -385,7 +392,12 @@ class Page_Exams extends Page Render::setTitle(Dictionary::translate('title_edit-exam')); $exam = $this->makeEditFromArray($this->currentExam); - Render::addTemplate('page-add-edit-exam', ['exam' => $exam, 'locations' => $this->locations]); + foreach ($this->lectures as &$lecture) { + if ($lecture['lectureid'] === $this->currentExam['lectureid']) { + $lecture['selected'] = 'selected'; + } + } + Render::addTemplate('page-add-edit-exam', ['exam' => $exam, 'locations' => $this->locations, 'lectures' => $this->lectures]); } } diff --git a/modules-available/exams/templates/page-add-edit-exam.html b/modules-available/exams/templates/page-add-edit-exam.html index 3f0ef372..744aad29 100644 --- a/modules-available/exams/templates/page-add-edit-exam.html +++ b/modules-available/exams/templates/page-add-edit-exam.html @@ -12,6 +12,7 @@
+

{{lang_locationInfo}}

+ + {{#lectures}} + + {{/lectures}} + +
+ +
+ bla +
+ +
@@ -112,7 +134,7 @@ document.addEventListener("DOMContentLoaded", function () { $('.datepicker').datepicker(dateSettings); $('.timepicker2').timepicker(timeSettings); - showDuration(); + startEndChanged(); }); $('#locations').multiselect({numberDisplayed: 1}); @@ -137,7 +159,7 @@ document.addEventListener("DOMContentLoaded", function () { } }); - var showDuration = function () { + 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()) { @@ -145,12 +167,40 @@ document.addEventListener("DOMContentLoaded", function () { 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) + return; + from = slxMoment(from); + to = slxMoment(to); + if (from.isBefore(sd) || to.isAfter(ed)) { + e.css('color', '#999'); + e.data('inrange', false) + } else { + e.css('color', ''); + e.data('inrange', true); + } + }); + updateLectureInfo(); + } + + var updateLectureInfo = function() { + var sel = $('#lecturelist option:selected'); + if (sel.val() === '' || sel.data('inrange')) { + $('#lecture-info').text('-'); + } else { + $('#lecture-info').text('{{lang_lectureOutOfRange}} (' + slxMoment(sel.data('from')).format('YYYY-MM-DD H:mm') + ' - ' + slxMoment(sel.data('to')).format('YYYY-MM-DD H:mm') + ')'); + } } - start_date.change(showDuration); - start_time.change(showDuration); - end_date.change(showDuration); - end_time.change(showDuration); + start_date.change(startEndChanged); + start_time.change(startEndChanged); + end_date.change(startEndChanged); + end_time.change(startEndChanged); + $('#lecturelist').change(updateLectureInfo); }, false); // --> diff --git a/modules-available/exams/templates/page-exams.html b/modules-available/exams/templates/page-exams.html index 184a69e3..fc88e4f4 100644 --- a/modules-available/exams/templates/page-exams.html +++ b/modules-available/exams/templates/page-exams.html @@ -17,6 +17,11 @@ {{^locationnames}} {{lang_global}} {{/locationnames}} + {{#lecturename}} +
+ {{lang_autostart}}: {{lecturename}} +
+ {{/lecturename}}
{{description}} {{^description}} @@ -24,9 +29,9 @@ {{/description}}
- {{starttime_s}} - {{endtime_s}} - + {{starttime_s}} + {{endtime_s}} +
{{^liesInPast}} -- cgit v1.2.3-55-g7522