diff --git a/application/controllers/Cis/MyLv.php b/application/controllers/Cis/MyLv.php new file mode 100644 index 000000000..bcd06aee7 --- /dev/null +++ b/application/controllers/Cis/MyLv.php @@ -0,0 +1,36 @@ + 'student/anrechnung_beantragen:r' // TODO(chris): permissions? + ]); + + // Loads phrases system + $this->loadPhrases([ + 'global' + ]); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + + /** + * @return void + */ + public function index() + { + $this->load->view('Cis/MyLv'); + } + +} diff --git a/application/controllers/components/Cis/Mylv.php b/application/controllers/components/Cis/Mylv.php new file mode 100644 index 000000000..b911b5357 --- /dev/null +++ b/application/controllers/components/Cis/Mylv.php @@ -0,0 +1,216 @@ + 'student/anrechnung_beantragen:r', // TODO(chris): permissions? + 'Studiensemester' => 'student/anrechnung_beantragen:r', // TODO(chris): permissions? + 'Lvs' => 'student/anrechnung_beantragen:r', // TODO(chris): permissions? + 'Info' => 'student/anrechnung_beantragen:r', // TODO(chris): permissions? + 'Pruefungen' => 'student/anrechnung_beantragen:r' // TODO(chris): permissions? + ]); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + */ + public function Student() + { + $this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); + + $result = $this->LehrveranstaltungModel->getLvsByStudentWithGrades(get_uid()); + + if (isError($result)) + return $this->outputJsonError(getError($result)); + + $this->outputJsonSuccess(getData($result)); + } + + /** + */ + public function Studiensemester() + { + $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + + $result = $this->StudiensemesterModel->getWhereStudentHasLvs(get_uid()); + + if (isError($result)) + return $this->outputJsonError(getError($result)); + + $this->outputJsonSuccess(getData($result)); + } + + /** + */ + public function Lvs($studiensemester_kurzbz) + { + $this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); + + $result = $this->LehrveranstaltungModel->getLvsByStudentWithGrades(get_uid(), $studiensemester_kurzbz, getUserLanguage()); + + if (isError($result)) + return $this->outputJsonError(getError($result)); + + $this->outputJsonSuccess(getData($result)); + } + + /** + */ + public function Info($studiensemester_kurzbz, $lehrveranstaltung_id) + { + $this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); + $result = $this->LehrveranstaltungModel->load($lehrveranstaltung_id); + + if (isError($result)) + return $this->outputJsonError(getError($result)); + $lv = current(getData($result) ?: []); + + if (!$lv) + return $this->outputJsonError('Could\'t find Lehrveranstaltung with id: ' . $lehrveranstaltung_id); + + + $this->load->model('education/Lehreinheitmitarbeiter_model', 'LehreinheitmitarbeiterModel'); + + $result = $this->LehreinheitmitarbeiterModel->getForLv($lehrveranstaltung_id, $studiensemester_kurzbz); + + if (isError($result)) + return $this->outputJsonError(getError($result)); + + $lvinfo = []; + $lvinfo['lektoren'] = getData($result) ?: []; + + $kollisionsfreie_user = unserialize(KOLLISIONSFREIE_USER); + $lvinfo['lektoren'] = array_values(array_filter($lvinfo['lektoren'], function ($v) use ($kollisionsfreie_user) { + return !in_array($v->uid, $kollisionsfreie_user); + })); + + $lvinfo['lvLeitung'] = array_values(array_filter($lvinfo['lektoren'], function ($v) { + return $v->lehrfunktion_kurzbz == 'LV-Leitung'; + })); + + + $this->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel'); + $result = $this->OrganisationseinheitModel->getWithType($lv->oe_kurzbz); + + if (isError($result)) + return $this->outputJsonError(getError($result)); + + $lvinfo['oe'] = current(getData($result) ?: []); + + + $this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel'); + $result = $this->BenutzerfunktionModel->getBenutzerFunktionenDetailed('Leitung', $lv->oe_kurzbz); + + if (isError($result)) + return $this->outputJsonError(getError($result)); + + $lvinfo['oeLeitung'] = getData($result) ?: []; + + + $result = $this->LehrveranstaltungModel->getKoordinator($lehrveranstaltung_id, $studiensemester_kurzbz); + + if (isError($result)) + return $this->outputJsonError(getError($result)); + + $lvinfo['koordinator'] = getData($result) ?: []; + + if (defined('ACTIVE_ADDONS') && in_array('lvinfo', explode(';', ACTIVE_ADDONS)) && file_exists(FHCPATH . 'addons/lvinfo/include/lvinfo.class.php')) + { + require_once(FHCPATH . 'addons/lvinfo/include/lvinfo.class.php'); + $lvinfoObj = new lvinfo(); + $lvinfoObj->loadLVinfo($lehrveranstaltung_id, $studiensemester_kurzbz, null, true); + if (is_array($lvinfoObj->result)) + { + $oldP = property_exists($this, 'p') ? $this->p : null; + $result = []; + $lvinfos = $lvinfoObj->result; + $lvinfoSet = new lvinfo(); + $lvinfoSet->load_lvinfo_set($studiensemester_kurzbz); + foreach ($lvinfos as $lvi) + { + $this->p = null; + $this->loadPhrases('ui', $lvi->sprache); + $result[$lvi->sprache] = []; + foreach ($lvinfoSet->result as $set) + { + $key = $set->lvinfo_set_kurzbz; + if (!isset($lvi->data[$key])) + continue; + $info['header'] = $set->lvinfo_set_bezeichnung[$lvi->sprache]; + if (isset($set->einleitungstext[$lvi->sprache])) + $info['subheader'] = $set->einleitungstext[$lvi->sprache]; + switch ($set->lvinfo_set_typ) + { + case 'boolean': + $info['body'] = $this->p->t('ui', $lvi->data[$key] === true ? 'ja' : 'nein'); + break; + case 'array': + $info['body'] = array_map('htmlspecialchars', $lvi->data[$key]); + break; + case 'editor': + $info['body'] = $lvi->data[$key]; + break; + default: + $info['body'] = htmlspecialchars($lvi->data[$key]); + } + if ($info['body']) + $result[$lvi->sprache][] = $info; + } + } + if ($result) + { + $lvinfo['lvinfo'] = $result; + $lvinfo['lvinfoDefaultLang'] = getUserLanguage(); + + $this->load->model('system/Sprache_model', 'SpracheModel'); + $result = $this->SpracheModel->loadMultiple(array_keys($result)); + if (!isError($result)) + { + $result = getData($result); + $lvinfo['sprachen'] = []; + foreach ($result as $sprache) { + $lvinfo['sprachen'][$sprache->sprache] = $sprache; + } + } + } + $this->p = $oldP; + } + } + + + $this->outputJsonSuccess($lvinfo); + } + + /** + */ + public function Pruefungen($lehrveranstaltung_id) + { + $this->load->model('education/Pruefung_model', 'PruefungModel'); + + $result = $this->PruefungModel->getByStudentAndLv(get_uid(), $lehrveranstaltung_id, getUserLanguage()); + + if (isError($result)) + return $this->outputJsonError(getError($result)); + + $this->outputJsonSuccess(getData($result)); + } + +} diff --git a/application/controllers/components/Phrasen.php b/application/controllers/components/Phrasen.php new file mode 100644 index 000000000..ba0280aab --- /dev/null +++ b/application/controllers/components/Phrasen.php @@ -0,0 +1,23 @@ +load->library('PhrasesLib', [$module], 'pj'); + $this->outputJsonSuccess(json_decode($this->pj->getJSON())); + } + +} diff --git a/application/models/education/Lehreinheitmitarbeiter_model.php b/application/models/education/Lehreinheitmitarbeiter_model.php index dd5c7c858..ae1ac55d2 100644 --- a/application/models/education/Lehreinheitmitarbeiter_model.php +++ b/application/models/education/Lehreinheitmitarbeiter_model.php @@ -41,4 +41,38 @@ class Lehreinheitmitarbeiter_model extends DB_Model return error ('Incorrect parameter type'); } } + + /** + * @param integer $lehrveranstaltung_id + * @param string $studiensemester_kurzbz + * + * @return stdClass + */ + public function getForLv($lehrveranstaltung_id, $studiensemester_kurzbz) + { + $this->addSelect('ma.uid, ma.vorname, ma.nachname, ma.titelpre, ma.titelpost, lehrfunktion_kurzbz'); + $this->addGroupBy('ma.uid, ma.vorname, ma.nachname, ma.titelpre, ma.titelpost, lehrfunktion_kurzbz'); + + $this->addJoin('lehre.tbl_lehreinheit le', 'lehreinheit_id'); + $this->addJoin('campus.vw_mitarbeiter ma', $this->dbTable . '.mitarbeiter_uid=ma.uid'); + + $this->addOrder('nachname'); + $this->addOrder('vorname'); + + if (defined('CIS_LV_LEKTORINNENZUTEILUNG_VERTRAGSPRUEFUNG_VON') && CIS_LV_LEKTORINNENZUTEILUNG_VERTRAGSPRUEFUNG_VON != '') + { + $this->addJoin('(SELECT vertrag_id, CASE WHEN vertragsstatus_kurzbz=\'storno\' THEN 0 WHEN vertragsstatus_kurzbz=\'erteilt\' THEN 1 ELSE 2 END AS vertragsstatus_kurzbz FROM lehre.tbl_vertrag_vertragsstatus) v', 'vertrag_id', 'LEFT'); + $having = $this->db->compile_binds('(EXISTS (SELECT 1 FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=? AND tbl_studiensemester.start < (SELECT start FROM public.tbl_studiensemester stsem WHERE stsem.studiensemester_kurzbz=?)) OR MIN(vertragsstatus_kurzbz)=1)', [ + $studiensemester_kurzbz, + CIS_LV_LEKTORINNENZUTEILUNG_VERTRAGSPRUEFUNG_VON + ]); + $this->db->having($having); + } + + return $this->loadWhere([ + 'lehrveranstaltung_id' => $lehrveranstaltung_id, + 'studiensemester_kurzbz' => $studiensemester_kurzbz + ]); + } + } diff --git a/application/models/education/Lehrveranstaltung_model.php b/application/models/education/Lehrveranstaltung_model.php index f54443955..b85699073 100644 --- a/application/models/education/Lehrveranstaltung_model.php +++ b/application/models/education/Lehrveranstaltung_model.php @@ -231,6 +231,104 @@ class Lehrveranstaltung_model extends DB_Model return $this->execQuery($qry, $params); } + /** + * Gets Lehrveranstaltungen of a student with grades if available + * + * @param string $student_uid + * @param string $studiensemester_kurzbz + * @param string|null $sprache + * + * @return stdClass + */ + public function getLvsByStudentWithGrades($student_uid, $studiensemester_kurzbz, $sprache = null) + { + if ($sprache) { + $sprache_qry = $this->db->compile_binds('SELECT index FROM public.tbl_sprache WHERE sprache = ?', [$sprache]); + $bezeichnung = 'bezeichnung_mehrsprachig[(' . $sprache_qry . ')]'; + $sgbezeichnung = $sprache == 'English' ? 'COALESCE(sg.english, sg.bezeichnung)' : 'sg.bezeichnung'; + $lvbezeichnung = $sprache == 'English' ? 'COALESCE(v.bezeichnung_english, v.bezeichnung)' : 'v.bezeichnung'; + } else { + $bezeichnung = 'bezeichnung'; + $sgbezeichnung = 'sg.bezeichnung'; + $lvbezeichnung = 'v.bezeichnung'; + } + + $this->addDistinct(); + // TODO(chris): selects + /* + semester (?) + module + bezeichnung + sg_bezeichnung + studiengang_kuerzel + lvnote + znote + studiengang_kz + lehrveranstaltung_id + benotung + lvinfo + farbe + + sprache (?) + ects (?) + incoming (?) + orgform_kurzbz (?) + */ + // TODO(chris): module or kf + #$this->addSelect($this->dbTable . '.*'); + $this->addSelect('v.*'); + $this->addSelect($this->dbTable . '.benotung'); + $this->addSelect($this->dbTable . '.lvinfo'); + $this->addSelect($this->dbTable . '.farbe'); + $this->addSelect($this->dbTable . '.incoming'); + $this->addSelect($this->dbTable . '.orgform_kurzbz'); + $this->addSelect('v.studiengang_kz'); + $this->addSelect('v.lehrveranstaltung_id'); + $this->addSelect('v.semester'); + + $this->addSelect('v.sprache'); + $this->addSelect('v.ects'); + + #$this->addSelect('splv.module'); + $this->addSelect($lvbezeichnung . ' AS bezeichnung'); + $this->addSelect($sgbezeichnung . ' AS sg_bezeichnung'); + $this->addSelect('UPPER(sg.typ::VARCHAR(1) || sg.kurzbz) AS studiengang_kuerzel'); + + $this->addSelect('COALESCE(gnn.' . $bezeichnung . ', gnn.bezeichnung, gn.note::text) AS lvnote'); + $this->addSelect('COALESCE(znn.' . $bezeichnung . ', znn.bezeichnung, zn.note::text) AS znote'); + + $this->addJoin('campus.vw_student_lehrveranstaltung v', 'lehrveranstaltung_id'); + $this->addJoin('public.tbl_studiengang sg', $this->dbTable . '.studiengang_kz = sg.studiengang_kz'); + $this->db->where("v.lehreverzeichnis<>''"); + + $this->addJoin('campus.tbl_lvgesamtnote gn', 'gn.lehrveranstaltung_id=v.lehrveranstaltung_id AND gn.student_uid=v.uid AND gn.studiensemester_kurzbz=v.studiensemester_kurzbz', 'LEFT'); + $this->addJoin('lehre.tbl_note gnn', 'gn.note=gnn.note', 'LEFT'); + + $this->addJoin('lehre.tbl_zeugnisnote zn', 'zn.lehrveranstaltung_id=v.lehrveranstaltung_id AND zn.student_uid=v.uid AND zn.studiensemester_kurzbz=v.studiensemester_kurzbz', 'LEFT'); + $this->addJoin('lehre.tbl_note znn', 'zn.note=znn.note', 'LEFT'); + + /*if (!defined("CIS_PROFIL_STUDIENPLAN_MODULE_AUSBLENDEN") || !CIS_PROFIL_STUDIENPLAN_MODULE_AUSBLENDEN) { + $modulebezeichnung = str_replace('v.', 'm.', $lvbezeichnung); + $modulesql = ' + LEFT JOIN lehre.tbl_studienplan_lehrveranstaltung p ON(lv.studienplan_lehrveranstaltung_id_parent=p.studienplan_lehrveranstaltung_id) + LEFT JOIN lehre.tbl_lehrveranstaltung m ON(m.lehrveranstaltung_id = p.lehrveranstaltung_id)'; + } else { + $modulebezeichnung = 'NULL'; + $modulesql = ''; + } + + $this->addJoin('( + SELECT lv.lehrveranstaltung_id, sps.studiensemester_kurzbz, so.studiengang_kz, lv.semester, ' . $modulebezeichnung . ' AS module + FROM lehre.tbl_studienplan_lehrveranstaltung lv + LEFT JOIN lehre.tbl_studienplan sp ON(sp.studienplan_id=lv.studienplan_id) + JOIN lehre.tbl_studienplan_semester sps ON(sp.studienplan_id=sps.studienplan_id AND sps.semester=lv.semester) + JOIN lehre.tbl_studienordnung so ON(so.studienordnung_id=sp.studienordnung_id) + ' . $modulesql . ' + ) splv', 'splv.lehrveranstaltung_id=v.lehrveranstaltung_id AND splv.studiensemester_kurzbz=v.studiensemester_kurzbz AND splv.studiengang_kz=v.studiengang_kz', 'LEFT');*/ + + return $this->loadWhere(['v.uid' => $student_uid, 'v.lehre' => true, 'v.studiensemester_kurzbz' => $studiensemester_kurzbz]); + } + /** * Gets valid Lehrveranstaltungen with incoming places for a Studiensemester. * Only @@ -471,4 +569,76 @@ class Lehrveranstaltung_model extends DB_Model return $this->execQuery($qry, array($student_uid)); } + + /** + * @param integer $lehrveranstaltung_id + * @param string $studiensemester_kurzbz + * + * @return stdClass + */ + public function getKoordinator($lehrveranstaltung_id, $studiensemester_kurzbz = null) + { + $binds = [ + $lehrveranstaltung_id, + $lehrveranstaltung_id, + $lehrveranstaltung_id, + $lehrveranstaltung_id + ]; + $qry = " + SELECT + a.uid, vorname, nachname, titelpre, titelpost + FROM ( + SELECT + koordinator as uid + FROM + lehre.tbl_lehrveranstaltung + WHERE + lehrveranstaltung_id = ? + UNION + SELECT + uid + FROM + lehre.tbl_lehreinheit + JOIN lehre.tbl_lehrveranstaltung AS lehrfach ON(tbl_lehreinheit.lehrfach_id = lehrfach.lehrveranstaltung_id) + JOIN public.tbl_fachbereich ON(lehrfach.oe_kurzbz=tbl_fachbereich.oe_kurzbz) + JOIN public.tbl_benutzerfunktion ON(tbl_fachbereich.fachbereich_kurzbz=tbl_benutzerfunktion.fachbereich_kurzbz) + WHERE + tbl_benutzerfunktion.funktion_kurzbz='fbk' + AND (tbl_benutzerfunktion.datum_von IS null OR tbl_benutzerfunktion.datum_von <= now()) + AND (tbl_benutzerfunktion.datum_bis IS null OR tbl_benutzerfunktion.datum_bis >= now()) + AND tbl_lehreinheit.lehrveranstaltung_id = ? + AND tbl_benutzerfunktion.oe_kurzbz = ( + SELECT + tbl_studiengang.oe_kurzbz + FROM + lehre.tbl_lehrveranstaltung + JOIN public.tbl_studiengang USING(studiengang_kz) + WHERE lehrveranstaltung_id = ? + ) + AND EXISTS ( + SELECT + lehrveranstaltung_id + FROM + lehre.tbl_lehrveranstaltung + WHERE + lehrveranstaltung_id = ? + AND koordinator IS null + ) + "; + + if ($studiensemester_kurzbz !== null) + { + $qry .= " AND tbl_lehreinheit.studiensemester_kurzbz = ?"; + $binds[] = $studiensemester_kurzbz; + } + + $qry .= " + ) AS a + JOIN campus.vw_mitarbeiter ON(a.uid=vw_mitarbeiter.uid) + WHERE vw_mitarbeiter.aktiv + "; + + return $this->execQuery($qry, $binds); + } + } diff --git a/application/models/education/Pruefung_model.php b/application/models/education/Pruefung_model.php index e3776c4ad..48a6f4f3c 100644 --- a/application/models/education/Pruefung_model.php +++ b/application/models/education/Pruefung_model.php @@ -36,4 +36,34 @@ class Pruefung_model extends DB_Model return $this->execQuery($qry, array($person_id, $studiensemester_kurzbz)); } + + /** + * Gets Pruefungen of a student for a Lehrveranstaltung. + * + * @param string $uid + * @param string $lehrveranstaltung_id + * @param string|null $lehrveranstaltung_id + * + * @return object + */ + public function getByStudentAndLv($uid, $lehrveranstaltung_id, $sprache = null) + { + $this->dbTable = 'lehre.tbl_pruefung'; + + if ($sprache) { + $sprache_qry = $this->db->compile_binds('SELECT index FROM public.tbl_sprache WHERE sprache = ?', [$sprache]); + $bezeichnung = 'bezeichnung_mehrsprachig[(' . $sprache_qry . ')]'; + } else { + $bezeichnung = 'bezeichnung'; + } + + $this->addSelect($this->dbTable . '.pruefung_id, ' . $this->dbTable . '.pruefungstyp_kurzbz, ' . $this->dbTable . '.datum, COALESCE(n.' . $bezeichnung . ', n.note::text) AS note'); + + $this->addJoin('lehre.tbl_lehreinheit le', 'lehreinheit_id'); + $this->addJoin('lehre.tbl_lehrveranstaltung lv', 'lehrveranstaltung_id'); + $this->addJoin('lehre.tbl_note n', 'note'); + + return $this->loadWhere(['lehrveranstaltung_id' => $lehrveranstaltung_id, 'student_uid' => $uid]); + } + } diff --git a/application/models/organisation/Organisationseinheit_model.php b/application/models/organisation/Organisationseinheit_model.php index bec4aee47..86523cab9 100644 --- a/application/models/organisation/Organisationseinheit_model.php +++ b/application/models/organisation/Organisationseinheit_model.php @@ -188,4 +188,18 @@ class Organisationseinheit_model extends DB_Model } return $this->loadWhere($condition); } + + /** + * @param string $oe_kurzbz + * + * @return stdClass + */ + public function getWithType($oe_kurzbz) + { + $this->addSelect($this->dbTable . '.*, t.bezeichnung AS organisationseinheittyp'); + $this->addJoin('public.tbl_organisationseinheittyp t', 'organisationseinheittyp_kurzbz'); + + return $this->load($oe_kurzbz); + } + } diff --git a/application/models/organisation/Studiensemester_model.php b/application/models/organisation/Studiensemester_model.php index bb9b94c92..a7333260d 100644 --- a/application/models/organisation/Studiensemester_model.php +++ b/application/models/organisation/Studiensemester_model.php @@ -204,4 +204,22 @@ class Studiensemester_model extends DB_Model return $this->execQuery($query, array($studiensemester_kurzbz)); } + + /** + * @param string $student_uid + * + * @return StdClass + */ + public function getWhereStudentHasLvs($student_uid) + { + $this->addDistinct(); + $this->addSelect($this->dbTable . '.*'); + + $this->addJoin('campus.vw_student_lehrveranstaltung v', 'studiensemester_kurzbz'); + $this->db->where("v.lehreverzeichnis<>''"); + + $this->addOrder($this->dbTable . '.start'); + + return $this->loadWhere(['uid' => $student_uid, 'v.lehre' => true]); + } } diff --git a/application/models/system/Sprache_model.php b/application/models/system/Sprache_model.php index 202157a83..fb12b91e9 100644 --- a/application/models/system/Sprache_model.php +++ b/application/models/system/Sprache_model.php @@ -11,4 +11,19 @@ class Sprache_model extends DB_Model $this->dbTable = 'public.tbl_sprache'; $this->pk = 'sprache'; } + + /** + * @param array $sprachen + * + * @return stdClass + */ + public function loadMultiple($sprachen) + { + $this->db->where_in('sprache', $sprachen); + + $this->addOrder('index'); + + return $this->load(); + } + } diff --git a/application/views/Cis/MyLv.php b/application/views/Cis/MyLv.php new file mode 100644 index 000000000..8786fd866 --- /dev/null +++ b/application/views/Cis/MyLv.php @@ -0,0 +1,82 @@ + ['public/js/apps/Cis/MyLv/Student.js'], + 'customCSSs' => [ + 'public/css/components/dashboard.css' + ] +); + +$this->load->view('templates/CISHTML-Header', $includesArray); +?> + +
+

MyLv

+
+ + +

+ +

, .Semester

+ + + bezeichnung: bezeichnung; ?>
+ + studiensemester_kurzbz: studiensemester_kurzbz; ?>
+ + +
+ +
+ +
+ +load->view('templates/CISHTML-Footer', $includesArray); ?> diff --git a/public/js/apps/Cis/MyLv/Student.js b/public/js/apps/Cis/MyLv/Student.js new file mode 100644 index 000000000..27c340a2c --- /dev/null +++ b/public/js/apps/Cis/MyLv/Student.js @@ -0,0 +1,7 @@ +import MylvStudent from "../../../components/Cis/Mylv/Student.js"; + +Vue.createApp({ + components: { + MylvStudent + } +}).mount('#content'); \ No newline at end of file diff --git a/public/js/components/Cis/Mylv/Semester.js b/public/js/components/Cis/Mylv/Semester.js new file mode 100644 index 000000000..58d1d442e --- /dev/null +++ b/public/js/components/Cis/Mylv/Semester.js @@ -0,0 +1,44 @@ +import MylvSemesterStudiengang from "./Semester/Studiengang.js"; + +export default { + components: { + MylvSemesterStudiengang + }, + provide() { + return { + studien_semester: this.semester + } + }, + props: { + semester: String, + lvs: Array + }, + computed: { + ready() { return this.lvs !== null; }, + studiengaenge() { + return [... new Map( + this.lvs + .map(lv => [ + lv.studiengang_kz + '#' + lv.semester, + { + studiengang_kz: lv.studiengang_kz, + bezeichnung: lv.sg_bezeichnung, + kuerzel: lv.studiengang_kuerzel, + semester: lv.semester + } + ]) + ).values()].sort((a, b) => a.bezeichnung.toLowerCase() == b.bezeichnung.toLowerCase() ? a.semester > b.semester : a.bezeichnung.toLowerCase() > b.bezeichnung.toLowerCase()); + }, + }, + methods: { + lvsForStudiengang(studiengang) { + return this.lvs.filter(lv => lv.studiengang_kz == studiengang.studiengang_kz && lv.semester == studiengang.semester); + } + }, + template: `
+ +
+
+ +
` +}; \ No newline at end of file diff --git a/public/js/components/Cis/Mylv/Semester/Studiengang.js b/public/js/components/Cis/Mylv/Semester/Studiengang.js new file mode 100644 index 000000000..4f1eaa45d --- /dev/null +++ b/public/js/components/Cis/Mylv/Semester/Studiengang.js @@ -0,0 +1,45 @@ +import MylvSemesterStudiengangLv from "./Studiengang/Lv.js"; +import Phrasen from "../../../../mixins/Phrasen.js"; + +export default { + components: { + MylvSemesterStudiengangLv + }, + mixins: [ + Phrasen + ], + props: { + bezeichnung: String, + kuerzel: String, + semester: Number, + lvs: Array + }, + computed: { + lehrveranstaltungen() { + return [... new Map( + this.lvs + .map(lv => [ + lv.lehrveranstaltung_id, + lv + ]) + ).values()] + } + }, + methods: { + note(lv) { + return lv.benotung ? lv.znote || lv.lvnote || null : null; + } + }, + template: `
+
+

{{bezeichnung}} - {{kuerzel}} + {{semester}}.{{p.t('lehre/semester')}} +

+
+
+ +
+
+
+
` +}; \ No newline at end of file diff --git a/public/js/components/Cis/Mylv/Semester/Studiengang/Lv.js b/public/js/components/Cis/Mylv/Semester/Studiengang/Lv.js new file mode 100644 index 000000000..5efe5d36b --- /dev/null +++ b/public/js/components/Cis/Mylv/Semester/Studiengang/Lv.js @@ -0,0 +1,89 @@ +import LvPruefungen from "./Lv/Pruefungen.js"; +import LvInfo from "./Lv/Info.js"; + +// TODO(chris): L10n + +export default { + inject: ['studien_semester'], + props: { + lehrveranstaltung_id: Number, + bezeichnung: String, + module: String, + farbe: String, + lvinfo: Boolean, + benotung: Boolean, + lvnote: String, + znote: String, + studiengang_kuerzel: String, + semester: String, + orgform_kurzbz: String, + sprache: String, + ects: Number, + incoming: Number + }, + data: () => { + return { + pruefungen: null, + info: null + } + }, + computed: { + bodyStyle() { + const bodyStyle = {}; + if (this.farbe) + bodyStyle['background-color'] = '#' + this.farbe; + return bodyStyle; + }, + grade() { + return this.benotung ? this.znote || this.lvnote || null : null; + } + }, + methods: { + openPruefungen() { + if (!this.pruefungen) { + this.pruefungen = true; + LvPruefungen.popup({ + lehrveranstaltung_id: this.lehrveranstaltung_id, + bezeichnung: this.bezeichnung + }).then(() => this.pruefungen = false).catch(() => this.pruefungen = false); + } + }, + openInfos() { + if (!this.info) { + this.info = true; + // TODO(chris): load all this params on ajax? + LvInfo.popup({ + lehrveranstaltung_id: this.lehrveranstaltung_id, + bezeichnung: this.bezeichnung, + studiengang_kuerzel: this.studiengang_kuerzel, + semester: this.semester, + studien_semester: this.studien_semester, + orgform_kurzbz: this.orgform_kurzbz, + sprache: this.sprache, + ects: this.ects, + incoming: this.incoming + }).then(() => this.info = false).catch(() => this.info = false); + } + } + }, + template: `
+
+ {{module}} +
+
+
{{bezeichnung}}
+
+ +
` +}; \ No newline at end of file diff --git a/public/js/components/Cis/Mylv/Semester/Studiengang/Lv/Info.js b/public/js/components/Cis/Mylv/Semester/Studiengang/Lv/Info.js new file mode 100644 index 000000000..935e1e0c0 --- /dev/null +++ b/public/js/components/Cis/Mylv/Semester/Studiengang/Lv/Info.js @@ -0,0 +1,187 @@ +import BsModal from '../../../../../Bootstrap/Modal.js'; +import Phrasen from '../../../../../../mixins/Phrasen.js'; + +const infos = {}; + +export default { + components: { + BsModal + }, + mixins: [ + BsModal, + Phrasen + ], + props: { + lehrveranstaltung_id: Number, + bezeichnung: String, + studiengang_kuerzel: String, + semester: Number, + studien_semester: String, + orgform_kurzbz: String, + sprache: String, + ects: Number, + incoming: Number, + /* + * NOTE(chris): + * Hack to expose in "emits" declared events to $props which we use + * in the v-bind directive to forward all events. + * @see: https://github.com/vuejs/core/issues/3432 + */ + onHideBsModal: Function, + onHiddenBsModal: Function, + onHidePreventedBsModal: Function, + onShowBsModal: Function, + onShownBsModal: Function + }, + data: () => ({ + result: true, + info: null + }), + computed: { + lektorNames() { + return this.info.lektoren.map(e => ((e.titelpre || '') + ' ' + (e.vorname || '') + ' ' + (e.nachname || '') + ' ' + (e.titelpost || '')).trim()); + }, + lvLeitung() { + return this.info.lvLeitung && this.info.lvLeitung.length ? this.info.lvLeitung.map(e => ((e.titelpre || '') + ' ' + (e.vorname || '') + ' ' + (e.nachname || '') + ' ' + (e.titelpost || '')).trim()) : null; + }, + oe() { + return this.info.oe.organisationseinheittyp ? (this.info.oe.organisationseinheittyp + ' ' + this.info.oe.bezeichnung) : ''; + }, + oeLeitung() { + if (!this.info.oeLeitung || !this.info.oeLeitung.length) + return ['-']; + return this.info.oeLeitung.map(e => ((e.titelpre || '') + ' ' + (e.vorname || '') + ' ' + (e.nachname || '') + ' ' + (e.titelpost || '')).trim()); + }, + koordinator() { + if (!this.info.koordinator || !this.info.koordinator.length) + return null; + return this.info.koordinator.map(e => ((e.titelpre || '') + ' ' + (e.vorname || '') + ' ' + (e.nachname || '') + ' ' + (e.titelpost || '')).trim()); + }, + currentLang() { + if (!this.info) + return null; + if (this.info.lastLang) + return this.info.lastLang; + if (!this.info.lvinfo) + return null; + return this.info.lvinfoDefaultLang && this.info.lvinfo[this.info.lvinfoDefaultLang] ? this.info.lvinfoDefaultLang : Object.keys(this.info.lvinfo).shift(); + } + }, + created() { + if (infos[this.lehrveranstaltung_id]) { + this.info = infos[this.lehrveranstaltung_id]; + } else { + axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Cis/Mylv/Info/' + this.studien_semester + '/' + this.lehrveranstaltung_id).then(res => { + this.info = infos[this.lehrveranstaltung_id] = res.data.retval || []; + }).catch(() => this.info = {}); + } + }, + mounted() { + this.modal = this.$refs.modalContainer.modal; + }, + popup(options) { + return BsModal.popup.bind(this)(null, options); + }, + template: ` + + + ` +} diff --git a/public/js/components/Cis/Mylv/Semester/Studiengang/Lv/Pruefungen.js b/public/js/components/Cis/Mylv/Semester/Studiengang/Lv/Pruefungen.js new file mode 100644 index 000000000..b41b58664 --- /dev/null +++ b/public/js/components/Cis/Mylv/Semester/Studiengang/Lv/Pruefungen.js @@ -0,0 +1,72 @@ +import BsModal from '../../../../../Bootstrap/Modal.js'; + +const pruefungen = {}; + +export default { + components: { + BsModal + }, + mixins: [ + BsModal + ], + props: { + lehrveranstaltung_id: Number, + bezeichnung: String, + /* + * NOTE(chris): + * Hack to expose in "emits" declared events to $props which we use + * in the v-bind directive to forward all events. + * @see: https://github.com/vuejs/core/issues/3432 + */ + onHideBsModal: Function, + onHiddenBsModal: Function, + onHidePreventedBsModal: Function, + onShowBsModal: Function, + onShownBsModal: Function + }, + data: () => ({ + result: true, + pruefungen: null + }), + created() { + if (pruefungen[this.lehrveranstaltung_id]) + this.pruefungen = pruefungen[this.lehrveranstaltung_id]; + else + axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Cis/Mylv/Pruefungen/' + this.lehrveranstaltung_id).then(res => { + this.pruefungen = pruefungen[this.lehrveranstaltung_id] = res.data.retval || []; + }); + }, + mounted() { + this.modal = this.$refs.modalContainer.modal; + }, + popup(options) { + return BsModal.popup.bind(this)(null, options); + }, + template: ` + + + ` +} diff --git a/public/js/components/Cis/Mylv/Student.js b/public/js/components/Cis/Mylv/Student.js new file mode 100644 index 000000000..125422b01 --- /dev/null +++ b/public/js/components/Cis/Mylv/Student.js @@ -0,0 +1,116 @@ +import MylvSemester from "./Semester.js"; +import Phrasen from "../../../mixins/Phrasen.js"; + +// TODO(chris): phrase: global/studiensemester_auswaehlen +// TODO(chris): phrase: next & prev +aria-label + +export default { + components: { + MylvSemester + }, + mixins: [ + Phrasen + ], + data: () => { + return { + firstLoad: true, + studiensemester: null, + lvs: {}, + currentSemester: null + }; + }, + computed: { + ready() { + return this.studiensemester !== null && (!this.firstLoad || this.current.lvs !== null); + }, + current() { + if (this.currentSemester === null) + return { semester: null, lvs: null }; + if (this.lvs[this.currentSemester] === undefined) { + this.lvs[this.currentSemester] = { + semester: this.currentSemester, + lvs: null + }; + axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Cis/Mylv/Lvs/' + this.currentSemester).then(res => { + this.lvs[this.currentSemester].lvs = res.data.retval; + this.firstLoad = false; + }); + } + return this.lvs[this.currentSemester]; + }, + nearestSem() { + let now = Date.now(); + let nearestSem = null; + let nearestSemDiff = 0; + this.studiensemester.forEach(sem => { + let start = new Date(sem.start); + let end = new Date(sem.ende); + if (now >= start && now <= end) { + nearestSem = sem.studiensemester_kurzbz; + nearestSemDiff = 0; + return; + } + let diff = Math.min(Math.abs(now - start), Math.abs(now - end)); + if (nearestSem === null || diff < nearestSemDiff) { + nearestSem = sem.studiensemester_kurzbz; + nearestSemDiff = diff; + } + + }); + return nearestSem; + }, + currentIsFirst() { + return this.studiensemester[0].studiensemester_kurzbz == this.currentSemester; + }, + currentIsLast() { + return this.studiensemester[this.studiensemester.length-1].studiensemester_kurzbz == this.currentSemester; + } + }, + methods: { + prevSem() { + this.$refs.studiensemester.selectedIndex--; + this.$refs.studiensemester.dispatchEvent(new Event('change', { bubbles: true })); + }, + nextSem() { + this.$refs.studiensemester.selectedIndex++; + this.$refs.studiensemester.dispatchEvent(new Event('change', { bubbles: true })); + }, + setHash(val) { + location.hash = val; + } + }, + created() { + axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Cis/Mylv/Studiensemester').then(res => { + this.studiensemester = res.data.retval; + const hash = location.hash.substring(1); + if (hash && this.studiensemester.filter(s => s.studiensemester_kurzbz == hash).length) + this.currentSemester = hash; + else + this.currentSemester = this.nearestSem; + }); + }, + template: `
+
+
+ +
+
+
+ + + +
+
+
+ +
+
+ +
` +}; \ No newline at end of file diff --git a/public/js/mixins/Phrasen.js b/public/js/mixins/Phrasen.js new file mode 100644 index 000000000..2d9876ac9 --- /dev/null +++ b/public/js/mixins/Phrasen.js @@ -0,0 +1,69 @@ +const categories = {}; +const loadingModules = {}; + +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 loadLazy(category, val) { + // NOTE(chris): load module if it's not loaded yet + if (loadingModules[category]) { + loadingModules[category].push(val); + if (categories[category]) // NOTE(chris): this is for safety in case the loading finished the moment before the val was pushed into the array + while (loadingModules[category].length) + Vue.triggerRef(loadingModules[category].pop()); + return Vue.unref(val); + } + loadingModules[category] = [val]; + + axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Phrasen/LoadModule/' + category).then(res => { + if (res.data.retval) + categories[category] = extractCategory(res.data.retval, category); + else + categories[category] = {}; + + while (loadingModules[category].length) + Vue.triggerRef(loadingModules[category].pop()); + }).catch(err => console.error(err)); +} + +export default { + data: () => { + return { + p: { + 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'); + return ''; + } + if (!categories[category]) { + if (window.FHC_JS_PHRASES_STORAGE_OBJECT !== undefined) + categories[category] = extractCategory(FHC_JS_PHRASES_STORAGE_OBJECT, category); + + if (!categories[category] || Object.keys(categories[category]).length === 0) { + let val = Vue.ref(''); + loadLazy(category, val); + return Vue.unref(val); + } + } + let result = categories[category][phrase]; + if (!result) + return '<< PHRASE ' + phrase + '>>'; + if (params) + return result.replace(/\{([^}]*)\}/g, (match, p1) => params[p1] || match); + return result; + } + } + } + } +} diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 8ac09cee0..624aee0bd 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -17137,6 +17137,186 @@ array( 'insertvon' => 'system' ) ) + ), + array( + 'app' => 'core', + 'category' => 'lvinfo', + 'phrase' => 'lehrveranstaltungsinformationen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Lehrveranstaltungsinformationen", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Course Information", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'keinLektorZugeordnet', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Aktuell ist dieser Lehrveranstaltung noch kein Lektor zugeordnet", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "ENG Aktuell ist dieser Lehrveranstaltung noch kein Lektor zugeordnet", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'incomingplaetze', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Incomingplätze", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Places Available for Incoming Students", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'lehrbeauftragter', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Lehrbeauftragte*r", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Lecturer(s)", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'lvleitung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "LV-Leiter*in", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Head of Course", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'global', + 'phrase' => 'leitung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Leitung", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Head", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'global', + 'phrase' => 'koordination', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Koordination", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Coordination", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'global', + 'phrase' => 'sprache', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Sprache", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Language", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'global', + 'phrase' => 'verfuegbareSprachen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Verfügbare Sprachen", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Available languages", + 'description' => '', + 'insertvon' => 'system' + ) + ) ) );