Compare commits

...

63 Commits

Author SHA1 Message Date
Johann Hoffmann 3e8e977eef export default statement in BaseApi.js 2026-01-26 10:20:11 +01:00
Johann Hoffmann 94eb7021bb created FhcBase.js plugin which coordinates time orderly setup between Alerts, Phrasen & Api Plugins. These have slightly rewritten Adaption as BaseAlerts.js, BasePhrase.js & BaseApi.js which have internal ready states activated once their required dependencies have been injected. Added await/async to several handlers to guarantee initialized cross dependencies in all plugins; While testing new Plugin Setup found a few legacy Instances of $fhcApi in Cis4 Studium.js that were only still working because plugins/Phrasen is still importing the legacyApi. Fixed that code and the corresponding errors in Studium.php that where now alerted into ui! wow! Also added FhcBase Plugin to all apps inside fhc-core/public/js/apps; 2026-01-23 13:48:04 +01:00
Andreas Österreicher ef8314c33a Config to check Extension LVEvaluierung is enabled 2026-01-21 15:32:52 +01:00
Andreas Österreicher e4ebdd59b6 Merge branch 'epic-56039/LV-Evaluierung' 2026-01-21 14:51:24 +01:00
Andreas Österreicher d1cc7244f7 Merge branch 'feature-70999/bewerbungstool_datenschutz_checkbox' 2026-01-21 10:56:54 +01:00
ma0048 2f5b90b9d5 dsgvo confirm und proctoring confirm beim RT Login 2026-01-21 09:04:11 +01:00
Cristina 9b90e0edde Merge branch 'master' into epic-56039/LV-Evaluierung 2026-01-20 10:53:36 +01:00
Cristina 56e9b253d2 Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-01-20 10:52:23 +01:00
Cristina c5c7123358 Added lvevaluierung phrase 2026-01-20 10:51:49 +01:00
Cristina 50e93fc9ba Fixed wrong configs (restored CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN + removed double CIS_EVALUIERUNG_ANZEIGEN_STG) 2026-01-19 13:33:12 +01:00
Andreas Österreicher 47ea1e848c Merge branch 'feature-69528/reihungstestueberwachung_constructor' 2026-01-19 10:17:38 +01:00
ma0048 e0f99a8e88 gewichtung und regeln in die config verschoben 2026-01-19 08:28:25 +01:00
Harald Bamberger 63aebde7f2 Merge branch 'bug-70906/FHC4_Archive_Dokument_Studienbestaetigung' 2026-01-15 18:01:25 +01:00
ma0068 62f5b66324 add missing parameters studiensemester and studiengang_kz for document Studienbestaetigung and StudienbestaetigungEnglish 2026-01-15 17:24:38 +01:00
Harald Bamberger ef08e39449 api endpoint student: prevent storing an empty string as alias e.g. from StudVw Details Tab 2026-01-15 17:09:30 +01:00
Harald Bamberger c720c46125 education/Projektarbeit_model: fix missing from-clause entry for table tbl_projektarbeit error - ticket#6059687 2026-01-15 16:28:38 +01:00
Harald Bamberger 597d4dbfec Abschusspruefung Modal add model-type to datepicker to prevent sending full utc timestamp to backend 2026-01-15 11:37:51 +01:00
ma0048 d67cad925d externe ueberwachung deaktivieren ueber config
params in die config verlagert
2026-01-14 13:59:42 +01:00
Harald Bamberger 2d9e9df8a0 fix notiz component showing no data when setting sort an switching tabs in stv 2026-01-13 14:10:33 +01:00
Cristina 6d6659f6e5 Merge remote-tracking branch 'origin/epic-56039/LV-Evaluierung' into epic-56039/LV-Evaluierung 2026-01-13 13:39:21 +01:00
Cristina 6512ad0f1c Merge branch 'master' into epic-56039/LV-Evaluierung 2026-01-13 13:38:04 +01:00
Cristina f034a99928 Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-01-13 13:37:43 +01:00
ma0048 40ae57fb0c proctoring check ueberarbeitet 2026-01-08 12:01:20 +01:00
Andreas Österreicher 7e6805ca98 Merge branch 'master' into epic-56039/LV-Evaluierung 2025-12-18 13:18:16 +01:00
Cristina 871534af52 Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2025-12-18 13:07:27 +01:00
Cristina 4dd4d8aaa5 Removed filtering aktiv studienganege from method to get Studiengaenge by Stgs
otherwise when switching to former semester (which will have inactive studiengaenge) it will not appear
2025-12-17 14:35:24 +01:00
Cristina f9ac824cc0 Added LV-Evaluierung NEU link button to CIS LV Menu 2025-12-17 14:33:04 +01:00
Cristina db048e876c Renamed getEntitledStgs to getByStgs
Permissions check happens in application controller.
2025-12-15 16:48:07 +01:00
Cristina e6ef234c8b Added method getOrgformsByStg to Studiengang_model.php
Get OrgForms of given Studiengang and Studiensemester
2025-12-15 16:27:47 +01:00
ma0048 5a77478071 insertvon entfernt 2025-12-15 12:27:17 +01:00
Cristina 8280ec42b0 Added method getEntitledStgs to Studiengang_model.php
Get active Studiengänge with Kuerzel by given Studiengang-Kennzahlen. Helpful to easily get Studiengänge the user is entitled for.
2025-12-11 16:07:07 +01:00
ma0048 df639c8b07 sprache mitschicken 2025-12-11 08:28:01 +01:00
ma0048 48512f46ab exam name geandert 2025-12-10 15:15:23 +01:00
ma0048 8bab5285e9 testversuch ueber config
abfrage gefixed
menu wird nicht mehr angezeigt
2025-12-10 08:08:46 +01:00
Cristina 76cdaefdc7 Merge branch 'master' into epic-56039/LV-Evaluierung 2025-12-09 17:06:06 +01:00
Cristina f725594f0c Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2025-12-09 17:00:24 +01:00
ma0048 d8e57d43f9 session url in die config verschoben 2025-12-02 13:53:38 +01:00
ma0048 5c9710f44d externe ueberwachung v1 2025-12-02 13:49:05 +01:00
Cristina 92030ca697 Merge branch 'feature-69379/Tooltip-Direktive' into epic-56039/LV-Evaluierung 2025-11-18 11:02:57 +01:00
Cristina 993dff3351 Gendered phrase lektorInnen (FHTW style) 2025-11-13 11:11:37 +01:00
Cristina 0a118a1427 Clustered LV-Evaluierung phrases after merge 2025-11-13 10:50:36 +01:00
Cristina 4ab0c0a44e Merge branch 'master' into epic-56039/LV-Evaluierung
# Conflicts:
#	system/dbupdate_3.4.php
#	system/phrasesupdate.php
2025-11-13 10:44:39 +01:00
Cristina 43281c6ba2 Removed test buttons from FhcChart basic template 2025-11-13 09:48:54 +01:00
Cristina 343964750b Merge branch 'master' into epic-56039/LV-Evaluierung
# Conflicts:
#	system/dbupdate_3.4.php
#	system/phrasesupdate.php
2025-10-14 17:22:39 +02:00
Cristina 2aee86baba Changed phrase lvevaluierung/logoutText 2025-10-14 16:16:23 +02:00
Cristina 0d51df95f9 Merge branch 'master' into epic-56039/LV-Evaluierung
# Conflicts:
#	system/dbupdate_3.4.php
#	system/phrasesupdate.php
2025-10-02 11:30:55 +02:00
Cristina deebe987d3 Added slot selectedItem to autocomplete component in Input.js 2025-10-01 13:51:43 +02:00
Cristina cb77f3148a Added method to get Stundenplantermine for Lehreinheit/Lehrveranstaltung 2025-10-01 13:50:57 +02:00
Cristina cb64c83b50 Added method to get LV-Leitung from given LV 2025-10-01 13:48:10 +02:00
Cristina ed747fe07c Merge remote-tracking branch 'origin/epic-56039/LV-Evaluierung' into epic-56039/LV-Evaluierung
# Conflicts:
#	application/models/education/Lehrveranstaltung_model.php
2025-09-03 15:26:39 +02:00
Cristina 10d58d1a42 Added column prestudent_id to getStudentsByLv method query in Lehrveranstaltung_model.php 2025-09-03 15:24:44 +02:00
Cristina fd2ff27e22 Added column prestudent_id to getStudentsByLv method query in Lehrveranstaltung_model.php 2025-09-03 15:24:07 +02:00
Cristina 594333889d Merge branch 'epic-56039/LV-Evaluierung_NEU' of https://github.com/FH-Complete/FHC-Core into epic-56039/LV-Evaluierung
# Conflicts:
#	system/dbupdate_3.4.php
#	system/phrasesupdate.php
2025-09-03 15:14:53 +02:00
Cristina 94e25da6c8 Added phrases for lvevaluierung 2025-07-16 16:13:27 +02:00
Cristina 430da0b236 Merge branch 'master' into epic-56039/LV-Evaluierung_NEU 2025-07-16 11:22:42 +02:00
Cristina 0df082af1f Merge branch 'master' into epic-56039/LV-Evaluierung_NEU 2025-07-02 12:16:19 +02:00
Cristina 56bfe46675 Added phrases for lvevaluierung 2025-07-02 12:12:44 +02:00
Cristina daf9d2ea15 Added checkbox 'evaluierung' Lehrveranstaltung Details in VILESCI 2025-06-25 13:35:20 +02:00
Cristina 4b8af46a2e Added 'evaluierung' to lehrveranstaltung.class.php and updated related methods 2025-06-25 13:30:05 +02:00
Cristina fd2de106f8 Added column evaluierung to lehre.tbl_lehrveranstaltung
- evaluierung (boolean) indicates if lehrveranstaltung shall be evaluated. Default is true.
2025-06-24 15:02:45 +02:00
Cristina 08182fe0f5 Added phrases for LV-Evaluierung 2025-06-23 15:51:30 +02:00
Cristina 1e32bc211b Merge branch 'master' into epic-56039/LV-Evaluierung_NEU 2025-06-16 09:36:04 +02:00
Cristina 5ee72fd836 Merge branch 'master' into epic-56039/LV-Evaluierung_NEU 2025-05-27 18:23:51 +02:00
63 changed files with 2609 additions and 109 deletions
@@ -29,7 +29,7 @@ class Studium extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getStudienAllSemester'=> self::PERM_LOGGED,
'getAllStudienSemester'=> self::PERM_LOGGED,
'getStudiengaengeForStudienSemester'=> self::PERM_LOGGED,
'getStudienplaeneBySemester'=> self::PERM_LOGGED,
'getLvEvaluierungInfo'=> self::PERM_LOGGED,
@@ -51,7 +51,7 @@ class Studium extends FHCAPI_Controller
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function getStudienAllSemester(){
public function getAllStudienSemester(){
$parameter_studiensemester = $this->input->get('studiensemester',true);
$parameter_studiengang = $this->input->get('studiengang',true);
@@ -59,24 +59,26 @@ class Studium extends FHCAPI_Controller
$parameter_studienplan = $this->input->get('studienplan',true);
$aktuelles_studiensemester = current($this->getDataOrTerminateWithError($this->StudiensemesterModel->getAktOrNextSemester()));
if($this->getDataOrTerminateWithError($this->StudentModel->isStudent(getAuthUID()))){
$studentLehrverband =$this->StudentlehrverbandModel->loadWhere(["student_uid" => getAuthUID(), "studiensemester_kurzbz" => $aktuelles_studiensemester->studiensemester_kurzbz]);
$studentLehrverband = current($this->getDataOrTerminateWithError($studentLehrverband));
$lv_result =$this->StudentlehrverbandModel->loadWhere([
"student_uid" => getAuthUID(),
"studiensemester_kurzbz" => $aktuelles_studiensemester->studiensemester_kurzbz
]);
$lv_data = $this->getDataOrTerminateWithError($lv_result);
if ($studentLehrverband = current($lv_data)) {
$student_studiensemester = $studentLehrverband->studiensemester_kurzbz;
$student_studiengang = $studentLehrverband->studiengang_kz;
$student_semester = $studentLehrverband->semester;
}
$student_studiensemester = $studentLehrverband->studiensemester_kurzbz;
$student_studiengang = $studentLehrverband->studiengang_kz;
$student_semester = $studentLehrverband->semester;
$student_studienplan = $this->getStudienPlanFromPrestudentStatus(getAuthPersonId())->studienplan_id;
if(!isset($parameter_studiensemester))
$parameter_studiensemester = $student_studiensemester;
if(!isset($parameter_studiengang))
$parameter_studiengang = $student_studiengang;
if(!isset($parameter_semester))
$parameter_semester = $student_semester;
if(!isset($parameter_studienplan))
$parameter_studienplan = $student_studienplan;
$parameter_studiensemester = $parameter_studiensemester ?? $student_studiensemester;
$parameter_studiengang = $parameter_studiengang ?? $student_studiengang;
$parameter_semester = $parameter_semester ?? $student_semester;
$parameter_studienplan = $parameter_studienplan ?? $student_studienplan;
}
if(isset($parameter_studiensemester)){
@@ -596,8 +596,8 @@ class Dokumente extends FHCAPI_Controller
buildDropdownEntryPrintArray("bescheid", "Bescheid (nur Voransicht)", "xml=abschlusspruefung.rdf.php&xsl_stg_kz=$studiengang_kz&xsl=Bescheid&output=pdf", $uid, 25, null),
buildDropdownEntryPrintArray("diplomasupp", "Diploma Supplement (nur Voransicht)", "xml=diplomasupplement.xml.php&xsl_stg_kz=$studiengang_kz&xsl=DiplSupplement&output=pdf", $uid, 26, null),
buildDropdownEntryPrintArray("studienbestaetigung", "Studienbestätigung", "xml=student.rdf.php&xsl=Inskription&output=pdf", $uid, 50, null),
buildDropdownEntryPrintArray("studienbestaetigung_en", "Studienbestätigung Englisch", "xml=student.rdf.php&xsl=InskriptionEng&output=pdf", $uid, 51, null),
buildDropdownEntryPrintArray("studienbestaetigung", "Studienbestätigung", "xml=student.rdf.php&xsl=Inskription&output=pdf&ss=$studiensemester_kurzbz&xsl_stg_kz=$studiengang_kz", $uid, 50, null),
buildDropdownEntryPrintArray("studienbestaetigung_en", "Studienbestätigung Englisch", "xml=student.rdf.php&xsl=InskriptionEng&output=pdf&ss=$studiensemester_kurzbz&xsl_stg_kz=$studiengang_kz", $uid, 51, null),
buildDropdownEntryPrintArray("zutrittskarte", "Zutrittskarte", "xsl=ZutrittskarteStud&output=pdf&data=$uid", $uid,200, "zutrittskarte.php"),
buildDropdownEntryPrintArray("studienblatt", "Studienblatt", "xml=studienblatt.xml.php&xsl=Studienblatt&output=pdf&ss=$studiensemester_kurzbz", $uid, 60, null),
buildDropdownEntryPrintArray("studienblatt_eng", "Studienblatt Englisch", "xml=studienblatt.xml.php&xsl=StudienblattEng&output=pdf&ss=$studiensemester_kurzbz", $uid, 61, null),
@@ -686,8 +686,8 @@ class Dokumente extends FHCAPI_Controller
buildDropdownEntryPrintArray("accountinfo", "Accountinfoblatt", "xml=accountinfoblatt.xml.php&xsl=AccountInfo&output=pdf", $uidString, 10, null),
buildDropdownEntryPrintArray("ausbildungsvertrag", "Ausbildungsvertrag", "xml=ausbildungsvertrag.xml.php&xsl=Ausbildungsver&output=pdf", $uidString, 20, null),
buildDropdownEntryPrintArray("ausbildungsvertrag_en", "Ausbildungsvertrag Englisch", "xml=ausbildungsvertrag.xml.php&xsl=AusbVerEng&output=pdf", $uidString, 21, null),
buildDropdownEntryPrintArray("studienbestaetigung", "Studienbestätigung", "xml=student.rdf.php&xsl=Inskription&output=pdf", $uidString, 50, null),
buildDropdownEntryPrintArray("studienbestaetigung_en", "Studienbestätigung Englisch", "xml=student.rdf.php&xsl=InskriptionEng&output=pdf", $uidString, 51, null),
buildDropdownEntryPrintArray("studienbestaetigung", "Studienbestätigung", "xml=student.rdf.php&xsl=Inskription&output=pdf&ss=$studiensemester_kurzbz&xsl_stg_kz=$studiengang_kz", $uidString, 50, null),
buildDropdownEntryPrintArray("studienbestaetigung_en", "Studienbestätigung Englisch", "xml=student.rdf.php&xsl=InskriptionEng&output=pdf&ss=$studiensemester_kurzbz&xsl_stg_kz=$studiengang_kz", $uidString, 51, null),
buildDropdownEntryPrintArray("zutrittskarte", "Zutrittskarte", "xsl=ZutrittskarteStud&output=pdf&data=$uidString", $uidString,200, "zutrittskarte.php"),
buildDropdownEntryPrintArray("studienblatt", "Studienblatt", "xml=studienblatt.xml.php&xsl=Studienblatt&output=pdf&ss=$studiensemester_kurzbz", $uidString, 60, null),
buildDropdownEntryPrintArray("studienblatt_eng", "Studienblatt Englisch", "xml=studienblatt.xml.php&xsl=StudienblattEng&output=pdf&ss=$studiensemester_kurzbz", $uidString, 61, null),
@@ -321,6 +321,10 @@ class Student extends FHCAPI_Controller
foreach ($array_allowed_props_benutzer as $prop) {
$val = $this->input->post($prop);
if ($val !== null) {
if($prop === 'alias' && $val === '')
{
$val = null;
}
$update_benutzer[$prop] = $val;
}
}
@@ -316,8 +316,8 @@ class Lehrveranstaltung_model extends DB_Model
(SELECT status_kurzbz FROM public.tbl_prestudentstatus WHERE prestudent_id=tbl_student.prestudent_id ORDER BY datum DESC, insertamum DESC, ext_id DESC LIMIT 1) as status,
tbl_bisio.bisio_id, tbl_bisio.von, tbl_bisio.bis, tbl_student.studiengang_kz AS stg_kz_student,
tbl_zeugnisnote.note, tbl_mitarbeiter.mitarbeiter_uid, tbl_person.matr_nr, tbl_benutzer.uid,
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as kuerzel, tbl_studiengang.orgform_kurzbz, vw_student_lehrveranstaltung.semester, vw_student_lehrveranstaltung.studiensemester_kurzbz, vw_student_lehrveranstaltung.bezeichnung
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as kuerzel, tbl_studiengang.orgform_kurzbz, vw_student_lehrveranstaltung.semester, vw_student_lehrveranstaltung.studiensemester_kurzbz, vw_student_lehrveranstaltung.bezeichnung,
tbl_student.prestudent_id
FROM
campus.vw_student_lehrveranstaltung
JOIN public.tbl_benutzer USING(uid)
@@ -386,6 +386,37 @@ class Lehrveranstaltung_model extends DB_Model
return $this->execQuery($query, array($lehrveranstaltung_id, $studiensemester_kurzbz));
}
/**
* Get LV-Leitung of given Lehrveranstaltung ID and Studiensemester.
*
* @param $lehrveranstaltung_id
* @param $studiensemester
* @return array|stdClass|null
*/
public function getLvLeitung($lehrveranstaltung_id, $studiensemester)
{
$params = [$lehrveranstaltung_id, $studiensemester];
$qry = "
SELECT
vorname, nachname, mitarbeiter_uid, lehrfunktion_kurzbz
FROM
lehre.tbl_lehreinheit
JOIN lehre.tbl_lehreinheitmitarbeiter lema USING (lehreinheit_id)
JOIN public.tbl_benutzer b ON b.uid = lema.mitarbeiter_uid
JOIN public.tbl_person p using (person_id)
WHERE
tbl_lehreinheit.lehrveranstaltung_id= ?
AND tbl_lehreinheit.studiensemester_kurzbz = ?
AND lehrfunktion_kurzbz = 'LV-Leitung'
ORDER BY
lema.insertamum DESC
LIMIT 1
";
return $this->execQuery($qry, $params);
}
/**
* Gets all Leiter of Lehrveranstaltungsorganisationseinheit
* @param $lehrveranstaltung_id
@@ -64,16 +64,16 @@ class Projektarbeit_model extends DB_Model
if (isset($projekttyp))
{
if (is_array($projekttyp))
$qry .= ' AND tbl_projektarbeit.projekttyp_kurzbz IN ?';
$qry .= ' AND pa.projekttyp_kurzbz IN ?';
else
$qry .= ' AND tbl_projektarbeit.projekttyp_kurzbz=?';
$qry .= ' AND pa.projekttyp_kurzbz=?';
$params[] = $projekttyp;
}
if (isset($final))
{
$qry .= ' AND tbl_projektarbeit.final=?';
$qry .= ' AND pa.final=?';
$params[] = $final;
}
@@ -801,4 +801,73 @@ class Studiengang_model extends DB_Model
return $this->execReadOnlyQuery($qry, array($studiengang_kz, $orgform_kurzbz, $studiensemester_kurzbz));
}
/**
* Get active Studiengänge with Kuerzel by given Studiengang-Kennzahlen.
* Helpful to easily get Studiengänge the user is entitled for.
*
* @param $studiengang_kz_arr
* @param $studiensemester_kurzbz
* @return array|stdClass|null Returns one row per Studiengang. Not considering the Orgforms.
*/
public function getByStgs($studiengang_kz_arr, $studiensemester_kurzbz)
{
if (is_numeric($studiengang_kz_arr))
{
$studiengang_kz_arr = [$studiengang_kz_arr];
}
$qry = '
SELECT
DISTINCT stg.*, UPPER(typ::varchar(1) || kurzbz) AS kuerzel
FROM
public.tbl_studiengang stg
JOIN lehre.tbl_studienordnung sto USING(studiengang_kz)
JOIN lehre.tbl_studienplan stpl USING(studienordnung_id)
JOIN lehre.tbl_studienplan_semester stplsem USING(studienplan_id)
WHERE
stg.studiengang_kz IN ?
AND stplsem.studiensemester_kurzbz = ?
ORDER BY
stg.kurzbzlang
';
return $this->execQuery($qry, [$studiengang_kz_arr, $studiensemester_kurzbz]);
}
/**
* Get OrgForms of given Studiengang and Studiensemester.
*
* @param $studiengang_kz
* @param $studiensemester_kurzbz
* @return array|stdClass|null
*/
public function getOrgformsByStg($studiengang_kz, $studiensemester_kurzbz)
{
$qry = '
SELECT
stpl.orgform_kurzbz
FROM
public.tbl_studiengang stg
JOIN lehre.tbl_studienordnung sto USING(studiengang_kz)
JOIN lehre.tbl_studienplan stpl USING(studienordnung_id)
JOIN lehre.tbl_studienplan_semester stplsem USING(studienplan_id)
WHERE
stg.studiengang_kz = ?
AND stg.aktiv = TRUE
AND stplsem.studiensemester_kurzbz = ?
GROUP BY
stpl.orgform_kurzbz
ORDER BY
CASE stpl.orgform_kurzbz
WHEN \'VZ\' THEN 1
WHEN \'BB\' THEN 2
WHEN \'DUA\' THEN 3
ELSE 4
END,
stpl.orgform_kurzbz;
';
return $this->execQuery($qry, [$studiengang_kz, $studiensemester_kurzbz]);
}
}
@@ -535,4 +535,53 @@ class Stundenplan_model extends DB_Model
return $this->execQuery($query, [$uid, $uid]);
}
/**
* Get Stundenplantermine for given Lehreinheit.
*
* @param $lehreinheit_id
* @return array|stdClass|null
*/
public function getTermineByLe($lehreinheit_id)
{
$qry = '
SELECT DISTINCT
datum
FROM
lehre.vw_stundenplan
WHERE
lehreinheit_id = ?
ORDER BY
datum ASC
';
return $this->execQuery($qry, [$lehreinheit_id]);
}
/**
* Get Stundenplantermine for given Lehrveranstaltung of given Studiensemester.
*
* @param $lehrveranstaltung_id
* @param $studiensemester_kurzbz
* @return array|stdClass|null
*/
public function getTermineByLv($lehrveranstaltung_id, $studiensemester_kurzbz)
{
$qry = '
SELECT DISTINCT
datum
FROM
lehre.vw_stundenplan
WHERE
lehreinheit_id IN (
SELECT lehreinheit_id
FROM lehre.tbl_lehreinheit
WHERE lehrveranstaltung_id = ?
AND studiensemester_kurzbz = ?
)
ORDER BY datum ASC
';
return $this->execQuery($qry, [$lehrveranstaltung_id, $studiensemester_kurzbz]);
}
}
+15
View File
@@ -0,0 +1,15 @@
(function () {
function sendMessage() {
let frame = window.frames['content'];
if (frame)
frame.postMessage({ type: "proctoringReady" });
}
window.addEventListener("message", function (e)
{
if (e.data.indexOf("proctoringReady_") === 0)
{
sendMessage();
}
});
})();
+30
View File
@@ -0,0 +1,30 @@
.proctoring-blocker
{
position: fixed;
inset: 0;
z-index: 99999;
backdrop-filter: blur(6px);
pointer-events: all;
user-select: none;
}
.proctoring-blocker.hidden
{
display: none !important;
}
.proctoring-text
{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
font-size: 16px;
font-family: sans-serif;
}
.proctoring-blur-fallback
{
filter: blur(6px);
}
+7 -1
View File
@@ -45,7 +45,7 @@ if (!$db = new basis_db())
$PHP_SELF=$_SERVER["PHP_SELF"];
// Start session
session_start();
require_once './session_init.php';
// If language is changed by language select menu, reset language variables
if (isset($_GET['sprache_user']) && !empty($_GET['sprache_user']))
@@ -182,6 +182,12 @@ echo '
if(!isset($_SESSION['pruefling_id']))
die($p->t('testtool/bitteZuerstAnmelden'));
if (!empty($_SESSION['externe_ueberwachung']) && isset($_SESSION['externe_ueberwachung_verified'])): ?>
<link href="frage.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="frage_externe_ueberwachung.js"></script>
<div id="proctoringBlocker" class="proctoring-blocker hidden"></div>
<?php endif;
$pruefling = new pruefling();
$pruefling->load($_SESSION['pruefling_id']);
@@ -0,0 +1,53 @@
(function () {
let ok = false;
let blocker;
function showBlocker() {
blocker = document.getElementById("proctoringBlocker");
if (!blocker)
{
blocker = document.createElement("div");
blocker.id = "proctoringBlocker";
blocker.className = "proctoring-blocker";
blocker.innerHTML = '<div class="proctoring-text">Loading...</div>';
document.body.appendChild(blocker);
}
document.documentElement.classList.add("proctoring-blur-fallback");
}
function block() {
showBlocker();
blocker.classList.remove("hidden");
}
function unblock() {
document.documentElement.classList.remove("proctoring-blur-fallback");
if (!blocker) return;
blocker.classList.add("hidden");
}
const blockTimer = setTimeout(function () {
if (!ok)
block();
}, 1500);
window.addEventListener("message", function (e) {
const data = e.data || {};
if (data.type === "proctoringReady")
{
ok = true;
clearTimeout(blockTimer);
unblock();
}
});
setTimeout(function () {
if (!ok) {
top.location.href = "resetconnection.php";
}
}, 3000);
})();
+64
View File
@@ -1,16 +1,79 @@
<?php
$contentpage = 'login.php';
require_once './session_init.php';
if(isset($_GET['prestudent']) && is_numeric($_GET['prestudent']))
{
$contentpage = 'login.php?prestudent='.$_GET['prestudent'];
}
if ((isset($_SESSION['externe_ueberwachung']) && $_SESSION['externe_ueberwachung'] === true) &&
isset($_SESSION['externe_ueberwachung_verified']) && $_SESSION['externe_ueberwachung_verified'] === false)
{
header("Location: resetconnection.php");
exit;
}
?><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<title>TestTool - FH Technikum Wien</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="../../skin/style.css.php" rel="stylesheet" type="text/css">
<?php
if (!empty($_SESSION['externe_ueberwachung'])) : ?>
<script type="text/javascript" src="externeueberwachung.js"></script>
<script>
function loadInContent(url)
{
if (url.includes('logout=true'))
{
return doLogout(url);
}
let frame = document.getElementById('content_testtool');
if (frame)
{
frame.src = url;
}
}
function doLogout(url)
{
fetch(url)
let topbarFrame = window.frames['topbar'];
let menuFrame = window.frames['menu'];
let contentFrame = window.frames['content'];
if (contentFrame)
contentFrame.location.href = 'logout.html';
if (menuFrame)
menuFrame.location.href = menuFrame.location.pathname;
if (topbarFrame)
topbarFrame.location.href = topbarFrame.location.pathname;
return false;
}
function changeSprache(content_params, sprache)
{
let topbarFrame = window.frames['topbar'];
let menuFrame = window.frames['menu'];
let contentFrame = window.frames['content'];
if (topbarFrame)
topbarFrame.location.href = topbarFrame.location.pathname + '?sprache_user=' + sprache;
if (menuFrame)
menuFrame.location.href = menuFrame.location.pathname + '?sprache_user=' + sprache;
if (contentFrame)
contentFrame.location.href = contentFrame.location.pathname + '?' + content_params;
}
</script>
<?php endif; ?>
</head>
<frameset rows="13%,*" cols="*" frameborder="NO" border="0" framespacing="0">
@@ -26,3 +89,4 @@ if(isset($_GET['prestudent']) && is_numeric($_GET['prestudent']))
</noframes>
</frameset>
</html>
+23 -4
View File
@@ -40,8 +40,7 @@ if (!$db = new basis_db())
die('Fehler beim Oeffnen der Datenbankverbindung');
// Start session
session_start();
require_once './session_init.php';
// Logout (triggered by logout button in menu.php)
if (isset($_GET['logout']) && $_GET['logout'] == true)
{
@@ -173,6 +172,12 @@ if (isset($_REQUEST['prestudent']))
else
$reload_menu = true;
}
if ($rt->externe_ueberwachung && defined('TESTTOOL_EXTERNE_UEBERWACHUNG_ALLOWED') && TESTTOOL_EXTERNE_UEBERWACHUNG_ALLOWED)
{
$_SESSION['externe_ueberwachung'] = true;
$_SESSION['externe_ueberwachung_verified'] = false;
}
}
$pruefling = new pruefling();
@@ -339,6 +344,8 @@ if ((isset($_SESSION['prestudent_id']) && !isset($_SESSION['pruefling_id']) &&
!isset($_SESSION['confirmation_needed']) && !isset($_SESSION['confirmed_code'])) ||
(isset($_SESSION['confirmation_needed']) && $_SESSION['confirmation_needed'] === true &&
isset($_SESSION['confirmed_code']) && $_SESSION['confirmed_code'] === true &&
isset($_SESSION['externe_ueberwachung']) && $_SESSION['externe_ueberwachung'] === true &&
isset($_SESSION['externe_ueberwachung_verified']) && $_SESSION['externe_ueberwachung_verified'] === true &&
isset($_SESSION['prestudent_id']) && !isset($_SESSION['pruefling_id'])))
{
$pruefling = new pruefling();
@@ -460,7 +467,13 @@ if (isset($_POST['save']) && isset($_SESSION['prestudent_id']))
<?php
if (isset($_SESSION['confirmation_needed']) && $_SESSION['confirmation_needed'] === true &&
if ((isset($_SESSION['externe_ueberwachung']) && $_SESSION['externe_ueberwachung'] === true) &&
isset($_SESSION['externe_ueberwachung_verified']) && $_SESSION['externe_ueberwachung_verified'] === false)
{
echo "<script> top.location.href = 'resetconnection.php';</script>";
exit;
}
else if (isset($_SESSION['confirmation_needed']) && $_SESSION['confirmation_needed'] === true &&
isset($_SESSION['confirmed_code']) && $_SESSION['confirmed_code'] === false)
{
echo '
@@ -718,7 +731,7 @@ else // LOGIN Site (vor Login)
echo '<script>
function changeconfirmation()
{
document.getElementById("confirmationSubmit").disabled = !document.getElementById("confirmationCheckbox").checked;
document.getElementById("confirmationSubmit").disabled = !document.getElementById("confirmationCheckbox").checked || !document.getElementById("dsgvoconfirm").checked || !document.getElementById("procotoringconfirm").checked;
}
</script>';
echo '<div class="row text-center">
@@ -728,6 +741,12 @@ else // LOGIN Site (vor Login)
<input id="confirmationCheckbox" type="checkbox" name="confirmation" onclick="changeconfirmation()" />
'.$p->t('testtool/confirmationText').'
<br><br>
<input id="dsgvoconfirm" type="checkbox" name="confirmation" onclick="changeconfirmation()" />
'.$p->t('testtool/dsgvoConfirmText').'
<br><br>
<input id="procotoringconfirm" type="checkbox" name="confirmation" onclick="changeconfirmation()" />
'.$p->t('testtool/procotoringConfirmText').'
<br><br>
<button id="confirmationSubmit" type="submit" class="btn btn-primary" disabled/>
'.$p->t('testtool/start').'
</button>
+31 -7
View File
@@ -34,7 +34,7 @@ if (!$db = new basis_db())
die('Fehler beim Oeffnen der Datenbankverbindung');
// Start session
session_start();
require_once './session_init.php';
// If language is changed by language select menu, reset language and session variables
if(isset($_GET['sprache_user']) && !empty($_GET['sprache_user']))
@@ -61,8 +61,12 @@ $p = new phrasen($sprache_user);
<?php
$gebiet_hasMathML = false; // true, wenn irgendein Gebiet eine/n Frage/Vorschlag im MathML-Format enthält
$invalid_gebiete = false;
if (isset($_SESSION['pruefling_id']))
if ((isset($_SESSION['externe_ueberwachung']) && $_SESSION['externe_ueberwachung'] === true) &&
isset($_SESSION['externe_ueberwachung_verified']) && $_SESSION['externe_ueberwachung_verified'] === false)
{
exit;
}
else if (isset($_SESSION['pruefling_id']))
{
//content_id fuer Einfuehrung auslesen
$qry = "SELECT content_id FROM testtool.tbl_ablauf_vorgaben WHERE studiengang_kz=".$db->db_add_param($_SESSION['studiengang_kz'])." LIMIT 1";
@@ -73,7 +77,7 @@ if (isset($_SESSION['pruefling_id']))
// Link zur Startseite
echo '<tr><td class="ItemTesttool" style="margin-left: 20px;" nowrap>
<a class="ItemTesttool navButton" href="login.php" target="content">'.$p->t('testtool/startseite').'</a>
<a class="ItemTesttool navButton" href="login.php" onclick="return loadContent(this.href);">'.$p->t('testtool/startseite').'</a>
</td></tr>';
// Link zur Einleitung
@@ -83,7 +87,7 @@ if (isset($_SESSION['pruefling_id']))
{
echo '
<tr id="tr-einleitung"><td class="ItemTesttool" style="margin-left: 20px;" nowrap>
<a class="ItemTesttool navButton" href="../../cms/content.php?content_id='.$content_id->content_id.'&sprache='.$sprache_user.'" target="content">'.$p->t('testtool/einleitung').'</a>
<a class="ItemTesttool navButton" href="../../cms/content.php?content_id='.$content_id->content_id.'&sprache='.$sprache_user.'" onclick="return loadContent(this.href);">'.$p->t('testtool/einleitung').'</a>
</td></tr>
';
}
@@ -379,10 +383,13 @@ if (isset($_SESSION['pruefling_id']))
}
}
echo '<tr>
<!--<td width="10" class="ItemTesttoolLeft" nowrap>&nbsp;</td>-->
<td class="'.$class.'">
<a class="'.$class.'" href="frage.php?gebiet_id='.$row->gebiet_id.'" onclick="document.location.reload()" target="content" style="'.$style.'">'.$gebietbezeichnung.'</a>
<a class="'.$class.'" href="frage.php?gebiet_id='.$row->gebiet_id.'" onclick="return loadContent(this.href);" style="'.$style.'">'.$gebietbezeichnung.'</a>
</td>
<!--<td width="10" class="ItemTesttoolRight" nowrap>&nbsp;</td>-->
</tr>';
@@ -401,7 +408,7 @@ if (isset($_SESSION['pruefling_id']))
// Link zum Logout
echo '<tr><td class="ItemTesttool" style="margin-left: 20px;" nowrap>
<a class="ItemTesttool navButton" href="login.php?logout=true" target="content">Logout</a>
<a class="ItemTesttool navButton" href="login.php?logout=true" onclick="return loadContent(this.href);">Logout</a>
</td></tr>';
echo '</td></tr></table>';
@@ -439,5 +446,22 @@ else
'</td></tr>');
}
});
function loadContent(url)
{
if (parent && typeof parent.loadInContent === 'function')
{
parent.loadInContent(url);
return false;
}
let frame = parent?.frames?.["content"];
if (frame)
{
frame.location.href = url;
return false;
}
}
</script>
</html>
+18
View File
@@ -0,0 +1,18 @@
<?php
require_once './session_init.php';
require_once('../../config/cis.config.inc.php');
require_once('../../config/global.config.inc.php');
require_once '../../include/externe_ueberwachung.class.php';
if (!$db = new basis_db())
die('Fehler beim Oeffnen der Datenbankverbindung');
if ((defined('TESTTOOL_EXTERNE_UEBERWACHUNG_ALLOWED') && TESTTOOL_EXTERNE_UEBERWACHUNG_ALLOWED) && isset($_SESSION['externe_ueberwachung']) && $_SESSION['externe_ueberwachung'] === true)
{
$ueberwachung = new externeUeberwachung();
$url = $ueberwachung->start($_SESSION['prestudent_id'], $_SESSION['reihungstestID'], $_SESSION['sprache']);
$urlSafe = htmlspecialchars($url, ENT_QUOTES);
header("Location: $urlSafe");
$_SESSION['externe_ueberwachung_verified'] = true;
}
+11
View File
@@ -0,0 +1,11 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
session_set_cookie_params(
0,
'/; samesite=None',
'',
true,
true
);
session_start();
}
+11 -3
View File
@@ -67,9 +67,17 @@ function changeSprache(sprache)
content_params.set('sprache_user', sprache); // add or replace sprache_user
// Pass GET-param sprache_user to topbar.php, menu.php and content (login.php or frage.php) and refresh the frames.
location.href = location.pathname + '?sprache_user=' + sprache; // refreshes topbar.php
parent.menu.location.href = parent.menu.location.pathname + '?sprache_user=' + sprache; // refreshes menu.php
parent.content.location.href = parent.content.location.pathname + '?' + content_params; // refreshes login.php or frage.php
if (parent && typeof parent.changeSprache === 'function')
{
parent.changeSprache(content_params, sprache);
return false;
}
else
{
location.href = location.pathname + '?sprache_user=' + sprache; // refreshes topbar.php
parent.menu.location.href = parent.menu.location.pathname + '?sprache_user=' + sprache; // refreshes menu.php
parent.content.location.href = parent.content.location.pathname + '?' + content_params; // refreshes login.php or frage.php
}
}
$(document).on("keydown", function (e) {
+1
View File
@@ -456,6 +456,7 @@
"fortawesome/font-awesome4": "4.7.*",
"fortawesome/font-awesome6": "6.1.*",
"fzaninotto/faker": "1.*",
"firebase/php-jwt": "^6.0",
"highcharts/highcharts-dist": "^7.1.2",
Generated
+58 -1
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "1de37a74ba51a66057eb2712b21340c8",
"content-hash": "f4f0af4586f46f97d8b6092c1ac0fb3a",
"packages": [
{
"name": "afarkas/html5shiv",
@@ -882,6 +882,63 @@
},
"type": "library"
},
{
"name": "firebase/php-jwt",
"version": "v6.0.0",
"source": {
"type": "git",
"url": "https://github.com/firebase/php-jwt.git",
"reference": "0541cba75ab108ef901985e68055a92646c73534"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/0541cba75ab108ef901985e68055a92646c73534",
"reference": "0541cba75ab108ef901985e68055a92646c73534",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": ">=4.8 <=9"
},
"suggest": {
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
},
"type": "library",
"autoload": {
"psr-4": {
"Firebase\\JWT\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Neuman Vong",
"email": "neuman+pear@twilio.com",
"role": "Developer"
},
{
"name": "Anant Narayanan",
"email": "anant@php.net",
"role": "Developer"
}
],
"description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
"homepage": "https://github.com/firebase/php-jwt",
"keywords": [
"jwt",
"php"
],
"support": {
"issues": "https://github.com/firebase/php-jwt/issues",
"source": "https://github.com/firebase/php-jwt/tree/v6.0.0"
},
"time": "2022-01-24T15:18:34+00:00"
},
{
"name": "fortawesome/font-awesome4",
"version": "4.7.0",
+12
View File
@@ -301,4 +301,16 @@ define ('DEFAULT_ECHTER_DIENSTVERTRAG',[103,111]);
// Weiterleiten zu CIS neu (wenn Rechte vorhanden)
define('CIS_REDIRECT_TO_CIS4', false);
//Externe Ueberwachung
define('EXTERNE_UEBERWACHUNG_PROTOCOL_URL', 'https://example.com');
define('EXTERNE_UEBERWACHUNG_SECRET_KEY', null);
define('EXTERNE_UEBERWACHUNG_INTEGRATION_NAME', 'example');
define('EXTERNE_UEBERWACHUNG_SESSION_URL', 'https://example.com');
define('EXTERNE_UEBERWACHUNG_TRIAL_TEST', false);
define('EXTERNE_UEBERWACHUNG_EXAM_PARAMS', []);
define('EXTERNE_UEBERWACHUNG_EXAM_RULES', []);
define('EXTERNE_UEBERWACHUNG_EXAM_SCORE', []);
?>
+7
View File
@@ -28,11 +28,15 @@ define('CIS_LEHRVERANSTALTUNG_LEHRFACH_ANZEIGEN',false);
define('CIS_LEHRVERANSTALTUNG_GESAMTNOTE_ANZEIGEN', true);
define('CIS_LEHRVERANSTALTUNG_ANRECHNUNG_ANZEIGEN', true);
define('CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN', true);
define('CIS_LEHRVERANSTALTUNG_EVALUIERUNG_ANZEIGEN', true);
// Wenn gesetzt, werden die Digitale Anwesenheit-Icons nur fuer diese Studiengaenge angezeigt, sonst für alle
// define('CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN_STG', serialize(array('257')));
// define('CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN_LVA', serialize(array('39455','39481','39480','41906','41905','41904','39459','39512','39454','39482','42230','42231','39458','41921','41922','39457','42896')));
// Wenn gesetzt, werden die LV-Evaluierung-Icons nur für diese Studiengaenge angezeigt, sonst alle
define('CIS_EVALUIERUNG_ANZEIGEN_STG', serialize((array('335', '585', '914', '298')))); // BIW, MAI, BUB, MIO
// Im CIS Menue Links bei Modulen anzeigen wenn Lehrauftrag
define('CIS_LEHRVERANSTALTUNG_MODULE_LINK',true);
@@ -364,4 +368,7 @@ define('SANCHO_MAIL_FOOTER_IMG', 'sancho_footer_DEFAULT.jpg');
// Gibt an, ob in der StudVW der Status vorgerueckt werden kann
define('STATUS_VORRUECKEN_ANZEIGEN', true);
//externe Ueberwachung im Testtool erlauben
define('TESTTOOL_EXTERNE_UEBERWACHUNG_ALLOWED', false);
?>
+216
View File
@@ -0,0 +1,216 @@
<?php
require_once(dirname(__FILE__).'/basis_db.class.php');
require_once(dirname(__FILE__).'/prestudent.class.php');
require_once(dirname(__FILE__).'/person.class.php');
require_once(dirname(__FILE__).'/reihungstest.class.php');
require_once(dirname(__FILE__).'/../vendor/autoload.php');
use Firebase\JWT\JWT;
class externeUeberwachung extends basis_db
{
public function __construct()
{
parent::__construct();
}
public function getStatusByPrestudent($prestudent_id)
{
$session_id = $this->getSessionByPrestudent($prestudent_id);
return $this->getSessionStatus($session_id);
}
public function start($prestudent_id, $reihungstest_id, $sprache)
{
$session_id = $this->getSessionByPrestudent($prestudent_id);
if (!$session_id)
{
$session_id = $this->createSession($prestudent_id);
}
else
{
$status = $this->getSessionStatus($session_id);
if (in_array($status, array('late_to_start', 'finished')))
{
$session_id = $this->createSession($prestudent_id);
}
}
$payload = $this->getPayload($session_id, $prestudent_id, $reihungstest_id, $sprache);
return $this->getStartUrl($payload);
}
private function createSession($prestudent_id)
{
if (is_null($prestudent_id))
{
$this->errormsg = 'Falsche Parameterübergabe';
return false;
}
$uuid = $this->genereateUUID();
$qry = "INSERT INTO testtool.tbl_externe_ueberwachung (prestudent_id, session_id)
VALUES (".
$this->db_add_param($prestudent_id).",".
$this->db_add_param($uuid).")";
if($this->db_query($qry))
{
return $uuid;
}
else
{
$this->errormsg = 'Fehler beim Speichern der Antwort';
return false;
}
}
public function getSessionByPrestudent($prestudent_id)
{
if (is_null($prestudent_id))
{
$this->errormsg = 'Falsche Parameterübergabe';
return false;
}
$qry = "SELECT session_id
FROM testtool.tbl_externe_ueberwachung
WHERE prestudent_id = ".$this->db_add_param($prestudent_id, FHC_INTEGER) . "
ORDER BY insertamum DESC
LIMIT 1";
if($result = $this->db_query($qry))
{
if ($row = $this->db_fetch_object($result))
{
return $row->session_id;
}
else
{
$this->errormsg = 'Daten konnten nicht geladen werden';
return false;
}
}
else
{
$this->errormsg = 'Fehler bei einer Abfrage';
return false;
}
}
public function getSessionStatus($session_id)
{
$payload = $this->getSessionPayload($session_id);
$jwt = $this->createToken($payload);
$url = $this->getSessionUrl($session_id);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: JWT {$jwt}",
"Content-Type: application/json",
]);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($response, true);
return isset($data['status']) ? $data['status'] : false;
}
private function getSessionPayload($session_id)
{
return [
"session_id" => $session_id,
"iat" => time(),
"exp" => time() + 120,
];
}
private function getPayload($session_id, $prestudent_id, $reihungstest_id, $sprache)
{
$prestudent = new prestudent($prestudent_id);
$person = new Person($prestudent->person_id);
$reihungstest = new Reihungstest($reihungstest_id);
$datetime = new DateTime();
$today = $datetime->format('Y-m-d');
$payload = [
"userId" => $prestudent_id,
"lastName" => $person->nachname,
"firstName" => $person->vorname,
"language" => $sprache === 'German' ? 'de' : 'en',
"accountName" => "technikum_wien",
"accountId" => "technikum_wien",
"examId" => $reihungstest_id . '_' . $today,
"examName" => !is_null(trim($reihungstest->anmerkung)) ? ($reihungstest->anmerkung . '_' . $today) : ($reihungstest_id . '_' . $today),
"duration" => 1440,
"schedule" => false,
"trial" => EXTERNE_UEBERWACHUNG_TRIAL_TEST,
"proctoring" => "offline",
"startDate" => $reihungstest->datum . 'T00:00:00Z',
"endDate" => $reihungstest->datum . 'T23:59:59Z',
"sessionId" => $session_id,
"sessionUrl" => EXTERNE_UEBERWACHUNG_SESSION_URL
];
if (defined('EXTERNE_UEBERWACHUNG_EXAM_PARAMS'))
{
$payload = array_merge($payload, EXTERNE_UEBERWACHUNG_EXAM_PARAMS);
}
if (defined('EXTERNE_UEBERWACHUNG_EXAM_RULES'))
{
$payload['rules'] = EXTERNE_UEBERWACHUNG_EXAM_RULES;
}
if (defined('EXTERNE_UEBERWACHUNG_EXAM_SCORE'))
{
$payload['scoreConfig'] = EXTERNE_UEBERWACHUNG_EXAM_SCORE;
}
return $payload;
}
private function getSessionUrl($session_id)
{
return EXTERNE_UEBERWACHUNG_PROTOCOL_URL . "/api/v2/integration/simple/". EXTERNE_UEBERWACHUNG_INTEGRATION_NAME . "/sessions/". urlencode($session_id) ."/status/";
}
private function getStartUrl($payload)
{
$token = $this->createToken($payload);
$query = http_build_query(['token' => $token]);
return EXTERNE_UEBERWACHUNG_PROTOCOL_URL . '/integration/simple/'. EXTERNE_UEBERWACHUNG_INTEGRATION_NAME .'/start?' . $query;
}
private function createToken($payload)
{
return JWT::encode($payload, EXTERNE_UEBERWACHUNG_SECRET_KEY, 'HS256');
}
private function genereateUUID()
{
$data = openssl_random_pseudo_bytes(16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
}
?>
+25 -3
View File
@@ -70,6 +70,7 @@ class lehrveranstaltung extends basis_db
public $farbe;
public $lehrauftrag=true;
public $lehrveranstaltung_template_id; // integer
public $evaluierung=true; // boolean
public $studienplan_lehrveranstaltung_id;
@@ -170,6 +171,7 @@ class lehrveranstaltung extends basis_db
$this->benotung = $this->db_parse_bool($row->benotung);
$this->lvinfo = $this->db_parse_bool($row->lvinfo);
$this->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$this->evaluierung = $this->db_parse_bool($row->evaluierung);
// FIXME: LV-Bezeichnung richtig mehrsprachig machen
// Zwischenzeitlich 'Italian' zum bezeichnung_arr dazugegeben
@@ -244,6 +246,7 @@ class lehrveranstaltung extends basis_db
$lv_obj->benotung = $this->db_parse_bool($row->benotung);
$lv_obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$lv_obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$lv_obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$lv_obj->bezeichnung_arr['German'] = $row->bezeichnung;
$lv_obj->bezeichnung_arr['English'] = $row->bezeichnung_english;
@@ -394,6 +397,7 @@ class lehrveranstaltung extends basis_db
$lv_obj->benotung = $this->db_parse_bool($row->benotung);
$lv_obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$lv_obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$lv_obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$lv_obj->bezeichnung_arr['German'] = $row->bezeichnung;
$lv_obj->bezeichnung_arr['Italian'] = $row->bezeichnung;
@@ -524,6 +528,7 @@ class lehrveranstaltung extends basis_db
$lv_obj->benotung = $this->db_parse_bool($row->benotung);
$lv_obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$lv_obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$lv_obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$lv_obj->bezeichnung_arr['German'] = $row->bezeichnung;
$lv_obj->bezeichnung_arr['English'] = $row->bezeichnung_english;
@@ -607,6 +612,7 @@ class lehrveranstaltung extends basis_db
$lv_obj->benotung = $this->db_parse_bool($row->benotung);
$lv_obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$lv_obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$lv_obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$lv_obj->bezeichnung_arr['German'] = $row->bezeichnung;
$lv_obj->bezeichnung_arr['English'] = $row->bezeichnung_english;
@@ -779,7 +785,7 @@ class lehrveranstaltung extends basis_db
insertvon, planfaktor, planlektoren, planpersonalkosten, plankostenprolektor, updateamum, updatevon, sort,
zeugnis, projektarbeit, sprache, koordinator, bezeichnung_english, orgform_kurzbz, incoming, lehrtyp_kurzbz, oe_kurzbz,
raumtyp_kurzbz, anzahlsemester, semesterwochen, lvnr, semester_alternativ, farbe, lehrveranstaltung_template_id,sws,lvs,alvs,lvps,las,benotung,lvinfo,
lehrauftrag, lehrmodus_kurzbz) VALUES ('.
lehrauftrag, lehrmodus_kurzbz, evaluierung) VALUES ('.
$this->db_add_param($this->studiengang_kz). ', '.
$this->db_add_param($this->bezeichnung). ', '.
$this->db_add_param($this->kurzbz). ', '.
@@ -824,7 +830,8 @@ class lehrveranstaltung extends basis_db
$this->db_add_param($this->benotung, FHC_BOOLEAN).','.
$this->db_add_param($this->lvinfo, FHC_BOOLEAN).','.
$this->db_add_param($this->lehrauftrag, FHC_BOOLEAN).','.
$this->db_add_param($this->lehrmodus_kurzbz)
$this->db_add_param($this->lehrmodus_kurzbz).','.
$this->db_add_param($this->evaluierung, FHC_BOOLEAN)
.');';
}
else
@@ -880,7 +887,8 @@ class lehrveranstaltung extends basis_db
'las = '.$this->db_add_param($this->las).', '.
'benotung = '.$this->db_add_param($this->benotung, FHC_BOOLEAN).', '.
'lvinfo = '.$this->db_add_param($this->lvinfo, FHC_BOOLEAN).', '.
'lehrauftrag = '.$this->db_add_param($this->lehrauftrag, FHC_BOOLEAN).' '.
'lehrauftrag = '.$this->db_add_param($this->lehrauftrag, FHC_BOOLEAN).', '.
'evaluierung = '.$this->db_add_param($this->evaluierung, FHC_BOOLEAN).' '.
'WHERE lehrveranstaltung_id = ' . $this->db_add_param($this->lehrveranstaltung_id, FHC_INTEGER, false) . ';';
}
@@ -991,6 +999,7 @@ class lehrveranstaltung extends basis_db
$lv_obj->benotung = $this->db_parse_bool($row->benotung);
$lv_obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$lv_obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$lv_obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$lv_obj->bezeichnung_arr['German'] = $row->bezeichnung;
$lv_obj->bezeichnung_arr['English'] = $row->bezeichnung_english;
@@ -1086,6 +1095,7 @@ class lehrveranstaltung extends basis_db
$l->benotung = $this->db_parse_bool($row->benotung);
$l->lvinfo = $this->db_parse_bool($row->lvinfo);
$l->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$l->evaluierung = $this->db_parse_bool($row->evaluierung);
$l->bezeichnung_arr['German'] = $row->bezeichnung;
$l->bezeichnung_arr['English'] = $row->bezeichnung_english;
@@ -1170,6 +1180,7 @@ class lehrveranstaltung extends basis_db
$lv_obj->benotung = $this->db_parse_bool($row->benotung);
$lv_obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$lv_obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$lv_obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$lv_obj->bezeichnung_arr['German'] = $row->bezeichnung;
$lv_obj->bezeichnung_arr['English'] = $row->bezeichnung_english;
@@ -1271,6 +1282,7 @@ class lehrveranstaltung extends basis_db
$obj->benotung = $this->db_parse_bool($row->benotung);
$obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$obj->bezeichnung_arr['German'] = $row->bezeichnung;
$obj->bezeichnung_arr['English'] = $row->bezeichnung_english;
@@ -1396,6 +1408,7 @@ class lehrveranstaltung extends basis_db
$obj->lvinfo =$this->db_parse_bool( $lv->lvinfo);
$obj->zeugnis = $this->db_parse_bool($lv->zeugnis);
$obj->lehrauftrag = $this->db_parse_bool($lv->lehrauftrag);
$obj->evaluierung = $this->db_parse_bool($lv->evaluierung);
$values[] = $obj;
@@ -1422,6 +1435,7 @@ class lehrveranstaltung extends basis_db
$obj->lvinfo =$this->db_parse_bool( $this->lvinfo);
$obj->zeugnis = $this->db_parse_bool($this->zeugnis);
$obj->lehrauftrag = $this->db_parse_bool($this->lehrauftrag);
$obj->evaluierung = $this->db_parse_bool($this->evaluierung);
$values[] = $obj;
}
@@ -1476,6 +1490,7 @@ class lehrveranstaltung extends basis_db
$obj->export = $lv->export;
$obj->genehmigung = $lv->genehmigung;
$obj->lehrauftrag = $lv->lehrauftrag;
$obj->evaluierung = $lv->evaluierung;
$obj->lehre = $lv->lehre;
$obj->children = array();
if(count($lv->childs) > 0)
@@ -1507,6 +1522,7 @@ class lehrveranstaltung extends basis_db
$obj->zeugnis = $this->db_parse_bool($this->zeugnis);
$obj->curriculum = $this->db_parse_bool($this->curriculum);
$obj->lehrauftrag = $this->lehrauftrag;
$obj->evaluierung = $this->db_parse_bool($this->evaluierung);
$values[] = $obj;
}
@@ -1613,6 +1629,7 @@ class lehrveranstaltung extends basis_db
$lv_obj->benotung = $this->db_parse_bool($row->benotung);
$lv_obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$lv_obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$lv_obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$lv_obj->bezeichnung_arr['German'] = $row->bezeichnung;
$lv_obj->bezeichnung_arr['English'] = $row->bezeichnung_english;
@@ -1700,6 +1717,7 @@ class lehrveranstaltung extends basis_db
$lv_obj->benotung = $this->db_parse_bool($row->benotung);
$lv_obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$lv_obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$lv_obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$lv_obj->bezeichnung_arr['German'] = $row->bezeichnung;
$lv_obj->bezeichnung_arr['English'] = $row->bezeichnung_english;
@@ -1873,6 +1891,7 @@ class lehrveranstaltung extends basis_db
$lv_obj->benotung = $this->db_parse_bool($row->benotung);
$lv_obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$lv_obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$lv_obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$lv_obj->bezeichnung_arr['German'] = $row->bezeichnung;
$lv_obj->bezeichnung_arr['English'] = $row->bezeichnung_english;
@@ -2003,6 +2022,7 @@ class lehrveranstaltung extends basis_db
$lv_obj->benotung = $this->db_parse_bool($row->benotung);
$lv_obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$lv_obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$lv_obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$lv_obj->studiengang_kurzbzlang = $row->studiengang_kurzbzlang;
@@ -2131,6 +2151,7 @@ class lehrveranstaltung extends basis_db
$lv_obj->benotung = $this->db_parse_bool($row->benotung);
$lv_obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$lv_obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$lv_obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$lv_obj->bezeichnung_arr['German'] = $row->bezeichnung;
$lv_obj->bezeichnung_arr['English'] = $row->bezeichnung_english;
@@ -2402,6 +2423,7 @@ class lehrveranstaltung extends basis_db
$obj->benotung = $this->db_parse_bool($row->benotung);
$obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$obj->bezeichnung_arr['German'] = $row->bezeichnung;
$obj->bezeichnung_arr['English'] = $row->bezeichnung_english;
+7 -3
View File
@@ -63,6 +63,7 @@ class reihungstest extends basis_db
public $zugangs_ueberpruefung = false; //boolean
public $zugangscode; //smallint
public $externe_ueberwachung = false; //boolean
/**
@@ -119,6 +120,7 @@ class reihungstest extends basis_db
$this->aufnahmegruppe_kurzbz = $row->aufnahmegruppe_kurzbz;
$this->zugangs_ueberpruefung = $this->db_parse_bool($row->zugangs_ueberpruefung);
$this->zugangscode = $row->zugangscode;
$this->externe_ueberwachung = $this->db_parse_bool($row->externe_ueberwachung);
return true;
}
@@ -234,7 +236,7 @@ class reihungstest extends basis_db
$qry = 'BEGIN; INSERT INTO public.tbl_reihungstest (studiengang_kz, ort_kurzbz, anmerkung, datum, uhrzeit,
insertamum, insertvon, updateamum, updatevon, max_teilnehmer, oeffentlich, freigeschaltet,
studiensemester_kurzbz, stufe, anmeldefrist, aufnahmegruppe_kurzbz, zugangs_ueberpruefung, zugangscode) VALUES('.
studiensemester_kurzbz, stufe, anmeldefrist, aufnahmegruppe_kurzbz, zugangs_ueberpruefung, zugangscode, externe_ueberwachung) VALUES('.
$this->db_add_param($this->studiengang_kz, FHC_INTEGER).', '.
$this->db_add_param($this->ort_kurzbz).', '.
$this->db_add_param($this->anmerkung).', '.
@@ -250,7 +252,8 @@ class reihungstest extends basis_db
$this->db_add_param($this->anmeldefrist).','.
$this->db_add_param($this->aufnahmegruppe_kurzbz). ',' .
$this->db_add_param($this->zugangs_ueberpruefung, FHC_BOOLEAN).','.
$this->db_add_param($this->zugangscode) . ');';
$this->db_add_param($this->zugangscode) . ','.
$this->db_add_param($this->externe_ueberwachung, FHC_BOOLEAN) . ');';
}
else
{
@@ -270,7 +273,8 @@ class reihungstest extends basis_db
'anmeldefrist='.$this->db_add_param($this->anmeldefrist).', '.
'aufnahmegruppe_kurzbz='.$this->db_add_param($this->aufnahmegruppe_kurzbz).', '.
'zugangs_ueberpruefung='.$this->db_add_param($this->zugangs_ueberpruefung, FHC_BOOLEAN).', '.
'zugangscode='.$this->db_add_param($this->zugangscode).' '.
'zugangscode='.$this->db_add_param($this->zugangscode).', '.
'externe_ueberwachung='.$this->db_add_param($this->externe_ueberwachung, FHC_BOOLEAN).' '.
'WHERE reihungstest_id='.$this->db_add_param($this->reihungstest_id, FHC_INTEGER, false).';';
}
+21
View File
@@ -500,6 +500,27 @@ if((!defined('CIS_LEHRVERANSTALTUNG_ANRECHNUNG_ANZEIGEN') || CIS_LEHRVERANSTALTU
);
}
// LV-Evaluierung NEU
if(defined('CIS_LEHRVERANSTALTUNG_EVALUIERUNG_ANZEIGEN')
&& CIS_LEHRVERANSTALTUNG_EVALUIERUNG_ANZEIGEN
&& $angemeldet
&& (!defined('CIS_EVALUIERUNG_ANZEIGEN_STG') || in_array($lv->studiengang_kz, unserialize(CIS_EVALUIERUNG_ANZEIGEN_STG)))
&& ($rechte->isBerechtigt('extension/lvevaluierung_init')))
{
$text='(Pilotphase)';
$link= APP_ROOT. 'index.ci.php/extensions/FHC-Core-Evaluierung/Initiierung?lehrveranstaltung_id='. urlencode($lv->lehrveranstaltung_id).'&studiensemester_kurzbz='.urlencode($angezeigtes_stsem);
$menu[]=array
(
'id'=>'extension_lvevaluierung_menu_initiierung',
'position'=>'140',
'name'=>$p->t('lvevaluierung/lvevaluierung'). ' - '. strtoupper($p->t('global/neu')),
'icon'=>'../../../skin/images/button_lvevaluierung.png',
'link'=> $link,
'text'=>$text
);
}
//************* Menuepunkte anzeigen ****************
+2
View File
@@ -53,6 +53,8 @@ $this->phrasen['testtool/fuerFolgendeStgAngemeldet']='Für folgende Studiengäng
$this->phrasen['testtool/invalideGebiete']='Ein oder mehrere Fragengebiet/e inkorrekt!<br>Bitte melden Sie dies der Betreuungsperson.';
$this->phrasen['testtool/confirmationText']='<b>Ich bestätige, den Online-Reihungstest persönlich, selbständig, ohne Hilfe einer zusätzlichen Person und ohne Hilfsmittel zu absolvieren.<br>
I confirm that I will complete the online placement test personally, independently, without the help of an additional person and without any aids.</b>';
$this->phrasen['testtool/dsgvoConfirmText']='<b>Ich habe die <a href ="'. APP_ROOT. 'cms/dms.php?id=373121" target="_blank">Datenschutzerklärung</a> gelesen.</b>';
$this->phrasen['testtool/procotoringConfirmText']='<b>Ich stimme der digitalen Beaufsichtigung beim Online-Reihungstest (Proctoring) zu.</b>';
$this->phrasen['testtool/loginNoetig']='Bitte beachten Sie, dass der Reihungstest erst <b>unmittelbar</b> vor Ihrem <b>Reihungstesttermin</b> von uns aktiviert wird.<br>
Please note that the test will be activated by us <b>immediately</b> before your <b>placement test date</b>.';
$this->phrasen['testtool/start']='Reihungstest jetzt starten';
+37
View File
@@ -0,0 +1,37 @@
export default {
getAllStudienSemester(studiensemester=undefined, studiengang=undefined, semester=undefined, studienplan=undefined) {
return {
method: 'get',
url: '/api/frontend/v1/Studium/getAllStudienSemester',
params: {studiensemester, studiengang, semester, studienplan}
};
},
getStudiengaengeForStudienSemester(studiensemester) {
return {
method: 'get',
url: `/api/frontend/v1/Studium/getStudiengaengeForStudienSemester/${studiensemester}`,
};
},
getStudienplaeneBySemester(studiengang, studiensemester) {
return {
method: 'get',
url: `/api/frontend/v1/Studium/getStudienplaeneBySemester`,
params: {
studiengang,
studiensemester,
}
};
},
getLvPlanForStudiensemester(studiensemester_kurzbz, lvid) {
return {
method: 'get',
url: `/api/frontend/v1/LvPlan/getLvPlanForStudiensemester/${studiensemester}/${lvid}`,
};
},
getLvEvaluierungInfo(studiensemester_kurzbz, lvid) {
return {
method: 'get',
url: `/api/frontend/v1/Studium/getLvEvaluierungInfo/${studiensemester_kurzbz}/${lvid}`,
};
}
};
+4 -3
View File
@@ -1,9 +1,9 @@
import FhcSearchbar from "../components/searchbar/searchbar.js";
import CisMenu from "../components/Cis/Menu.js";
import PluginsPhrasen from '../plugins/Phrasen.js';
import ApiSearchbar from '../api/factory/searchbar.js';
import Theme from "../plugins/Theme.js";
import FhcBase from '../plugins/FhcBase/FhcBase.js';
// import PluginsPhrasen from '../plugins/Phrasen.js';
const app = Vue.createApp({
name: 'CisApp',
components: {
@@ -148,6 +148,7 @@ app.use(primevue.config.default, {
tooltip: 8000
}
})
app.use(PluginsPhrasen);
// app.use(PluginsPhrasen);
app.use(FhcBase);
app.use(Theme);
app.mount('#cis-header');
+2 -1
View File
@@ -1,5 +1,6 @@
import PluginsPhrasen from '../../plugins/Phrasen.js';
import {setScrollbarWidth} from "../../helpers/CssVarCalcHelpers.js";
import FhcBase from "../../plugins/FhcBase/FhcBase.js";
const app = Vue.createApp({
name: 'DocumentsApp',
@@ -89,5 +90,5 @@ const app = Vue.createApp({
setScrollbarWidth();
app.use(PluginsPhrasen);
app.use(FhcBase);
app.mount('#content');
+2 -3
View File
@@ -1,7 +1,6 @@
import ProfilUpdateView from "../../components/Cis/ProfilUpdate/ProfilUpdateView.js";
import PluginsPhrasen from "../../plugins/Phrasen.js";
import ApiProfilUpdate from '../../api/factory/profilUpdate.js';
import FhcBase from "../../plugins/FhcBase/FhcBase.js";
// TODO: sobald in verwendung den vue router pfad zu ProfilUpdateView definieren und diese app in component auslagern
const app = Vue.createApp({
@@ -34,4 +33,4 @@ const app = Vue.createApp({
});
},
});
app.use(PluginsPhrasen).mount("#content");
app.use(FhcBase).mount("#content");
+2 -2
View File
@@ -1,6 +1,6 @@
import {CoreNavigationCmpt} from '../../components/navigation/Navigation.js';
import DashboardAdmin from '../../components/Dashboard/Admin.js';
import PluginsPhrasen from '../../plugins/Phrasen.js';
import FhcBase from "../../plugins/FhcBase/FhcBase.js";
const app = Vue.createApp({
name: 'AdminApp',
@@ -12,5 +12,5 @@ const app = Vue.createApp({
DashboardAdmin
}
});
app.use(PluginsPhrasen);
app.use(FhcBase);
app.mount('#main');
+3 -2
View File
@@ -1,5 +1,6 @@
import FhcDashboard from '../../components/Dashboard/Dashboard.js';
import PluginsPhrasen from '../../plugins/Phrasen.js';
import FhcBase from '../../plugins/FhcBase/FhcBase.js';
import Theme from '../../plugins/Theme.js';
import contrast from '../../directives/contrast.js';
import {setScrollbarWidth} from "../../helpers/CssVarCalcHelpers.js";
@@ -330,7 +331,7 @@ app.use(primevue.config.default, {
}
})
app.directive('tooltip', primevue.tooltip);
app.use(PluginsPhrasen);
app.use(FhcBase);
app.use(Theme);
app.directive('contrast', contrast);
app.mount('#fhccontent');
+2 -2
View File
@@ -1,6 +1,6 @@
import {CoreNavigationCmpt} from '../components/navigation/Navigation.js';
import DashboardAdmin from '../components/Dashboard/Admin.js';
import Phrases from "../plugin/Phrasen.js"
import FhcBase from "../plugins/FhcBase/FhcBase.js";
Vue.createApp({
name: 'DashboardAdminApp',
@@ -13,4 +13,4 @@ Vue.createApp({
},
mounted() {
}
}).use(Phrases).mount('#main');
}).use(FhcBase).mount('#main');
+2 -2
View File
@@ -1,5 +1,5 @@
import LVVerwaltung from "../components/LVVerwaltung/LVVerwaltung.js";
import Phrasen from "../plugins/Phrasen.js";
import FhcBase from "../plugins/FhcBase/FhcBase.js";
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
@@ -98,5 +98,5 @@ app
overlay: 1100
}
})
.use(Phrasen)
.use(FhcBase)
.mount('#main');
+2 -2
View File
@@ -21,7 +21,7 @@ import {LogsViewerTabulatorEventHandlers} from './TabulatorSetup.js';
import {CoreFilterCmpt} from '../../components/filter/Filter.js';
import {CoreNavigationCmpt} from '../../components/navigation/Navigation.js';
import PluginsPhrasen from '../../plugins/Phrasen.js';
import FhcBase from "../../plugins/FhcBase/FhcBase.js";
const logsViewerApp = Vue.createApp({
data: function() {
@@ -42,5 +42,5 @@ const logsViewerApp = Vue.createApp({
}
});
logsViewerApp.use(PluginsPhrasen).mount('#main');
logsViewerApp.use(FhcBase).mount('#main');
+2 -2
View File
@@ -1,7 +1,7 @@
//TODO Manu
//use this instead of Nachrichten.js
import NewMessage from "../../components/Messages/Details/NewMessage/NewDiv.js";
import Phrasen from "../../plugin/Phrasen.js";
import FhcBase from "../../plugins/FhcBase/FhcBase.js";
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
@@ -23,5 +23,5 @@ app
overlay: 1100
}
})
.use(Phrasen)
.use(FhcBase)
.mount('#main');
+2 -3
View File
@@ -1,6 +1,5 @@
import NewMessage from "../components/Messages/Details/NewMessage/NewDiv.js";
import Phrasen from "../plugins/Phrasen.js";
import FhcBase from "../plugins/FhcBase/FhcBase.js";
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
@@ -23,5 +22,5 @@ app
overlay: 1100
}
})
.use(Phrasen)
.use(FhcBase)
.mount('#main');
+2 -5
View File
@@ -16,10 +16,7 @@
*/
import FhcStudentenverwaltung from "../components/Stv/Studentenverwaltung.js";
import fhcapifactory from "./api/fhcapifactory.js";
import PluginsPhrasen from "../plugins/Phrasen.js";
import FhcBase from "../plugins/FhcBase/FhcBase.js";
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
@@ -217,5 +214,5 @@ app
overlay: 1100
}
})
.use(PluginsPhrasen)
.use(FhcBase)
.mount('#main');
+2 -2
View File
@@ -1,7 +1,7 @@
import StudierendenantragAntrag from "../../components/Studierendenantrag/Antrag.js";
import StudierendenantragStatus from "../../components/Studierendenantrag/Status.js";
import StudierendenantragInfoblock from "../../components/Studierendenantrag/Infoblock.js";
import PluginsPhrasen from '../../plugins/Phrasen.js';
import FhcBase from "../../plugins/FhcBase/FhcBase.js";
const app = Vue.createApp({
name: 'AntragApp',
@@ -21,5 +21,5 @@ const app = Vue.createApp({
}
});
app
.use(PluginsPhrasen)
.use(FhcBase)
.mount('#wrapper');
+2 -2
View File
@@ -1,5 +1,5 @@
import StudierendenantragLeitung from '../../../components/Studierendenantrag/Leitung.js';
import PluginsPhrasen from '../../../plugins/Phrasen.js';
import FhcBase from "../../../plugins/FhcBase/FhcBase.js";
const app = Vue.createApp({
name: 'LeitungApp',
@@ -8,6 +8,6 @@ const app = Vue.createApp({
}
});
app
.use(PluginsPhrasen)
.use(FhcBase)
.use(primevue.config.default,{zIndex: {overlay: 9999}})
.mount('#wrapper');
+2 -2
View File
@@ -1,5 +1,5 @@
import LvZuweisung from '../../../components/Studierendenantrag/Lvzuweisung.js';
import PluginsPhrasen from '../../../plugins/Phrasen.js';
import FhcBase from "../../../plugins/FhcBase/FhcBase.js";
const app = Vue.createApp({
name: 'LvzuweisungApp',
@@ -13,5 +13,5 @@ const app = Vue.createApp({
}
});
app
.use(PluginsPhrasen)
.use(FhcBase)
.mount('#wrapper');
+2 -2
View File
@@ -1,5 +1,5 @@
import LvPopup from '../../../components/Studierendenantrag/Leitung/LvPopup.js';
import PluginsPhrasen from '../../../plugins/Phrasen.js';
import FhcBase from "../../../plugins/FhcBase/FhcBase.js";
const app = Vue.createApp({
name: 'StudentApp',
@@ -8,5 +8,5 @@ const app = Vue.createApp({
}
});
app
.use(PluginsPhrasen)
.use(FhcBase)
.mount('#wrapper');
@@ -17,7 +17,7 @@
import LvTemplateUebersicht from '../../../lehre/lvplanung/LvTemplateUebersicht.js';
import {CoreNavigationCmpt} from '../../../components/navigation/Navigation.js';
import PluginsPhrasen from "../../../plugins/Phrasen.js";
import FhcBase from "../../../plugins/FhcBase/FhcBase.js";
const lvTemplatesApp = Vue.createApp({
@@ -30,5 +30,5 @@ const lvTemplatesApp = Vue.createApp({
lvTemplatesApp
.use(primevue.config.default,{zIndex: {overlay: 9999}})
.use(PluginsPhrasen)
.use(FhcBase)
.mount('#main')
-6
View File
@@ -8,12 +8,6 @@ export const FhcChart = {
},
template: `
<div style="width:100%;height:100%;overflow:auto">
<div role="group" aria-label="Chart Modus">
<Button :class="(chartOptions.chart.type === 'pie' ? 'active ' : '') + 'btn btn-outline-secondary'" style="width: 48px;" @click="chartOptions.chart.type='pie'"><i class="fa-solid fa-chart-pie"></i></Button>
<Button :class="(chartOptions.chart.type === 'bar' ? 'active ' : '') + 'btn btn-outline-secondary'" style="width: 48px;" @click="chartOptions.chart.type='bar'"><i class="fa-solid fa-chart-bar"></i></Button>
<Button :class="(chartOptions.chart.type === 'column' ? 'active ' : '') + 'btn btn-outline-secondary'" style="width: 48px;" @click="chartOptions.chart.type='column'"><i class="fa-solid fa-chart-simple"></i></Button>
<Button :class="(chartOptions.chart.type === 'line' ? 'active ' : '') + 'btn btn-outline-secondary'" style="width: 48px;" @click="chartOptions.chart.type='line'"><i class="fa-solid fa-chart-line"></i></Button>
</div>
<figure>
<highcharts class="chart" :options="chartOptions"></highcharts>
</figure>
-1
View File
@@ -3,7 +3,6 @@ import FhcSearchbar from "../searchbar/searchbar.js";
import CisSprachen from "./Sprachen.js"
import ThemeSwitch from "./ThemeSwitch.js";
import ApiCisMenu from '../../api/factory/cis/menu.js';
export default {
components: {
CisMenuEntry,
@@ -2,9 +2,10 @@ import BsModal from "../../Bootstrap/Modal.js";
import LvMenu from "./LvMenu.js";
import ApiAddons from '../../../api/factory/addons.js';
import ApiStudium from '../../../api/factory/studium.js';
export default {
name: "LvUebersicht",
props:{
event:{
type:Object,
@@ -63,7 +64,7 @@ export default {
// check lv evaluierung info
if (this.studium_studiensemester) {
this.$fhcApi.factory.studium.getLvEvaluierungInfo(this.studium_studiensemester, this.event.lehreinheit_id ?? this.event.lehrveranstaltung_id)
this.$api.call(ApiStudium.getLvEvaluierungInfo(this.studium_studiensemester, this.event.lehreinheit_id ?? this.event.lehrveranstaltung_id))
.then(data => data.data)
.then(res => {
this.lvEvaluierungMessage = res.message;
@@ -72,7 +73,7 @@ export default {
// check if the lv has lvplan entries for this studiensemester
if (this.studiensemester && this.event) {
return this.$fhcApi.factory.studium.getLvPlanForStudiensemester(this.studiensemester, this.event.lehreinheit_id ?? this.event.lehrveranstaltung_id)
this.$api.call(ApiStudium.getLvPlanForStudiensemester(this.studiensemester, this.event.lehreinheit_id ?? this.event.lehrveranstaltung_id))
.then(data => data.data)
.then(res => {
if (Array.isArray(res) && res.length > 0) {
@@ -31,7 +31,7 @@ export default {
this.event.lektor.slice(0, 3).map(lektor => lektor.kurzbz).join("\n")
+ "\n" + this.$p.t('lehre/weitereLektoren', [this.event.lektor.length - 3])
].join(": "));
} else {console.log(this.event.lektor);
} else {
tooltipArray.push([
this.$p.t('lehre/lektor'),
this.event.lektor.map(lektor => lektor.kurzbz).join("\n")
+8 -7
View File
@@ -1,7 +1,8 @@
import LvUebersicht from "../Mylv/LvUebersicht.js";
import ApiStudium from '../../../api/factory/studium.js';
export default {
name: "Studium",
data(){
return {
studienSemester :[],
@@ -97,28 +98,28 @@ export default {
return value;
},
changeSelectedStudienSemester(studiensemester_kurzbz) {
this.$fhcApi.factory.studium.getAllStudienSemester(studiensemester_kurzbz, this.selectedStudiengang, this.selectedSemester, this.selectedStudienordnung)
this.$api.call(ApiStudium.getAllStudienSemester(studiensemester_kurzbz, this.selectedStudiengang, this.selectedSemester, this.selectedStudienordnung))
.then(data => data.data)
.then(res => {
this.extractPropertyValues(res);
})
},
changeSelectedStudienGang(studiengang_kz) {
this.$fhcApi.factory.studium.getAllStudienSemester(this.selectedStudiensemester, studiengang_kz, this.selectedSemester, this.selectedStudienordnung)
this.$api.call(ApiStudium.getAllStudienSemester(this.selectedStudiensemester, studiengang_kz, this.selectedSemester, this.selectedStudienordnung))
.then(data => data.data)
.then(res => {
this.extractPropertyValues(res);
})
},
changeSelectedSemester(semester) {
this.$fhcApi.factory.studium.getAllStudienSemester(this.selectedStudiensemester, this.selectedStudiengang, semester, this.selectedStudienordnung)
this.$api.call(ApiStudium.getAllStudienSemester(this.selectedStudiensemester, this.selectedStudiengang, semester, this.selectedStudienordnung))
.then(data => data.data)
.then(res => {
this.extractPropertyValues(res);
})
},
changeSelectedStudienPlan(studienplan_id) {
this.$fhcApi.factory.studium.getAllStudienSemester(this.selectedStudiensemester, this.selectedStudiengang, this.selectedSemester, studienplan_id)
this.$api.call(ApiStudium.getAllStudienSemester(this.selectedStudiensemester, this.selectedStudiengang, this.selectedSemester, studienplan_id))
.then(data => data.data)
.then(res => {
this.extractPropertyValues(res);
@@ -256,8 +257,8 @@ export default {
const studienordnung = JSON.parse(this.getDataFromLocalStorage("studienordnung")) ?? undefined;
// only fetch default data if no data is stored in the local storage
this.$fhcApi.factory.studium.getAllStudienSemester(studiensemester, studiengang, semester, studienordnung)
this.$api.call(ApiStudium.getAllStudienSemester(studiensemester, studiengang, semester, studienordnung))
.then(data => data.data)
.then(res => {
this.extractPropertyValues(res);
+1
View File
@@ -280,6 +280,7 @@ export default {
<template #chip="data"><slot name="chip" v-bind="data"></slot></template>
<template #header="data"><slot name="header" v-bind="data"></slot></template>
<template #footer="data"><slot name="footer" v-bind="data"></slot></template>
<template #selectedItem="data"><slot name="selectedItem" v-bind="data"></slot></template>
<template #option="data"><slot name="option" v-bind="data"></slot></template>
<template #optiongroup="data"><slot name="optiongroup" v-bind="data"></slot></template>
<template #content="data"><slot name="content" v-bind="data"></slot></template>
+14 -2
View File
@@ -42,7 +42,11 @@ export default {
showErweitert: Boolean,
showDocument: Boolean,
showTinyMce: Boolean,
visibleColumns: Array
visibleColumns: Array,
tabulatorPersistenceId: {
type: String,
default: 'core-notiz'
}
},
data() {
return {
@@ -179,7 +183,15 @@ export default {
//responsiveLayout: "collapse",
maxHeight: '200px',
index: 'notiz_id',
persistenceID: 'core-notiz'
persistenceID: this.tabulatorPersistenceId,
persistence: {
sort: false,
columns: ["width", "visible", "frozen"],
filter: false,
headerFilter: false,
group: false,
page: false,
}
},
tabulatorEvents: [
{
@@ -757,6 +757,7 @@ export default {
:label="$p.t('global', 'datum')"
type="DatePicker"
v-model="formData.datum"
model-type="yyyy-MM-dd"
auto-apply
:enable-time-picker="false"
text-input
@@ -781,6 +782,7 @@ export default {
:label="$p.t('abschlusspruefung', 'sponsion')"
type="DatePicker"
v-model="formData.sponsion"
model-type="yyyy-MM-dd"
auto-apply
:enable-time-picker="false"
text-input
@@ -31,6 +31,7 @@ export default {
show-tiny-mce
:visibleColumns="['titel','text','verfasser','bearbeiter','dokumente']"
@reload="$emit('update:suffix')"
tabulator-persistence-id="stv-notiz-2026011301"
>
</core-notiz>
+359
View File
@@ -0,0 +1,359 @@
import PvConfig from "../../../../index.ci.php/public/js/components/primevue/config/config.esm.min.js";
import PvToast from "../../../../index.ci.php/public/js/components/primevue/toast/toast.esm.min.js";
import PvConfirm from "../../../../index.ci.php/public/js/components/primevue/confirmdialog/confirmdialog.esm.min.js";
import PvConfirmationService from "../../../../index.ci.php/public/js/components/primevue/confirmationservice/confirmationservice.esm.min.js";
import {CoreRESTClient} from "../../RESTClient.js";
export default {
init(app, $p) {
let resolveReady;
const readyPromise = new Promise(resolve => { resolveReady = resolve; });
const helperAppContainer = document.createElement('div');
document.body.appendChild(helperAppContainer);
const helperApp = Vue.createApp({
name: "FhcAlertApp",
components: { PvToast, PvConfirm },
methods: {
mailToUrl(slotProps) {
let mailTo = FHC_JS_DATA_STORAGE_OBJECT.systemerror_mailto;
let subject = 'Meldung%20Systemfehler';
let body = `
Danke, dass Sie uns den Fehler melden. %0D%0A %0D%0A
Bitte beschreiben Sie uns ausführlich das Problem.%0D%0A
Bsp: Ich habe X ausgewählt und Y angelegt. Beim Speichern erhielt ich die Fehlermeldung. [Optional: Ich habe den Browser Z verwendet.]%0D%0A
-----------------------------------------------------------------------------------------------------------------------------------%0D%0A
PROBLEM: ... %0D%0A %0D%0A %0D%0A
-----------------------------------------------------------------------------------------------------------------------------------%0D%0A
Fehler URL: ` + FHC_JS_DATA_STORAGE_OBJECT.called_path + '/' + FHC_JS_DATA_STORAGE_OBJECT.called_method + `%0D%0A
Fehler Meldung: ` + slotProps.message.detail + `%0D%0A
-----------------------------------------------------------------------------------------------------------------------------------%0D%0A %0D%0A
Wir kümmern uns um eine rasche Behebung des Problems!`
return "mailto:" + mailTo + "?subject=" + subject + "&body=" + body;
},
openMessagecard(e) {
bootstrap.Collapse.getOrCreateInstance(e.target.getAttribute('href')).toggle();
}
},
computed: {
showmaillink() { return FHC_JS_DATA_STORAGE_OBJECT.systemerror_mailto !== ''; }
},
template: /* html */`
<pv-toast ref="toast" class="fhc-alert" :base-z-index="99999">
<template #message="{ message }">
<!--span :class="slotProps.iconClass"></span-->
<div class="p-toast-message-text">
<span class="p-toast-summary">{{ message.summary }}</span>
<div v-if="message.detail && message.html" class="p-toast-detail" v-html="message.detail"></div>
<div v-else-if="message.detail" class="p-toast-detail">{{ message.detail }}</div>
</div>
</template>
</pv-toast>
<pv-toast ref="alert" class="fhc-alert" :base-z-index="99999" position="center">
<template #message="slotProps">
<i class="fa fa-circle-exclamation fa-2xl mt-3"></i>
<div class="p-toast-message-text">
<span class="p-toast-summary">{{slotProps.message.summary}}</span>
<div class="p-toast-detail my-3">Sorry! Ein interner technischer Fehler ist aufgetreten.</div>
<div class="d-flex justify-content-between align-items-center">
<a
class="align-bottom flex-fill me-2"
data-bs-toggle="collapse"
:href="'#fhcAlertCollapseMessageCard' + slotProps.message.id"
role="button"
aria-expanded="false"
:aria-controls="'fhcAlertCollapseMessageCard' + slotProps.message.id"
@click="openMessagecard"
>
Fehler anzeigen
</a>
<a
v-if="showmaillink"
class="btn btn-primary flex-fill"
target="_blank"
:href="mailToUrl(slotProps)"
>
Fehler melden
</a>
</div>
<div ref="messageCard" :id="'fhcAlertCollapseMessageCard' + slotProps.message.id" class="collapse mt-3">
<div class="card card-body text-body small alertCollapseText">
{{slotProps.message.detail}}
</div>
</div>
</div>
</template>
</pv-toast>
<pv-confirm group="fhcAlertConfirm"></pv-confirm>`
});
// Link the helper app to the main app's phrasen system
helperApp.config.globalProperties.$p = $p;
helperApp.use(PvConfig);
helperApp.use(PvConfirmationService);
const helperAppInstance = helperApp.mount(helperAppContainer);
const $fhcAlert = {
// Internal storage for cross-plugin dependencies
deps: { $p },
ready: readyPromise,
// Method used by FhcBase to inject the API later
setDeps(deps) {
Object.assign(this.deps, deps);
// Alert is ready when both Phrases and API are present
if (this.deps.$p && this.deps.$api) resolveReady();
},
alertSuccess(message) {
if (Array.isArray(message))
return message.forEach(this.alertSuccess);
helperAppInstance.$refs.toast.add({ severity: 'success', summary: 'Info', detail: message, life: 1000});
},
alertInfo(message) {
if (Array.isArray(message))
return message.forEach(this.alertInfo);
helperAppInstance.$refs.toast.add({ severity: 'info', summary: 'Info', detail: message, life: 3000 });
},
alertWarning(message) {
if (Array.isArray(message))
return message.forEach(this.alertWarning);
helperAppInstance.$refs.toast.add({ severity: 'warn', summary: 'Achtung', detail: message});
},
alertError(message) {
if (Array.isArray(message))
return message.forEach(this.alertError);
helperAppInstance.$refs.toast.add({ severity: 'error', summary: 'Achtung', detail: message });
},
async alertSystemError(message) {
//TODO(Manu) for translation of content of template: restructure in data
//and update definitions with translations
await this.ready;
if (Array.isArray(message))
return message.forEach(this.alertSystemError);
helperAppInstance.$refs.alert.add({
severity: 'error',
summary: Vue.computed(() => this.deps.$p.t('alert/systemerror')),
detail: message});
},
async confirmDelete() {
await this.ready;
return new Promise((resolve, reject) => {
helperAppInstance.$confirm.require({
group: 'fhcAlertConfirm',
header: Vue.computed(() => this.deps.$p.t('alert/attention')),
message: Vue.computed(() => this.deps.$p.t('alert/confirm_delete')),
acceptLabel: Vue.computed(() => this.deps.$p.t('ui/loeschen')),
acceptClass: 'p-button-danger',
rejectLabel: Vue.computed(() => this.deps.$p.t('ui/abbrechen')),
rejectClass: 'p-button-secondary',
accept() {
resolve(true);
},
reject() {
resolve(false);
},
});
});
},
confirm(options) {
return new Promise((resolve, reject) => {
helperAppInstance.$confirm.require({
group: options?.group ?? 'fhcAlertConfirm',
header: options?.header ?? Vue.computed(() => this.deps.$p.t('alert/attention')),
message: options?.message ?? '',
acceptLabel: options?.acceptLabel ?? 'Ok',
acceptClass: options?.acceptClass ?? 'btn btn-primary',
rejectLabel: options?.rejectLabel ?? Vue.computed(() => this.deps.$p.t('ui/abbrechen')),
rejectClass: options?.rejectClass ?? 'btn btn-outline-secondary',
accept() {
resolve(true);
},
reject() {
resolve(false);
},
});
});
},
alertDefault(severity, title, message, sticky = false, html = false) {
let options = { severity: severity, summary: title, detail: message, html };
if (!sticky)
options.life = 3000;
helperAppInstance.$refs.toast.add(options);
},
alertMultiple(messageArray, severity = 'info', title = 'Info', sticky = false, html = false){
// Check, if array has only string values
if (messageArray.every(message => typeof message === 'string')) {
messageArray.forEach(message => this.alertDefault(severity, title, message, sticky, html));
return true;
}
return false;
},
handleSystemError(error) {
// don't show an error message to the user if the error was an aborted request
if(error.hasOwnProperty('name') && error.name.toLowerCase() === "AbortError".toLowerCase())
return;
// Error is string
if (typeof error === 'string')
return this.alertSystemError(error);
// Error is array of strings
if (Array.isArray(error) && error.every(err => typeof err === 'string'))
return error.every(this.alertSystemError);
// Error has been handled already
if (error.hasOwnProperty('handled') && error.handled)
return;
// Error is object
if (typeof error === 'object' && error !== null) {
let errMsg = '';
if (error.hasOwnProperty('response') && error.response?.data?.retval)
errMsg += 'Error Message: ' + (error.response.data.retval.message || error.response.data.retval) + '\r\n';
else if (error.hasOwnProperty('message'))
errMsg += 'Error Message: ' + error.message.toUpperCase() + '\r\n';
if (error.hasOwnProperty('config') && error.config.hasOwnProperty('url'))
errMsg += 'Error ConfigURL: ' + error.config.url + '\r\n';
if (error.hasOwnProperty('stack'))
errMsg += 'Error Stack: ' + error.stack + '\r\n';
// Fallback object error message
if (errMsg == '')
errMsg = 'Error Message: ' + JSON.stringify(error) + '\r\n';
errMsg += 'Error Controller Path: ' + FHC_JS_DATA_STORAGE_OBJECT.called_path + '/' + FHC_JS_DATA_STORAGE_OBJECT.called_method;
return this.alertSystemError(errMsg);
}
// Fallback
this.alertSystemError('alertSystemError throws Generic Error\r\nError Controller Path: ' + FHC_JS_DATA_STORAGE_OBJECT.called_path + '/' + FHC_JS_DATA_STORAGE_OBJECT.called_method);
},
handleSystemMessage(message) {
// Message is string
if (typeof message === 'string')
return this.alertWarning(message);
// Message is array of strings
if (Array.isArray(message)) {
// If Array has only Strings
if (message.every(msg => typeof msg === 'string'))
return message.every(this.alertWarning);
// If Array has only Objects
if (message.every(msg => typeof msg === 'object') && msg !== null) {
return message.every(msg => {
if (msg.hasOwnProperty('data') && msg.data.hasOwnProperty('retval')) {
this.alertWarning(JSON.stringify(msg.data.retval));
} else {
this.alertSystemError(JSON.stringify(msg));
}
});
}
}
// Message is Object with data property
if (typeof message === 'object' && message !== null){
if (message.hasOwnProperty('data') && message.data.hasOwnProperty('retval')) {
// NOTE(chris): changed: alertSystemError => alertWarning
this.alertWarning(JSON.stringify(message.data.retval));
} else {
this.alertSystemError(JSON.stringify(message));
}
return;
}
// Fallback
this.alertSystemError('alertSystemError throws Generic Error\r\nError Controller Path: ' + FHC_JS_DATA_STORAGE_OBJECT.called_path + '/' + FHC_JS_DATA_STORAGE_OBJECT.called_method);
},
resetFormValidation(form) {
const event = new Event('fhc-form-reset');
form.querySelectorAll(['[data-fhc-form-validate],[data-fhc-form-error]']).forEach(el => el.dispatchEvent(event));
/*const alert = form.querySelector('div.alert.alert-danger[role="alert"]');
if (alert) {
alert.innerHTML = '';
alert.classList.add('d-none');
}
form.querySelectorAll('.invalid-feedback').forEach(n => n.remove());
form.querySelectorAll('.is-invalid').forEach(n => n.classList.remove('is-invalid'));
form.querySelectorAll('.is-valid').forEach(n => n.classList.remove('is-valid'));*/
},
handleFormValidation(error, form) {
if (form === undefined) {
if (error && error.nodeType === Node.ELEMENT_NODE)
return err => this.handleFormValidation(err, error);
} else {
if (error?.response?.status == 400) {
let errors = CoreRESTClient.getError(error.response.data);
if (typeof errors !== "object")
errors = error.response.data;
// NOTE(chris): reset form validation
this.resetFormValidation(form);
// NOTE(chris): set form input validation
const notFound = Object.entries(errors).filter(([key, detail]) => {
const input = form.querySelector('[data-fhc-form-validate="' + key + '"]');
if (!input)
return true;
input.dispatchEvent(new CustomEvent('fhc-form-invalidate', {detail}));
/*const input = form.querySelector('[name="' + key + '"]');
if (!input)
return true;
input.classList.add('is-invalid');
const feedback = document.createElement('div');
feedback.classList.add('invalid-feedback');
feedback.innerHTML = detail;
input.after(feedback);*/
return false;
}).map(arr => arr[1]);
//const alert = form.querySelector('div.alert.alert-danger[role="alert"]');
const alert = form.querySelector('[data-fhc-form-error]');
if (alert && notFound.length) {
alert.dispatchEvent(new CustomEvent('fhc-form-error', {detail: notFound}));
/*notFound.forEach(txt => {
const p = document.createElement('p');
p.innerHTML = txt;
alert.append(p);
});
if (notFound.length) {
alert.lastChild.classList.add('mb-0');
alert.classList.remove('d-none');
}*/
} else {
notFound.forEach(this.alertError);
}
return;
}
}
if (error?.response?.status == 400) {
let errors = CoreRESTClient.getError(error.response.data);
this.alertError((typeof errors === 'object') ? Object.values(errors) : errors);
} else {
this.handleSystemError(error);
}
}
};
return $fhcAlert;
}
};
+591
View File
@@ -0,0 +1,591 @@
/**
* BaseApi.js - Contains the original api plugin methods call, get, post, etc.
* Dependencies like phrasen($p) or alert($fhcAlert) are optional and can be injected at initialization
* or at a later stage.
*/
export class BaseApi {
constructor(deps = {}) {
// optional dependencies like { $fhcAlert: ..., $p: ... }
this.deps = deps;
this.resolveReady = null;
this.ready = new Promise(resolve => { this.resolveReady = resolve; });
// If deps were passed in constructor, resolve immediately
if (this.deps.$fhcAlert && this.deps.$p) this.resolveReady();
this.setupDefaultConfig()
this.axiosInstance = axios.create({
timeout: 500000,
baseURL: FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + "/"
});
this.setupInterceptors();
}
// === public functions START ===
// in case of api instantiation before vue app mount and later usage via fhcBase plugin
setDependencies(deps) {
Object.assign(this.deps, deps);
if (this.deps.$fhcAlert && this.deps.$p) this.resolveReady();
}
getUri(url) {
return this.axiosInstance.getUri({ url });
}
get(form, uri, params, config) {
[uri, params, config] = this.get_config(form, uri, params, config);
if (params) {
config = config ? { ...config, params } : { params };
}
return this.axiosInstance.get(uri, config);
}
post(form, uri, data, config) {
[uri, data, config] = this.get_config(form, uri, data, config);
return this.axiosInstance.post(uri, data, config);
}
getErrorHandler(config) {
return this.get_error_handler(config);
}
async call(factory, configoverwrite, form) {
if (Array.isArray(factory)) {
return Promise.allSettled(factory.map((config, index) => {
if (!Array.isArray(config)) config = ['#' + index, config];
return this.call(config[1], { errorHeader: config[0], errorHandling: false });
})).then(result => {
const [ , , config ] = this.get_config(form, undefined, undefined, configoverwrite || {});
const errorConfig = this.get_error_handler(config);
if (!errorConfig.success && !errorConfig.fail) {
return result;
}
const typedErrors = {};
for (let res of result) {
const [ allowed, item ] = res.status === 'fulfilled'
? [ errorConfig.success, res.value ]
: [ errorConfig.fail, res.reason ];
if (!allowed)
return;
const errors = this.popHandleableErrors(errorConfig, this.get_error_list(item));
for (let type in errors) {
if (!typedErrors[type])
typedErrors[type] = {
[item.config.errorHeader]: errors[type]
};
else
typedErrors[type][item.config.errorHeader] = errors[type];
}
};
for (let errType in typedErrors) {
errorConfig.handler[errType](typedErrors[errType]);
}
return result;
});
}
let { method = 'get', url, params, config } = factory;
if (configoverwrite !== undefined) config = configoverwrite;
method = method.toLowerCase();
return method === 'post' ? this.post(form, url, params, config) : this.get(form, url, params, config);
}
// === public functions END ===
// === private functions START ===
setupInterceptors() {
this.axiosInstance.interceptors.request.use(config => {
if (config.method != 'post' || !config.data)
return config;
if (config.data instanceof FormData)
return config;
if (!Object.values(config.data).every(item => {
if (item instanceof FileList)
return false;
if (Array.isArray(item))
return item.every(i => !(i instanceof File));
return true;
})) {
const newData = Object.entries(config.data).reduce((nd, [key, item]) => {
if (item instanceof FileList) {
for (const file of item)
nd.FormData.append(key + (item.length > 1 ? '[]' : ''), file);
} else if (Array.isArray(item)) {
if (item.every(i => !(i instanceof File))) {
nd.jsondata[key] = item;
} else {
item.forEach(file => nd.FormData.append(key + (item.length > 1 ? '[]' : ''), file));
}
} else {
nd.jsondata[key] = item;
}
return nd;
}, {
FormData: new FormData(),
jsondata: {}
});
newData.FormData.append('_jsondata', JSON.stringify(newData.jsondata));
config.data = newData.FormData;
}
return config;
});
this.axiosInstance.interceptors.response.use(
response => {
if (response.config?.errorHandling == 'off'
|| response.config?.errorHandling === false
|| response.config?.errorHandling == 'fail')
return this.clean_return_value(response);
// NOTE(chris): loop through errors
if (response.data.errors)
response.data.errors = response.data.errors.filter(
err => (response.config[err.type + 'ErrorHandler'] || this.DEFAULT_ERROR_CONFIG.handler[err.type])(err, response.config)
);
return this.clean_return_value(response);
},
error => {
if (error.code == 'ERR_CANCELED')
return Promise.reject({ handled: true, ...error });
const errorConfig = this.get_error_handler(error.config);
if (!errorConfig.fail)
return Promise.reject(error);
const remaining = this.get_error_list(error);
const errors = this.popHandleableErrors(errorConfig, remaining);
for (let type in errors) {
errorConfig.handler[type](errors[type]);
}
if (remaining.length)
return Promise.reject(error);
return Promise.reject({ handled: true, ...error });
}
);
}
setupDefaultConfig() {
this.DEFAULT_ERROR_CONFIG = {
success: true,
fail: true,
combine: {
form: ['validation', 'general'],
toast: ['validation', 'general', 'not_found', 'site_failed']
},
handler: {
form: (form, errors) => {
form.clearValidation();
errors.forEach(err => form.setFeedback(
false,
err.messages || err.message
));
},
toast: async (errors) => {
await this.ready;
async function _format_toast(errors) {
errors = errors.reduce((result, err) => {
switch (err.type) {
case 'not_found':
case 'site_failed':
if (err.message)
result[err.message] = [err.url];
else
result._default = [err.url];
break;
case 'general':
if (!result._default)
result._default = [];
result._default.push(err.message);
break;
case 'validation':
Object.entries(err.messages)
.forEach(([field, msg]) => {
if (!result[field])
result[field] = [];
if (Array.isArray(msg))
result[field].push(...msg);
else
result[field].push(msg);
});
break;
}
return result;
}, {});
let counter = 0;
const msgs = await Promise.all(Object.entries(errors)
.sort((a, b) => ['_default'].indexOf(b[0]) - ['_default'].indexOf(a[0])) // sort _default first
.map(async ([field, msgs]) => {
if (field == '_default') {
await this.deps.$p.loadCategory('dashboard');
const general = this.deps.$p.t('dashboard/general');
field = '<dt class="d-none">' + general + '</dt>';
} else {
field = '<dt>' + field + '</dt>';
}
counter += msgs.length;
return field
+ '<dd>'
+ msgs.join('</dd><dd>')
+ '</dd>';
}));
return {
counter,
msgs
}
}
let counter, msgs;
if (Array.isArray(errors)) {
({ counter, msgs } = await _format_toast(errors));
} else {
({ counter, msgs } = await Object.entries(errors)
.reduce(async (res, [title, errs]) => {
const result = await res;
const { counter, msgs } = await _format_toast(errs);
result.counter += counter;
result.msgs.push('<dt>'
+ title
+ '</dt><dd><dl>'
+ msgs.join('')
+ '</dl></dd>');
return result;
}, Promise.resolve({ counter: 0, msgs: []})));
}
await this.deps.$p.loadCategory('ui');
const n_errors = this.deps.$p.t('ui/n_errors', { n: counter });
this.deps.$fhcAlert.alertDefault(
'error',
n_errors,
'<dl>' + msgs.join('') + '</dl>',
true,
true
);
},
php: async (errors) => {
await this.ready;
this._send_array_or_object(errors, (error, title) => {
var message = '';
message += 'Message: ' + error.message + '\n\n';
message += 'Filename: ' + error.filename + '\n';
message += 'Line Number: ' + error.line + '\n';
if (error.backtrace && error.backtrace.length) {
message += '\nBacktrace: ';
error.backtrace.forEach(err => {
message += '\n\tFile: ' + err.file + '\n';
message += '\tLine: ' + err.line + '\n';
message += '\tFunction: ' + err.function + '\n';
});
}
switch (error.severity) {
case 'Warning':
case 'Core Warning':
case 'Compile Warning':
case 'User Warning':
if (title)
title += ': PHP ' + error.severity;
else
title = 'PHP ' + error.severity;
this.deps.$fhcAlert.alertDefault('warn', title, message, true);
break;
case 'Notice':
case 'User Notice':
case 'Runtime Notice':
if (title)
title += ': PHP ' + error.severity;
else
title = 'PHP ' + error.severity;
this.deps.$fhcAlert.alertDefault('info', title, message, true);
break;
default:
message = 'Type: PHP ' + error.severity + '\n\n' + message;
if (title)
message = title + '\n\n' + message;
this.deps.$fhcAlert.alertSystemError(message);
break;
}
});
},
exception: async (errors) => {
await this.ready;
this._send_array_or_object(errors, (error, title) => {
var message = '';
if (title)
message += title + '\n\n';
message += 'Type: ' + error.class + '\n\n';
message += 'Message: ' + error.message + '\n\n';
message += 'Filename: ' + error.filename + '\n';
message += 'Line Number: ' + error.line + '\n';
if (error.backtrace && error.backtrace.length) {
message += '\nBacktrace: ';
error.backtrace.forEach(err => {
message += '\n\tFile: ' + err.file + '\n';
message += '\tLine: ' + err.line + '\n';
message += '\tFunction: ' + err.function + '\n';
});
}
this.deps.$fhcAlert.alertSystemError(message);
});
},
db: async (errors) => {
await this.ready;
this._send_array_or_object(errors, (error, title) => {
var message = '';
if (title)
message += title + '\n\n';
if (error.heading !== undefined)
message += error.heading + '\n\n';
if (error.code !== undefined)
message += 'Code: ' + error.code + '\n\n';
if (error.sql !== undefined)
message += 'SQL: ' + error.sql + '\n\n';
if (error.message !== undefined)
message += 'Message: ' + error.message + '\n\n';
else if (error.messages !== undefined)
message += 'Messages: ' + error.messages.join('\n\t') + '\n\n';
if (error.filename !== undefined)
message += 'Filename: ' + error.filename + '\n';
if (error.line !== undefined)
message += 'Line Number: ' + error.line + '\n';
this.deps.$fhcAlert.alertSystemError(message);
});
},
auth: async (errors) => {
await this.ready;
this._send_array_or_object(errors, (error, title) => {
if (title)
title += ': ' + error.message;
else
title = error.message;
var message = '';
message += 'Controller name: ' + error.controller + '\n';
message += 'Method name: ' + error.method + '\n';
message += 'Required permissions: ' + error.required_permissions;
this.deps.$fhcAlert.alertDefault(
'error',
title,
message,
true
);
});
}
}
};
}
_send_array_or_object(errors, func) {
if (!errors) return;
if (Array.isArray(errors)) {
errors.forEach(error => func(error));
return;
}
// Handle Single Error Object
if (errors.type) {
func(errors);
return;
}
// Handle Category Container
Object.entries(errors).forEach(([title, value]) => {
const errorList = Array.isArray(value) ? value : [value];
errorList.forEach(error => {
if (error && typeof error === 'object') {
func(error, title);
}
});
});
}
get_config(form, uri, data, config) {
if (typeof form == 'string' && config === undefined) {
[uri, data, config] = [form, uri, data];
form = undefined;
} else if (form) {
if (typeof form != 'object')
throw new TypeError('Parameter 1 of _get_config must be an object or a string');
if (uri === undefined && data === undefined && config === undefined) {
config = form;
form = undefined;
}
}
if (form) {
// NOTE(chris): check if form is fhc-form
if (!form.clearValidation || !form.setFeedback)
throw new TypeError("'form' is not a Form Component");
form = {
clearValidation: form.clearValidation,
setFeedback: form.setFeedback
};
if (config)
config.form = form;
else
config = {form};
}
return [uri, data, config];
}
clean_return_value(response) {
if (typeof response.data === 'string' || response.data instanceof String)
return this.clean_return_value({ data: response });
const result = response.data;
delete response.data;
if (!result)
return {meta: {response}, data: null};
if (!result.meta)
result.meta = { response };
else
result.meta.response = response;
return result;
}
merge_error_config(config) {
if (config === false || config === 'off')
return { ...this.DEFAULT_ERROR_CONFIG, success: false, fail: false };
if (!config || config === true)
return { ...this.DEFAULT_ERROR_CONFIG };
if (config === 'success')
return { ...this.DEFAULT_ERROR_CONFIG, fail: false };
if (config === 'fail')
return { ...this.DEFAULT_ERROR_CONFIG, success: false };
const { success, fail, handler, combine } = config;
config = { ...this.DEFAULT_ERROR_CONFIG };
Object.entries({ fail, success }).forEach(([key, value]) => {
if (value !== undefined)
config[key] = value;
});
Object.entries({ handler, combine }).forEach(([key, value]) => {
if (value !== undefined)
config[key] = { ...config[key], ...value };
});
return config;
}
get_error_handler(config) {
const result = this.merge_error_config(config?.errorHandling);
if (!config?.form) {
result.combine = { ...result.combine, form: [] };
} else {
const formHandler = result.handler.form;
result.handler = { ...result.handler, form: errors => formHandler(config.form, errors) };
}
return result;
}
get_error_list(error) {
if (error.response) {
if (error.response.status == 404) {
return [{
type: 'not_found',
message: error.message,
url: error.request.responseURL
}];
} else {
if (error.response.data.errors == undefined) return [];
return error.response.data.errors;
}
} else if (error.request) {
return [{
type: 'site_failed',
message: error.message,
url: error.request.responseURL
}];
} else {
return [{
type: 'script',
message: error.message
}];
}
}
popHandleableErrors(errorHandling, errors) {
const result = {};
const copy = [];
if (errors == undefined) return {};
while (errors.length)
copy.push(errors.pop());
for (let error of copy) {
let type = error.type;
let newType = null;
for (let t in errorHandling.combine) {
let newTypeCombinesType = errorHandling
.combine[t]
.includes(type);
let newTypeHasHandler = errorHandling.handler[t];
if (newTypeCombinesType && newTypeHasHandler) {
newType = t;
if (newType == 'form')
break;
}
}
if (newType)
type = newType;
const handler = errorHandling.handler[type];
if (handler) {
if (!result[type])
result[type] = [];
if (Array.isArray(error))
result[type].push(...error);
else
result[type].push(error);
continue;
}
errors.push(error);
}
return result;
}
// === private functions END ===
}
export default BaseApi;
+100
View File
@@ -0,0 +1,100 @@
import ApiPhrasen from '../../api/factory/phrasen.js';
const categories = Vue.reactive({});
const loadingModules = {};
let user_language = Vue.ref(FHC_JS_DATA_STORAGE_OBJECT.user_language);
export const user_locale = Vue.computed(() => {
if (!user_language.value) return null;
return FHC_JS_DATA_STORAGE_OBJECT.server_languages.find(l => l.sprache == user_language.value).LC_Time;
});
function extractCategory(obj, category) {
return obj.filter(e => e.category == category).reduce((res, elem) => {
if (!res[elem.phrase]) res[elem.phrase] = elem.text;
return res;
}, {});
}
function getValueForLoadedPhrase(category, phrase, params) {
let result = categories[category][phrase];
if (!result)
return '<< PHRASE ' + phrase + '>>';
if (params)
result = result.replace(/\{([^}]*)\}/g, (match, p1) => params[p1] === undefined ? match : params[p1]);
return result;
}
export default {
init(app) {
// Create a controller to resolve the promise later
let resolveReady;
const readyPromise = new Promise(resolve => { resolveReady = resolve; });
const $p = {
deps: {},
ready: readyPromise,
setDeps(deps) {
Object.assign(this.deps, deps);
// Once we have the API, we are ready to load data
if (this.deps.$api) resolveReady();
},
user_language,
user_locale,
async loadCategory(category) {
if (Array.isArray(category))
return Promise.all(category.map(cat => this.loadCategory(cat)));
// 2. SAFETY: Check if API is available via deps
await this.ready;
if (!loadingModules[category])
loadingModules[category] = this.deps.$api
.call(ApiPhrasen.loadCategory(category))
.then(res => res?.data ? extractCategory(res.data, category) : {})
.then(res => {
categories[category] = res;
});
return loadingModules[category];
},
t_ref(category, phrase, params) {
console.warn('deprecated');
return Vue.computed(() => this.t(category, phrase, params));
},
t(category, phrase, params) {
if (params === undefined && (
(Array.isArray(category) && category.length == 2) ||
(category.split && category.split('/').length == 2))
) {
params = phrase;
[category, phrase] = category.split ? category.split('/') : category;
}
if (phrase === undefined) {
console.error('invalid input', category, phrase, params);
return '';
}
let val = Vue.computed(() => {
if (!categories[category])
return '';
return getValueForLoadedPhrase(category, phrase, params);
});
if (!categories[category])
this.loadCategory(category);
return val.value;
},
async setLanguage(language) {
await this.ready;
const catArray = Object.keys(categories);
return this.deps.$api.call(ApiPhrasen.setLanguage(catArray, language)).then(res => {
res.data.forEach(row => { categories[row.category][row.phrase] = row.text; });
user_language.value = language;
return res;
});
}
};
return $p;
}
};
+37
View File
@@ -0,0 +1,37 @@
import Phrasen from './BasePhrasen.js';
import Alert from './BaseAlert.js';
import { BaseApi } from './BaseApi.js';
export default {
install(app, options = {}) {
// init in order
const $p = Phrasen.init(app);
const $fhcAlert = Alert.init(app, $p);
// try to reuse existing CoreRESTClient api instance if one has been active since before
// fhcBase Plugin install
let $api = app.config.globalProperties.$api;
if (!($api instanceof BaseApi)) {
$api = new BaseApi({ $fhcAlert, $p }, options);
} else {
// If api existed pre-app install
$api.setDependencies({ $fhcAlert, $p });
}
// set ready promise for awaiting async functions
$p.setDeps({ $api, $fhcAlert });
$fhcAlert.setDeps({ $api });
// globalProperties Binding & provide
app.config.globalProperties.$p = $p;
app.config.globalProperties.$fhcAlert = $fhcAlert;
app.config.globalProperties.$api = $api;
app.config.globalProperties.$fhcApi = $api;
app.provide('$api', $api);
app.provide('$fhcApi', $api);
app.provide('$p', $p);
app.provide('$fhcAlert', $fhcAlert);
}
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

+4 -2
View File
@@ -84,9 +84,11 @@ require_once('dbupdate_3.4/60882_lehrfaecherverteilung_favorites.php');
require_once('dbupdate_3.4/66982_berufsschule.php');
require_once('dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php');
require_once('dbupdate_3.4/47972_pruefungsverwaltung_ects_angabe.php');
require_once('dbupdate_3.4/62063_lv_evaluierung.php');
require_once('dbupdate_3.4/67490_studstatus_suche_abort_controller_haengt.php');
require_once('dbupdate_3.4/69065_Projektarbeiten_Firmen_verwalten.php');
require_once('dbupdate_3.4/68744_StV_settings.php');
require_once('dbupdate_3.4/62889_reihungstest_ueberwachung_mit_constructor.php');
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
@@ -254,7 +256,7 @@ $tabellen=array(
"lehre.tbl_lehrmittel" => array("lehrmittel_kurzbz","beschreibung","ort_kurzbz"),
"lehre.tbl_lehrmodus" => array("lehrmodus_kurzbz","bezeichnung_mehrsprachig","aktiv"),
"lehre.tbl_lehrtyp" => array("lehrtyp_kurzbz","bezeichnung"),
"lehre.tbl_lehrveranstaltung" => array("lehrveranstaltung_id","kurzbz","bezeichnung","lehrform_kurzbz","studiengang_kz","semester","sprache","ects","semesterstunden","anmerkung","lehre","lehreverzeichnis","aktiv","planfaktor","planlektoren","planpersonalkosten","plankostenprolektor","koordinator","sort","zeugnis","projektarbeit","updateamum","updatevon","insertamum","insertvon","ext_id","bezeichnung_english","orgform_kurzbz","incoming","lehrtyp_kurzbz","oe_kurzbz","raumtyp_kurzbz","anzahlsemester","semesterwochen","lvnr","farbe","semester_alternativ","old_lehrfach_id","sws","lvs","alvs","lvps","las","benotung","lvinfo","lehrauftrag","lehrmodus_kurzbz","lehrveranstaltung_template_id"),
"lehre.tbl_lehrveranstaltung" => array("lehrveranstaltung_id","kurzbz","bezeichnung","lehrform_kurzbz","studiengang_kz","semester","sprache","ects","semesterstunden","anmerkung","lehre","lehreverzeichnis","aktiv","planfaktor","planlektoren","planpersonalkosten","plankostenprolektor","koordinator","sort","zeugnis","projektarbeit","updateamum","updatevon","insertamum","insertvon","ext_id","bezeichnung_english","orgform_kurzbz","incoming","lehrtyp_kurzbz","oe_kurzbz","raumtyp_kurzbz","anzahlsemester","semesterwochen","lvnr","farbe","semester_alternativ","old_lehrfach_id","sws","lvs","alvs","lvps","las","benotung","lvinfo","lehrauftrag","lehrmodus_kurzbz","lehrveranstaltung_template_id", "evaluierung"),
"lehre.tbl_lehrveranstaltung_kompatibel" => array("lehrveranstaltung_id","lehrveranstaltung_id_kompatibel"),
"lehre.tbl_lvangebot" => array("lvangebot_id","lehrveranstaltung_id","studiensemester_kurzbz","gruppe_kurzbz","incomingplaetze","gesamtplaetze","anmeldefenster_start","anmeldefenster_ende","insertamum","insertvon","updateamum","updatevon"),
"lehre.tbl_lvregel" => array("lvregel_id","lvregeltyp_kurzbz","operator","parameter","lvregel_id_parent","lehrveranstaltung_id","studienplan_lehrveranstaltung_id","insertamum","insertvon","updateamum","updatevon"),
@@ -359,7 +361,7 @@ $tabellen=array(
"public.tbl_profil_update_status" => array("status_kurzbz","beschreibung","bezeichnung_mehrsprachig"),
"public.tbl_profil_update_topic" => array("topic_kurzbz","beschreibung","bezeichnung_mehrsprachig"),
"public.tbl_raumtyp" => array("raumtyp_kurzbz","beschreibung","kosten","aktiv"),
"public.tbl_reihungstest" => array("reihungstest_id","studiengang_kz","ort_kurzbz","anmerkung","datum","uhrzeit","updateamum","updatevon","insertamum","insertvon","ext_id","freigeschaltet","max_teilnehmer","oeffentlich","studiensemester_kurzbz","aufnahmegruppe_kurzbz","stufe","anmeldefrist","zugangs_ueberpruefung","zugangscode"),
"public.tbl_reihungstest" => array("reihungstest_id","studiengang_kz","ort_kurzbz","anmerkung","datum","uhrzeit","updateamum","updatevon","insertamum","insertvon","ext_id","freigeschaltet","max_teilnehmer","oeffentlich","studiensemester_kurzbz","aufnahmegruppe_kurzbz","stufe","anmeldefrist","zugangs_ueberpruefung","zugangscode", "externe_ueberwachung"),
"public.tbl_rueckstellung" => array("rueckstellung_id","person_id","status_kurzbz","datum_bis","insertamum","insertvon"),
"public.tbl_rueckstellung_status" => array("status_kurzbz", "bezeichnung_mehrsprachig", "sort", "aktiv"),
"public.tbl_rt_ort" => array("rt_id","ort_kurzbz","uid"),
@@ -0,0 +1,15 @@
<?php
if (! defined('DB_NAME')) exit('No direct script access allowed');
//Add column evaluierung to lehre.tbl_lehrveranstaltung
if(!@$db->db_query("SELECT evaluierung FROM lehre.tbl_lehrveranstaltung LIMIT 1"))
{
$qry = "ALTER TABLE lehre.tbl_lehrveranstaltung ADD COLUMN evaluierung boolean NOT NULL DEFAULT true;
COMMENT ON COLUMN lehre.tbl_lehrveranstaltung.evaluierung IS 'TRUE wenn für diese LV eine LV-Evaluierung durchgeführt wird';
";
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_lehrveranstaltung '.$db->db_last_error().'</strong><br>';
else
echo '<br>Spalte evaluierung zu Tabelle lehre.tbl_lehrveranstaltung hinzugefügt';
}
@@ -0,0 +1,41 @@
<?php
if (! defined('DB_NAME')) exit('No direct script access allowed');
if(!$result = @$db->db_query("SELECT externe_ueberwachung FROM public.tbl_reihungstest LIMIT 1"))
{
$qry = "ALTER TABLE public.tbl_reihungstest ADD COLUMN externe_ueberwachung boolean NOT NULL DEFAULT false;";
if(!$db->db_query($qry))
echo '<strong>public.tbl_reihungstest: '.$db->db_last_error().'</strong><br>';
else
echo '<br>public.tbl_reihungstest: Spalte externe_ueberwachung hinzugefuegt';
}
if(!$result = @$db->db_query("SELECT 1 FROM testtool.tbl_externe_ueberwachung LIMIT 1"))
{
$qry = "CREATE TABLE testtool.tbl_externe_ueberwachung (
externe_ueberwachung_id INTEGER NOT NULL,
prestudent_id INTEGER NOT NULL,
session_id UUID NOT NULL,
insertamum TIMESTAMP DEFAULT NOW(),
CONSTRAINT tbl_externe_ueberwachung_pk PRIMARY KEY(externe_ueberwachung_id)
);
CREATE SEQUENCE testtool.tbl_externe_ueberwachungg_id_seq
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;
ALTER TABLE testtool.tbl_externe_ueberwachung ALTER COLUMN externe_ueberwachung_id SET DEFAULT nextval('testtool.tbl_externe_ueberwachungg_id_seq');
ALTER TABLE testtool.tbl_externe_ueberwachung ADD CONSTRAINT fk_prestudent_externe_ueberwachung FOREIGN KEY (prestudent_id) REFERENCES public.tbl_prestudent (prestudent_id) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE testtool.tbl_externe_ueberwachung ADD CONSTRAINT unique_externe_ueberwachung_session_id UNIQUE (session_id);
GRANT SELECT, INSERT ON testtool.tbl_externe_ueberwachung TO vilesci;
GRANT SELECT, INSERT ON testtool.tbl_externe_ueberwachung TO web;
GRANT SELECT, UPDATE ON testtool.tbl_externe_ueberwachungg_id_seq TO vilesci;
GRANT SELECT, UPDATE ON testtool.tbl_externe_ueberwachungg_id_seq TO web;";
if(!$db->db_query($qry))
echo '<strong>testtool.tbl_externe_ueberwachung: '.$db->db_last_error().'</strong><br>';
else
echo '<br>testtool.tbl_externe_ueberwachung: table created';
}
+551 -2
View File
@@ -13757,13 +13757,13 @@ Any unusual occurrences
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'LektorInnen',
'text' => 'Lehrende',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Lectors',
'text' => 'Lecturers',
'description' => '',
'insertvon' => 'system'
)
@@ -52686,6 +52686,555 @@ I have been informed that I am under no obligation to consent to the transmissio
)
)
),
// LVEVALUIERUNG ---------------------------------------------------------------------------------------------------
array(
'app' => 'core',
'category' => 'global',
'phrase' => 'abschicken',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Abschicken',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Submit',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'global',
'phrase' => 'zurueckZumStart',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Zurück zum Start',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Back to Start',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'lvevaluierungAbschicken',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'LV-Evaluierung abschicken',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Submit Course Evaluation',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'lvevaluierung',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'LV-Evaluierung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Course Evaluation',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'loginTextCodeEingeben',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Bitte geben Sie Ihren Code ein, um die Evaluierung zu starten:',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Please enter your code to start the evaluation:',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'loginCodeEingeben',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Evaluierung-Code eingeben',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Enter your Evaluation-Code',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'loginTextLvevaluierung',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => '
<div class="text-start mb-3">
<p>Die folgende LV-Evaluierung umfasst</p>
</div>
<ul class="text-start small">
<li>zwei (geschlossene) Pflichtfragen</li>
<li>die Möglichkeit, optional zu einzelnen Bereichen konkreteres Feedback zu geben</li>
<li>zwei optionale Freitextfragen</li>
</ul>
',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => '
<div class="text-start mb-3">
<p>The following course evaluation includes</p>
</div>
<ul class="text-start small">
<li>two (closed) mandatory questions</li>
<li>the opportunity to optionally provide more specific feedback on individual areas</li>
<li>two optional free-text questions</li>
</ul>
',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'loginTextAntwortoptionen',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => '
<div class="text-start mb-3">
<p>Die Antwortoptionen umfassen 5 Stufen:</p>
</div>
<ul class="text-start small">
<li>Sehr gut</li>
<li>Gut</li>
<li>Mittel</li>
<li>Schlecht</li>
<li>Sehr schlecht</li>
</ul>
',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => '
<div class="text-start mb-3">
<p>The answer options comprise 5 levels:</p>
</div>
<ul class="text-start small">
<li>Excellent</li>
<li>Good</li>
<li>Satisfactory</li>
<li>Poor</li>
<li>Insufficient</li>
</ul>
',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'evaluierungPeriodeBeendet',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Der Evaluierungszeitraum endete am {date}',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Evaluation period was closed on {date}',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'evaluierungPeriodeStartetErst',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Der Evaluierungszeitraum startet erst am {date}',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Evaluation period starts on {date}',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'evaluierungEingereicht',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Die Evaluierung wurde am {date} eingereicht',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Evaluation was submitted on {date}',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'evaluierungNichtMehrVerfuegbar',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Diese Evaluierung ist nicht mehr verfügbar.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'This evaluation is no longer available.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'evaluierungNichtVerfuegbar',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Diese Evaluierung ist nicht verfügbar.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'This evaluation is not available yet.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'logoutTitle',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Danke!<br>Was passiert nun mit Ihrem Feedback?',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Thank you!<br>What happens to your feedback now?',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'logoutText',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => '
<p>Danke, dass Sie sich Zeit für das Feedback genommen haben! Ihre ehrliche und konstruktive Rückmeldung ist essenziell, damit Ihre Lehrenden und Ihre Studiengangsleitung die Lehrveranstaltung immer weiter verbessern und anpassen können!</p>
<p>Und was passiert jetzt mit Ihrem Feedback? Die Studiengangsleitung wird die Ergebnisse &amp; Maßnahmen zusammen mit Ihrer Jahrgangsvertretung besprechen. So tragen Sie aktiv dazu bei, die Lehrveranstaltung für zukünftige Semester noch spannender &amp; lehrreicher zu machen!</p>
<p>Gemeinsam für mehr Qualität in der Lehre!</p>
',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => '
<p>Thank you for taking the time to provide feedback! Your honest and constructive input is essential for your lecturers and degree program director to continuously improve and adapt the course.</p>
<p>What happens with your feedback? The degree program director will discuss the results and potential measures together with your academic year representatives. You have thus actively contributed to making the course even more engaging and educational for future semesters!</p>
<p>Working together for higher quality in teaching!</p>
',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'evaluierungZeitAbgelaufen',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Die Evaluierungszeit ist abgelaufen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'The Evaluation time is over',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'evaluierungAntwortenNichtUebermittelt',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Ihre Antworten wurden nicht übermittelt.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Your responses were not submitted.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'evaluierungCodeExistiertNicht',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Dieser Evaluierungscode exisitiert nicht',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'This evaluation code does not exist',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'evaluierungNichtAktiv',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Die Evaluierung ist nicht aktiv',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Evaluation is not active',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'zeitLaeuftAb',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Zeit läuft bald ab zum Speichern bitte Abschicken klicken',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Time is running out — please click Submit to save your answers',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'spracheAuswaehlen',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Sprache auswählen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Select language',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'fhtwLogo',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'FH Technikum Wien Logo',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'UAS Technikum Wien Logo',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'evaluierungscodeEingeben',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Evaluierungscode eingeben',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Enter Evaluation code',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'fragebogen',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Fragebogen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Questionnaire',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'fragebogen',
'phrase' => 'pflichtantwortFehlt',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Pflichtantwort fehlt',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Mandatory answer missing',
'description' => '',
'insertvon' => 'system'
)
)
),
// ### DOKUMENTE ERSTELLEN PHRASEN END ###
// ### Personen zusammenlegen Phrasen BEGIN
array(
@@ -122,6 +122,7 @@
$lv->benotung = isset($_POST['benotung']);
$lv->lvinfo = isset($_POST['lvinfo']);
$lv->lehrauftrag = isset($_POST['lehrauftrag']);
$lv->evaluierung = isset($_POST['evaluierung']);
$lv->lehrveranstaltung_template_id = $lv->lehrtyp_kurzbz == 'tpl' ? '' : $_POST['lehrveranstaltung_template_id'];
if(!$lv->save())
@@ -446,6 +447,10 @@
<td>Lehrauftrag</td>
<td><input type="checkbox" name="lehrauftrag" '.($lv->lehrauftrag?'checked':'').'></td>
</tr>
<tr>
<td>Evaulierung</td>
<td><input type="checkbox" name="evaluierung" '.($lv->evaluierung?'checked':'').'></td>
</tr>
<tr id="lehrveranstaltung_template_id">
<td>Template</td>
<td colspan="2"><input type="text" name="lehrveranstaltung_template_id" value="'.$lv->lehrveranstaltung_template_id.'" size="6"> <span class="text-template_name"></span></td>
@@ -1458,6 +1458,7 @@ if(isset($_POST['speichern']) || isset($_POST['kopieren']))
$reihungstest->anmeldefrist = $datum_obj->formatDatum($_POST['anmeldefrist']);
$reihungstest->zugangs_ueberpruefung = false;
$reihungstest->zugangscode = null;
$reihungstest->externe_ueberwachung = false;
}
else
{
@@ -1474,6 +1475,7 @@ if(isset($_POST['speichern']) || isset($_POST['kopieren']))
$reihungstest->updatevon = $user;
$reihungstest->zugangs_ueberpruefung = isset($_POST['zugangs_ueberpruefung']);
$reihungstest->zugangscode = ($_POST['zugangcode'] === '' ? null : $_POST['zugangcode']);
$reihungstest->externe_ueberwachung = isset($_POST['externe_ueberwachung']);
}
$reihungstest->studiengang_kz = $_POST['studiengang_kz'];
//$reihungstest->ort_kurzbz = $_POST['ort_kurzbz'];
@@ -2571,6 +2573,14 @@ $studienplaene_list = implode(',', array_keys($studienplaene_arr));
<input type="number" class="input" id="zugangcode" name="zugangcode" value="<?php echo $db->convert_html_chars($reihungstest->zugangscode) ?>"> (Verpflichtend, wenn die Zugangsüberprüfung aktiviert ist)
</td>
</tr>
<?php if(defined('TESTTOOL_EXTERNE_UEBERWACHUNG_ALLOWED') && TESTTOOL_EXTERNE_UEBERWACHUNG_ALLOWED) : ?>
<tr>
<td class="feldtitel">Externe Überwachnung</td>
<td>
<input type="checkbox" id="externe_ueberwachung" name="externe_ueberwachung"<?php echo $reihungstest->externe_ueberwachung ? 'checked="checked"' : '' ?>>
</td>
</tr>
<?php endif; ?>
<tr>
<td>&nbsp;</td>
</tr>