Compare commits

..

78 Commits

Author SHA1 Message Date
ma0048 96e0dcaf17 Merge branch 'feature-75888/reihungstest_mehrfachdurchfuehrung' into demo 2026-05-05 15:20:57 +02:00
ma0048 bf5ab6b7dd nur prestudent mit dem bewerber status beruecksichtigen 2026-05-05 15:19:57 +02:00
ma0048 e229aa3639 Merge remote-tracking branch 'origin/feature-75887/reihungstest_constructor_popups' into demo 2026-04-16 14:45:50 +02:00
ma0048 eb26f4a15f Merge branch 'feature-75888/reihungstest_mehrfachdurchfuehrung' into demo
# Conflicts:
#	system/dbupdate_3.4.php
2026-04-16 14:45:00 +02:00
ma0048 13e8a1a9f6 bug fixed + infocenter performance 2026-04-16 14:21:02 +02:00
Andreas Österreicher 86538a163d Merge branch 'master' into demo 2026-04-13 11:55:16 +02:00
ma0048 b6c2f2c9c9 Merge remote-tracking branch 'origin/master' into demo
# Conflicts:
#	application/models/education/Projektarbeit_model.php
#	application/views/CisRouterView/CisRouterView.php
#	public/css/Cis4/Cis.css
#	public/js/api/factory/studiensemester.js
#	public/js/components/Stv/Studentenverwaltung/Details/Lehrveranstaltungstermine/ListLehrveranstaltungstermine.js
#	public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js
#	public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis.js
#	system/phrasesupdate.php
2026-04-01 11:34:28 +02:00
ma0048 36beb927f1 rt login zusaetzlicher check, ob die anmeldung mit dem prestudent studienplan uebereinstimmt 2026-03-16 09:51:08 +01:00
ma0048 ee41b2b68d alert und confirm auf dialog umgebaut 2026-03-16 09:40:40 +01:00
ma0048 a94366d5a8 Merge remote-tracking branch 'origin/feature-70746/lvverwaltung_lektorentausch' into demo
# Conflicts:
#	application/libraries/LektorLib.php
2026-02-12 10:37:45 +01:00
ma0048 4189346cfa Merge remote-tracking branch 'origin/feature-70656/lvverwaltung_gruppen_im_baum_und_drag_and_drop' into demo 2026-02-12 10:34:44 +01:00
ma0048 0d55998640 Merge remote-tracking branch 'origin/feature-69454/lvverwaltung_karteireiter_lv_termine' into demo 2026-02-12 10:34:25 +01:00
ma0048 0ee8bd38cf Merge remote-tracking branch 'origin/feature-69453/lv_verwaltung_karteireiter_noten' into demo 2026-02-12 10:34:12 +01:00
ma0048 e27ee82692 Merge remote-tracking branch 'origin/feature-69353/lv_verwaltung_stundensatz_auf_leer_setzen_geht_nicht' into demo 2026-02-12 10:34:00 +01:00
ma0048 5295a11326 Merge remote-tracking branch 'origin/bug-69492/bug_lvverwaltung_doppelte_eintraege' into demo 2026-02-12 10:33:46 +01:00
ma0048 c1acc84b2c Merge remote-tracking branch 'origin/bug-69283/scroll_verhalten_lv_verwaltung_anders_als_in_studierendenverwaltung' into demo 2026-02-12 10:33:32 +01:00
Andreas Österreicher fee929daa8 Fixed Merge Conflict 2026-01-28 11:38:45 +01:00
Andreas Österreicher b2f8427475 Merge branch 'master' into demo 2026-01-27 13:53:05 +01:00
ma0048 e26387a75a lektoren tausch auch bei verplanten lektoren hinzugefuegt 2026-01-19 15:08:59 +01:00
ma0048 ab63bf9d74 gruppen in baum anzeigen und drag and drop von gruppen 2026-01-12 09:27:27 +01:00
Andreas Österreicher d86f8d4114 Merge branch 'epic-56039/LV-Evaluierung' into demo 2025-12-18 13:46:11 +01:00
Andreas Österreicher ea17847fad Merge branch 'master' into demo 2025-12-18 13:40:45 +01:00
ma0048 60f128b314 tabs gleichgezogen 2025-12-09 13:00:40 +01:00
ma0048 cc0a89c924 lvverwaltung noten hinzugefuegt
noten highlight wenn keine note im zeugnis eingetragen ist
2025-12-04 14:18:13 +01:00
ma0048 8f974e8902 sql angepasst 2025-12-03 08:37:38 +01:00
ma0048 a91fe05395 funktionen umbenannt 2025-12-02 13:23:09 +01:00
ma0048 d9cdeb9773 scroll verhalten angepasst 2025-12-02 08:42:04 +01:00
ma0048 0de033e428 termine tab hinzugefuegt 2025-12-01 10:55:04 +01:00
ma0048 1ab9635039 disitnct lv id 2025-11-26 13:56:37 +01:00
ma0048 e7fdbe13d0 nullable felder speichern 2025-11-20 15:32:27 +01:00
Johann Hoffmann 5b3d06f1e0 Merge branch 'feature-61164/AbgabetoolQualityGates' into demo
# Conflicts:
#	application/controllers/api/frontend/v1/Lehre.php
#	application/models/education/Note_model.php
#	application/views/CisRouterView/CisRouterView.php
#	public/css/Cis4/Cis.css
#	system/phrasesupdate.php
2025-10-07 16:43:44 +02:00
Johann Hoffmann d48ac234b3 Merge branch 'feature-60873/GesamtnoteneingabeCis4' into demo
# Conflicts:
#	application/config/routes.php
#	application/models/education/Lehreinheit_model.php
#	application/models/education/Lehrveranstaltung_model.php
#	public/js/apps/Dashboard/Fhc.js
#	system/phrasesupdate.php
2025-10-07 16:38:21 +02:00
Johann Hoffmann a6167583a3 hinweistexte import/freigabe; distinct css for editable table cols notenvorschlag & freigabe; trigger 'getEntschuldigungsStatusForStudentOnDate' event when saving a pruefungstermin -> if akzeptierte entschuldigung is found for student on pruefungsdate it is automatically set to entschuldigt; fix event unmount lifecycle; 2025-10-07 16:26:58 +02:00
Andreas Österreicher 4ea486d320 Merge branch 'epic-56039/LV-Evaluierung' into demo 2025-10-06 12:04:35 +02:00
Andreas Österreicher e6fb47b335 Merge branch 'epic-56039/LV-Evaluierung' into demo 2025-10-01 14:45:35 +02:00
ma0048 7b9c89437d lvverwaltung 2025-09-03 11:18:40 +02:00
ma0048 027be3e30e Merge branch 'feature-60973/komponente_fuer_lehrfaecherverteilung' into demo
# Conflicts:
#	public/js/components/Tag/Tag.js
2025-09-03 10:51:56 +02:00
ma0048 3bdac887bf Merge branch 'refs/heads/feature-60973/komponente_fuer_lehrfaecherverteilung' into demo 2025-08-27 09:53:25 +02:00
ma0048 81bf016945 phrasesupdate 2025-08-27 09:43:27 +02:00
ma0048 1167251bdc Merge branch 'refs/heads/feature-60973/komponente_fuer_lehrfaecherverteilung' into demo
# Conflicts:
#	application/models/education/Lehrveranstaltung_model.php
#	application/models/person/Notiz_model.php
#	system/dbupdate_3.4.php
#	system/phrasesupdate.php
2025-08-27 09:29:50 +02:00
ma0048 407f62976c Merge branch 'refs/heads/feature-63364/pep_self_overview' into demo 2025-08-27 09:23:36 +02:00
ma0048 26db68726a Merge branch 'refs/heads/master' into demo
# Conflicts:
#	application/models/education/Lehrveranstaltung_model.php
#	application/models/organisation/Studienplan_model.php
#	application/models/person/Notiz_model.php
#	application/models/ressource/Mitarbeiter_model.php
#	public/js/components/Stv/Studentenverwaltung.js
#	public/js/components/Stv/Studentenverwaltung/Verband.js
#	system/dbupdate_3.4.php
#	system/phrasesupdate.php
2025-08-27 09:22:35 +02:00
Johann Hoffmann 1e68eb0b90 berechtigungsprüfung 'lehre/benotungstool:rw' in Noten Controller; API Method documentation; removed addMeta statements; removed Tabulator Event logging; 2025-08-21 13:35:38 +02:00
Johann Hoffmann 332efd4106 selection fixes; phrases; preserve scroll after redraw when pressing action buttons on far right; take nav offset into account for width calculation; 2025-08-20 16:25:21 +02:00
Johann Hoffmann f303191c54 alternative email per cis global config 2025-08-19 16:33:12 +02:00
Johann Hoffmann d6c7f16ceb Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4 2025-08-19 13:43:23 +02:00
Johann Hoffmann f1912fe739 custom selection handling due to bugged tab5 rowSelect, should work as intended now, WIP illegal emails 2025-08-19 13:32:18 +02:00
ma0048 d8d9521c9c pep self overview hinzugefuegt
tag readonly modus
2025-08-19 10:49:21 +02:00
Johann Hoffmann 2f7fe05d21 mobility legende; TopCalc Row (sum, negative, prueflinge); fix row selection issues; 2025-08-18 11:25:38 +02:00
Johann Hoffmann ee4b61f549 recommit branch; event naming changes; 2025-08-18 09:13:14 +02:00
Johann Hoffmann 511a4256bc moved LE loading and infoString setup into LehreinheitenModule, which can be bound to an instance of Primevue3 Dropdown via v-bind. WIP modularizing other common selections like LVA & Semester Kurzbz; 2025-08-08 13:35:01 +02:00
Johann Hoffmann 3c9db86df2 Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	application/config/routes.php
#	application/controllers/api/frontend/v1/Lehre.php
#	application/models/education/Lehrveranstaltung_model.php
#	application/views/CisRouterView/CisRouterView.php
#	public/js/apps/Dashboard/Fhc.js
#	system/phrasesupdate.php
2025-08-07 15:17:10 +02:00
Johann Hoffmann 367204a1ee removed legacy classes (except mobility) and moved crud functionality to LePruefungModel, LVgesamtnoteModel & LehrveranstaltungModel; 2025-08-07 14:54:41 +02:00
Johann Hoffmann bbe55a75ea noten/pruefungen import; import validation for nr of antritte and date/antritt chronological order; 2025-08-04 14:27:33 +02:00
Johann Hoffmann e58bf3a8cf WIP noten/pruefung import 2025-08-01 09:48:46 +02:00
Johann Hoffmann 1f2f866c61 positiv/negativ/unbenotet filter; reload lva/le correctly on sem/lva dropdown selection change; 2025-07-30 17:10:43 +02:00
Johann Hoffmann 6ccbc95697 createPruefung entry with "noch nicht eingetragen" note as default for a selection of students at given date; filterHeaders in noten cols; antrittCount col; pruefungsformatter with antritt highlighting; 2025-07-29 17:32:07 +02:00
Johann Hoffmann 52d9e0a195 pruefungen columns pro datum nicht pro termintyp; row Selection & modal newPruefungForSelectedStudents mit linked multiselect dropdown; WIP antritte berechnen 2025-07-25 12:50:51 +02:00
Johann Hoffmann 6a3982347b teilnoten/punkte berechnen für notenvorschläge; vorschläge übernehmen; noten freigabe mit passwort; prüfungen generisch anzeigen nach datum gruppiert; prüfungen anlegen/bearbeiten auf mapping termin1/2/3 möglich. 2025-07-24 17:02:57 +02:00
ma0048 10597a73a9 Merge branch 'feature-60973/komponente_fuer_lehrfaecherverteilung' into demo 2025-07-23 14:16:12 +02:00
ma0048 8623a4e569 Merge branch 'feature-60973/komponente_fuer_lehrfaecherverteilung' into demo 2025-07-17 16:22:19 +02:00
Johann Hoffmann fe7feeb74e Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4 2025-07-15 09:51:11 +02:00
ma0048 7eee9df8a6 phrases gefixed 2025-07-11 07:45:50 +02:00
ma0048 57af813cb4 Merge branch 'feature-60973/komponente_fuer_lehrfaecherverteilung' into demo
# Conflicts:
#	system/dbupdate_3.4.php
#	system/phrasesupdate.php
2025-07-11 07:41:17 +02:00
Johann Hoffmann f4a175b93e Merge branch 'feature-61592/AnwesenheitenFinetuning' into demo 2025-07-04 11:33:08 +02:00
Johann Hoffmann f2180981b4 Merge branch 'feature-61592/AnwesenheitenFinetuning' into demo 2025-07-03 15:51:27 +02:00
Johann Hoffmann c5abf8c293 Merge branch 'feature-61592/AnwesenheitenFinetuning' into demo 2025-07-03 13:32:56 +02:00
Johann Hoffmann d9cddd768f Merge branch 'feature-61592/AnwesenheitenFinetuning' into demo 2025-07-03 11:33:40 +02:00
Johann Hoffmann a904328aaf Revert "Added getTemplateLvTree method to Lehrveranstaltung_model.php"
This reverts commit f3d768bfdd.
2025-07-03 11:09:40 +02:00
Johann Hoffmann 74b1640a52 Merge branch 'feature-61592/AnwesenheitenFinetuning' into demo 2025-07-02 15:27:17 +02:00
Cris f3d768bfdd Added getTemplateLvTree method to Lehrveranstaltung_model.php
This method gets all Templates and unions with all Lehrveranstaltungen of given Studiensemester and Oes, that are assigned to
a template. This data structure can be used for nested tabulator data tree.
2025-07-02 15:25:25 +02:00
Johann Hoffmann af9ff4dbc8 merge; 2025-07-02 15:25:25 +02:00
ma0048 0cfaf32956 - nicht gestartete gebiete markieren 2025-07-02 15:25:25 +02:00
Andreas Österreicher fee0037eec Merge branch 'feature-55978/infocenter_electronic_onboarding_filter' into demo 2025-06-16 11:37:33 +02:00
Andreas Österreicher 6c99571096 Merge branch 'feature-55978/infocenter_electronic_onboarding_filter' into demo 2025-06-16 11:22:52 +02:00
Andreas Österreicher 909d7f62f2 Merge branch 'feature-40314/Electronic_Onboarding_Anbindung_IDA' into demo 2025-06-16 11:02:52 +02:00
Johann Hoffmann 658fe79ad7 load Teilnoten via moodle Event trigger; WIP further noten/punkte logic; 2025-05-12 16:45:33 +02:00
Johann Hoffmann 5bbf05ac8a WIP Gesamtnoteneingabe Notenberechnung endpoint 2025-05-12 12:50:56 +02:00
142 changed files with 7525 additions and 4874 deletions
-11
View File
@@ -38,19 +38,8 @@ $config['SIGNATUR_CHECK_PAABGABETYPEN'] = ['end'];
// to be used as "https://moodle.technikum-wien.at/course/view.php?idnumber=dl{$stg_kz}" for stg specific moodle routing
$config['STG_MOODLE_LINK'] = 'https://moodle.technikum-wien.at/course/view.php?idnumber=dl';
// TODO: check if these links change if the file changes and how to better retrieve the link?
$config['SIGNATUR_INFO_LINK_GERMAN'] = 'https://cis.technikum-wien.at/cms/dms.php?id=214779';
$config['SIGNATUR_INFO_LINK_ENGLISH'] = 'https://cis.technikum-wien.at/cms/dms.php?id=264256';
$config['ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT'] = true;
$config['ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER'] = true;
$config['BETREUER_SAMMELMAIL_BUTTON_STUDENT'] = true;
$config['MULTIEDIT_TABLE'] = true;
$config['STUDENT_EDIT_PROJEKTARBEIT_TITLE'] = true;
$config['CONFETTI_ON_ENDUPLOAD'] = true;
+1
View File
@@ -63,6 +63,7 @@ $route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1';
$route['Cis/LvPlan/.*'] = 'Cis/LvPlan/index/$1';
$route['Cis/MyLvPlan/.*'] = 'Cis/MyLvPlan/index/$1';
$route['Cis/Benotungstool/.*'] = 'Cis/Benotungstool/index/$1';
$route['Cis/MyLv/.*'] = 'Cis/MyLv/index/$1';
$route['Abgabetool/Assistenz'] = 'Cis/Abgabetool/Assistenz';
@@ -0,0 +1,51 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*
*/
class Benotungstool extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct([
'index' => self::PERM_LOGGED
]);
$this->_ci =& get_instance();
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @return void
*/
public function index()
{
// TODO: check if related CIS config is also loaded when being routed in Cis4 by vuerouter
// TODO: check if new benotungstool should be configurable the exact same way?
$viewData = array(
'uid'=>getAuthUID(),
'CIS_GESAMTNOTE_UEBERSCHREIBEN' => CIS_GESAMTNOTE_UEBERSCHREIBEN,
'CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF' => CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF,
'CIS_GESAMTNOTE_PRUEFUNG_TERMIN3' => CIS_GESAMTNOTE_PRUEFUNG_TERMIN3,
'CIS_GESAMTNOTE_PRUEFUNG_TERMIN2' => CIS_GESAMTNOTE_PRUEFUNG_TERMIN2,
'CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE' => CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE,
'CIS_GESAMTNOTE_PUNKTE' => CIS_GESAMTNOTE_PUNKTE,
'CIS_GESAMTNOTE_GEWICHTUNG' => CIS_GESAMTNOTE_GEWICHTUNG,
'CIS_ANWESENHEITSLISTE_NOTENLISTE_ANZEIGEN' => CIS_ANWESENHEITSLISTE_NOTENLISTE_ANZEIGEN
);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Benotungstool']);
}
}
@@ -35,12 +35,9 @@ class Abgabe extends FHCAPI_Controller
'getStudentProjektabgaben' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_student:rw', 'basis/abgabe_lektor:rw'),
'postStudentProjektarbeitZwischenabgabe' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_student:rw'),
'postStudentProjektarbeitEndupload' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_student:rw'),
'postStudentProjektarbeitTitel' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_student:rw'),
'getMitarbeiterProjektarbeiten' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_lektor:rw'),
'postProjektarbeitAbgabe' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_lektor:rw'),
'patchProjektarbeitAbgabeMultiple' => array('basis/abgabe_assistenz:rw'),
'deleteProjektarbeitAbgabe' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_lektor:rw'),
'deleteProjektarbeitAbgabeMultiple' => array('basis/abgabe_assistenz:rw'),
'postSerientermin' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_lektor:rw'),
'fetchDeadlines' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_lektor:rw'),
'getPaAbgabetypen' => self::PERM_LOGGED,
@@ -93,7 +90,6 @@ class Abgabe extends FHCAPI_Controller
$ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT = $this->config->item('ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT');
$ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER = $this->config->item('ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER');
$BETREUER_SAMMELMAIL_BUTTON_STUDENT = $this->config->item('BETREUER_SAMMELMAIL_BUTTON_STUDENT');
$MULTIEDIT_TABLE = $this->config->item('MULTIEDIT_TABLE');
$ret = array(
'old_abgabe_beurteilung_link' => $old_abgabe_beurteilung_link,
@@ -101,7 +97,7 @@ class Abgabe extends FHCAPI_Controller
'abgabetypenBetreuer' => $abgabetypenBetreuer,
'ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT' => $ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT,
'ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER' => $ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER,
'MULTIEDIT_TABLE' => $MULTIEDIT_TABLE,
'BETREUER_SAMMELMAIL_BUTTON_STUDENT' => $BETREUER_SAMMELMAIL_BUTTON_STUDENT,
);
$this->terminateWithSuccess($ret);
@@ -111,18 +107,10 @@ class Abgabe extends FHCAPI_Controller
* loads config related to abgabetool for students to avoid handing out links reserved for employees
*/
public function getConfigStudent() {
$moodle_link = $this->config->item('STG_MOODLE_LINK');
$title_edit_allowed = $this->config->item('STUDENT_EDIT_PROJEKTARBEIT_TITLE');
$confetti_on_endupload = $this->config->item('CONFETTI_ON_ENDUPLOAD');
$siginfolink_german = $this->config->item('SIGNATUR_INFO_LINK_GERMAN');
$siginfolink_english = $this->config->item('SIGNATUR_INFO_LINK_ENGLISH');
$moodle_link =$this->config->item('STG_MOODLE_LINK');
$ret = array(
'moodle_link' => $moodle_link,
'title_edit_allowed' => $title_edit_allowed,
'confetti_on_endupload' => $confetti_on_endupload,
'siginfolink_german' => $siginfolink_german,
'siginfolink_english' => $siginfolink_english
);
$this->terminateWithSuccess($ret);
@@ -199,8 +187,8 @@ class Abgabe extends FHCAPI_Controller
} else {
$result = $this->ProjektarbeitModel->getStudentProjektarbeitenWithBetreuer(getAuthUID());
}
$projektarbeiten = hasData($result) ? getData($result) : array();
$projektarbeiten = getData($result);
if(count($projektarbeiten)) {
foreach($projektarbeiten as $pa) {
@@ -422,16 +410,9 @@ class Abgabe extends FHCAPI_Controller
$this->checkAbgabeSignatur($paabgabe, $projektarbeit->student_uid);
$signaturstatus = $paabgabe->signatur;
// update projektarbeit cols with zusatzdaten AND abgabedatum!
$this->ProjektarbeitModel->update($projektarbeit->projektarbeit_id, array(
'sprache' => $sprache,
'seitenanzahl' => $seitenanzahl,
'abgabedatum' => date('Y-m-d'),
'schlagwoerter_en' => $schlagwoerter_en,
'schlagwoerter' => $schlagwoerter,
'abstract' => $abstract,
'abstract_en' => $abstract_en
));
// update projektarbeit cols
$this->ProjektarbeitModel->updateProjektarbeit($projektarbeit_id, $sprache, $abstract, $abstract_en
, $schlagwoerter, $schlagwoerter_en, $seitenanzahl);
// update paabgabe datum
@@ -467,218 +448,7 @@ class Abgabe extends FHCAPI_Controller
}
}
/**
* POST METHOD
* allows a student (or assistenz on their behalf) to update the titel of their own projektarbeit.
* blocked once the projektarbeit has been graded (checkProjektarbeitForFinishedStatus).
*/
public function postStudentProjektarbeitTitel()
{
if(!$this->config->item('STUDENT_EDIT_PROJEKTARBEIT_TITLE')) {
$this->terminateWithError($this->p->t('global', 'c4studentEditNotAllowed'), 'general');
};
$projektarbeit_id = $this->input->post('projektarbeit_id');
$titel = $this->input->post('titel');
if ($projektarbeit_id === NULL || trim((string)$projektarbeit_id) === ''
|| $titel === NULL || trim((string)$titel) === '') {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
// strip all HTML tags to prevent XSS in mail bodies, table views and Projektarbeitsbenotung
$titel = trim(strip_tags($titel));
if ($titel === '') {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
// Reject emojis and pictographs
// allows foreign letters, math symbols, accents, and standard punctuation.
$emojiPattern = '/[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F900}-\x{1FAFF}\x{23E9}-\x{23EF}\x{2b50}\x{2700}-\x{27BF}]/u';
// i would like this much more but our server does not recognize this utf-8 character range this way, so hexcodes it is
// if (preg_match('/\p{Extended_Pictographic}/u', $titel)) {
if (preg_match($emojiPattern, $titel)) {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$this->checkProjektarbeitForFinishedStatus($projektarbeit_id);
$this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel');
// Verify the projektarbeit actually belongs to the supplied student_uid
$res = $this->ProjektarbeitModel->getStudentInfoForProjektarbeitId($projektarbeit_id);
if (isError($res) || !hasData($res)) {
$this->terminateWithError($this->p->t('abgabetool', 'c4projektarbeitNichtGefunden'), 'general');
}
$assignedStudentUid = getData($res)[0]->uid;
// A student may only update their own title; assistenz is covered by checkZuordnung
$zugeordnet = $this->checkZuordnung($projektarbeit_id, getAuthUID());
if (getAuthUID() !== $assignedStudentUid && !$zugeordnet) {
$this->terminateWithError($this->p->t('abgabetool', 'c4noZuordnungBetreuerStudent'), 'general');
}
$result = $this->ProjektarbeitModel->load($projektarbeit_id);
$data = getData($result);
$oldTitle = $data[0]->titel ?? '';
$result = $this->ProjektarbeitModel->update(
$projektarbeit_id,
array(
'titel' => $titel,
'updatevon' => getAuthUID(),
'updateamum' => date('Y-m-d H:i:s')
)
);
$this->getDataOrTerminateWithError($result, 'general');
$this->logLib->logInfoDB(array(
'titelUpdate',
array(
'projektarbeit_id' => $projektarbeit_id,
'titel' => $titel,
'updatevon' => getAuthUID(),
'updateamum' => date('Y-m-d H:i:s')
),
getAuthUID(),
getAuthPersonId()
));
$this->sendTitelChangedEmail(
$projektarbeit_id,
$titel,
$oldTitle,
$assignedStudentUid
);
$result = $this->ProjektarbeitModel->load($projektarbeit_id);
$titel = hasData($result) ? getData($result)[0]->titel : $titel;
$this->terminateWithSuccess($titel);
}
/**
* Notifies all betreuer of a projektarbeit and all assistenzen responsible for its studiengang
* when a student updates the titel of their projektarbeit.
*
* Betreuer retrieval mirrors AbgabetoolJob->notifyBetreuerAboutChangedAbgaben.
* Assistenz retrieval mirrors AbgabetoolJob->notifyAssistenzAboutChangedAbgaben.
*
* @param int $projektarbeit_id
* @param string $new_titel The titel as it was saved
* @param string $student_uid
*/
private function sendTitelChangedEmail($projektarbeit_id, $new_titel, $old_titel, $student_uid)
{
$this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel');
$this->load->model('education/Projektbetreuer_model', 'ProjektbetreuerModel');
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$this->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
$this->load->model('person/Person_model', 'PersonModel');
$this->load->model('person/Person_model', 'PersonModel');
$studentNameResult = $this->PersonModel->getFullName($student_uid);
$studentFullName = hasData($studentNameResult) ? getData($studentNameResult) : $student_uid;
$studentInfoResult = $this->ProjektarbeitModel->getStudentInfoForProjektarbeitId($projektarbeit_id);
if (isError($studentInfoResult) || !hasData($studentInfoResult)) {
$this->logLib->logInfoDB(array('sendTitelChangedEmail: student info not found', $projektarbeit_id));
return;
}
$studentInfo = getData($studentInfoResult)[0];
$studiengang_kz = $studentInfo->studiengang_kz;
$stgResult = $this->StudiengangModel->load($studiengang_kz);
$oe_kurzbz = null;
if (!isError($stgResult) && hasData($stgResult)) {
$oe_kurzbz = getData($stgResult)[0]->oe_kurzbz ?? null;
}
// build shared mail data
$base_mail_data = array(
'studentFullName' => $studentFullName,
'new_titel' => $new_titel,
'old_titel' => $old_titel
);
// notify all betreuer
$betreuerResult = $this->ProjektbetreuerModel->getAllBetreuerOfProjektarbeit($projektarbeit_id);
if (!isError($betreuerResult) && hasData($betreuerResult)) {
$linkAbgabetool = APP_ROOT . $this->config->item('URL_MITARBEITER');
foreach (getData($betreuerResult) as $betreuer) {
$email = $betreuer->uid ? $betreuer->uid . '@' . DOMAIN : ($betreuer->private_email ?? null);
if (!$email) {
$this->logLib->logInfoDB(array('sendTitelChangedEmail: no email for betreuer', $betreuer->person_id));
continue;
}
$anredeResult = $this->ProjektarbeitModel->getProjektbetreuerAnrede($betreuer->person_id);
$anrede = (!isError($anredeResult) && hasData($anredeResult)) ? getData($anredeResult)[0] : null;
$mail_data = array_merge($base_mail_data, array(
'anredeFillString' => ($anrede->anrede ?? '') === 'Herr' ? 'r' : '',
'anrede' => $anrede->anrede ?? '',
'fullFormattedNameString' => $anrede->first ?? $email,
'linkAbgabetool' => $linkAbgabetool,
));
sendSanchoMail(
'PATitleUpdated',
$mail_data,
$email,
$this->p->t('abgabetool', 'c4PATitleChanged')
);
}
}
// notify assistenz for the studiengang OE
if (!$oe_kurzbz) {
$this->logLib->logInfoDB(array('sendTitelChangedEmail: no oe_kurzbz resolved, skipping assistenz', $studiengang_kz));
return;
}
$assistenzResult = $this->OrganisationseinheitModel->getAssistenzForOE($oe_kurzbz);
if (isError($assistenzResult) || !hasData($assistenzResult)) {
return;
}
$linkAbgabetool = APP_ROOT . $this->config->item('URL_ASSISTENZ');
// similar pattern as job uses via the assistenzMap
$sentTo = [];
foreach (getData($assistenzResult) as $assistenz) {
if (in_array($assistenz->person_id, $sentTo)) {
continue;
}
$sentTo[] = $assistenz->person_id;
$email = $assistenz->uid . '@' . DOMAIN;
$mail_data = array_merge($base_mail_data, array(
'anredeFillString' => $assistenz->anrede === 'Herr' ? 'r' : '',
'anrede' => $assistenz->anrede ?? '',
'fullFormattedNameString' => $assistenz->first ?? ($assistenz->uid . '@' . DOMAIN),
'linkAbgabetool' => $linkAbgabetool,
));
sendSanchoMail(
'PATitleUpdated',
$mail_data,
$email,
$this->p->t('abgabetool', 'c4PATitleChanged')
);
}
}
// validate paabgabe deadline against servertime just in case a student spoofs their local clock and thus
// unlocks the upload ui
private function checkPaabgabeDeadline($paabgabe_id) {
@@ -854,16 +624,6 @@ class Abgabe extends FHCAPI_Controller
$existingPaabgabeArr = getData($existingResult);
if(count($existingPaabgabeArr) > 0) $existingPaabgabe = $existingPaabgabeArr[0];
if($existingPaabgabe->note !== null || $existingPaabgabe->abgabedatum !== null) {
// check if a change of paabgabetyp is being attempted -> not allowed at this point
if($paabgabetyp_kurzbz !== $existingPaabgabe->paabgabetyp_kurzbz) {
$this->terminateWithError($this->p->t('abgabetool', 'c4abgabetypAendernNichtErlaubt'));
}
// check if a change of deadline aka datum is being attempted -> should not be allowed at this point?
}
$result = $this->PaabgabeModel->update(
$paabgabe_id,
array(
@@ -927,99 +687,6 @@ class Abgabe extends FHCAPI_Controller
$this->terminateWithSuccess([$paabgabe, $existingPaabgabe]);
}
/**
* called by abgabetool/assistenz when bulk-editing multiple abgabetermine via the flat termine table view
* only fields present in the payload are updated - absent fields are left untouched
*/
public function patchProjektarbeitAbgabeMultiple() {
$paabgabe_ids = $this->input->post('paabgabe_ids');
if ($paabgabe_ids === NULL || !is_array($paabgabe_ids) || count($paabgabe_ids) === 0) {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
// collect only fields that were actually sent
$updateFields = [];
$datum = $this->input->post('datum');
if ($datum !== NULL && trim((string)$datum) !== '') {
$updateFields['datum'] = $datum;
}
$paabgabetyp_kurzbz = $this->input->post('paabgabetyp_kurzbz');
if ($paabgabetyp_kurzbz !== NULL && trim((string)$paabgabetyp_kurzbz) !== '') {
$updateFields['paabgabetyp_kurzbz'] = $paabgabetyp_kurzbz;
}
$kurzbz = $this->input->post('kurzbz');
if ($kurzbz !== NULL) {
$updateFields['kurzbz'] = $kurzbz;
}
// booleans: only include if explicitly posted
$upload_allowed = $this->input->post('upload_allowed');
if ($upload_allowed !== NULL) {
$updateFields['upload_allowed'] = filter_var($upload_allowed, FILTER_VALIDATE_BOOLEAN);
}
$fixtermin = $this->input->post('fixtermin');
if ($fixtermin !== NULL) {
$updateFields['fixtermin'] = filter_var($fixtermin, FILTER_VALIDATE_BOOLEAN);
}
if (empty($updateFields)) {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$this->load->model('education/Paabgabe_model', 'PaabgabeModel');
$results = [];
foreach ($paabgabe_ids as $paabgabe_id) {
$paabgabe_id = trim((string)$paabgabe_id);
if ($paabgabe_id === '') {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$projektarbeit_id = $this->getProjektarbeitIDForPaabgabeID($paabgabe_id);
$this->checkProjektarbeitForFinishedStatus($projektarbeit_id);
$zugeordnet = $this->checkZuordnung($projektarbeit_id, getAuthUID());
if (!$zugeordnet) {
$this->terminateWithError($this->p->t('abgabetool', 'c4noZuordnungBetreuerStudent'), 'general');
}
$paabgabeResult = $this->PaabgabeModel->load($paabgabe_id);
$paabgabeArr = $this->getDataOrTerminateWithError($paabgabeResult, 'general');
if (count($paabgabeArr) === 0) {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$result = $this->PaabgabeModel->update(
$paabgabe_id,
array_merge($updateFields, [
'updatevon' => getAuthUID(),
'updateamum' => date('Y-m-d H:i:s')
])
);
$this->getDataOrTerminateWithError($result, 'general');
$results[] = getData($this->PaabgabeModel->load($paabgabe_id))[0];
$this->logLib->logInfoDB(array(
'paabgabe bulk updated',
$paabgabe_id,
$updateFields,
getAuthUID(),
getAuthPersonId()
));
}
$this->terminateWithSuccess($results);
}
/**
* called by abgabetool/mitarbeiter in mitarbeiterdetail.js when deleting an abgabetermin
* deletion is only possible if user is assistenz OR betreuer deletes their own custom termin
@@ -1052,55 +719,11 @@ class Abgabe extends FHCAPI_Controller
$result = $this->PaabgabeModel->delete($paabgabe_id);
$result = $this->getDataOrTerminateWithError($result, 'general');
// TODO: consider this in nightly email job
$this->logLib->logInfoDB(array($paabgabeArr[0], getAuthUID(), getAuthPersonId()));
$this->terminateWithSuccess($result);
}
/**
* called by abgabetool/assistenz when deleting multiple abgabetermine via the flat termine table view
*/
public function deleteProjektarbeitAbgabeMultiple() {
$paabgabe_ids = $this->input->post('paabgabe_ids');
if ($paabgabe_ids === NULL || !is_array($paabgabe_ids) || count($paabgabe_ids) === 0) {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$this->load->model('education/Paabgabe_model', 'PaabgabeModel');
$results = [];
foreach ($paabgabe_ids as $paabgabe_id) {
$paabgabe_id = trim((string)$paabgabe_id);
if ($paabgabe_id === '') {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$this->checkProjektarbeitForFinishedStatus($this->getProjektarbeitIDForPaabgabeID($paabgabe_id));
$zugeordnet = $this->checkZuordnungByPaabgabe($paabgabe_id, getAuthUID());
if (!$zugeordnet) {
$this->terminateWithError($this->p->t('abgabetool', 'c4noZuordnungBetreuerStudent'), 'general');
}
$paabgabeResult = $this->PaabgabeModel->load($paabgabe_id);
$paabgabeArr = $this->getDataOrTerminateWithError($paabgabeResult, 'general');
if (count($paabgabeArr) == 0) {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$result = $this->PaabgabeModel->delete($paabgabe_id);
$result = $this->getDataOrTerminateWithError($result, 'general');
$results[] = $result;
$this->logLib->logInfoDB(array($paabgabeArr[0], getAuthUID(), getAuthPersonId()));
}
$this->terminateWithSuccess($results);
}
/**
* endpoint for adding the same paabgabe for multiple projektarbeiten
* can be slow for large n since it queries twice per projektarbeit_id
@@ -1483,15 +1106,9 @@ class Abgabe extends FHCAPI_Controller
$this->terminateWithError($this->p->t('abgabetool', 'c4noZuordnungBetreuerStudent'), 'general');
}
// update projektarbeit cols with zusatzdaten only
$this->ProjektarbeitModel->update($projektarbeit_id, array(
'sprache' => $sprache,
'seitenanzahl' => $seitenanzahl,
'schlagwoerter_en' => $schlagwoerter_en,
'schlagwoerter' => $schlagwoerter,
'abstract' => $abstract,
'abstract_en' => $abstract_en
));
// update projektarbeit cols
$this->ProjektarbeitModel->updateProjektarbeit($projektarbeit_id,$sprache,$abstract,$abstract_en
,$schlagwoerter, $schlagwoerter_en, $seitenanzahl);
$this->logLib->logInfoDB(array('zusatzdatenEditMitarbeiter', array(
'updatevon' => getAuthUID(),
@@ -1589,7 +1206,7 @@ class Abgabe extends FHCAPI_Controller
};
Events::trigger('projektarbeit_is_current', $projektarbeit_id, $returnFunc);
if(!$projektarbeitIsCurrent) {
$this->terminateWithError($this->p->t('abgabetool','c4fehlerAktualitaetProjektarbeitv2'), 'general');
$this->terminateWithError($this->p->t('abgabetool','c4fehlerAktualitaetProjektarbeit'), 'general');
}
// Link to Abgabetool
@@ -1794,13 +1411,7 @@ class Abgabe extends FHCAPI_Controller
$data = getData($res)[0];
if($data->note !== NULL) {
// hardcode this error msg cause phrasen arent reliable and people keep bugging why the cant edit old entries they definitely shouldnt update
$message = $this->p->t('abgabetool','c4fehlerAktualitaetProjektarbeitv2');
if(strpos($message, "<<") === 0) { // phrase could not be loaded
$this->terminateWithError('Die Projektarbeit wurde bereits benotet, Sie dürfen deshalb keine weiteren Termine anlegen oder bearbeiten.', 'general');
} else {
$this->terminateWithError($message);
}
$this->terminateWithError($this->p->t('abgabetool','c4fehlerAktualitaetProjektarbeit'), 'general');
}
}
@@ -41,7 +41,9 @@ class LvPlan extends FHCAPI_Controller
'getLehreinheitStudiensemester' => self::PERM_LOGGED,
'studiensemesterDateInterval' => self::PERM_LOGGED,
'getLvPlanForStudiensemester' => self::PERM_LOGGED,
'getLv' => self::PERM_LOGGED
'getLv' => self::PERM_LOGGED,
'getLeEvents' => self::PERM_LOGGED,
'getLvEvents' => self::PERM_LOGGED,
]);
$this->load->library('LogLib');
@@ -54,6 +56,12 @@ class LvPlan extends FHCAPI_Controller
));
$this->load->library('form_validation');
$this->load->library('PhrasesLib');
$this->loadPhrases(
array(
'ui'
)
);
}
//------------------------------------------------------------------------------------------------------------------
@@ -145,6 +153,38 @@ class LvPlan extends FHCAPI_Controller
));
}
public function getLeEvents($le_id = null, $start_date = null, $end_date = null, $stundenplan = 'stundenplandev')
{
if (is_null($le_id) || is_null($start_date) || is_null($end_date))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
if ($stundenplan !== 'stundenplandev' && $stundenplan !== 'stundenplan')
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$this->load->library('StundenplanLib');
$result = $this->stundenplanlib->getEventsByLE($le_id, $start_date, $end_date, $stundenplan);
$lvplanEvents = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($lvplanEvents);
}
public function getLvEvents($lv_id = null, $start_date = null, $end_date = null, $stundenplan = 'stundenplandev')
{
if (is_null($lv_id) || is_null($start_date) || is_null($end_date))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
if ($stundenplan !== 'stundenplandev' && $stundenplan !== 'stundenplan')
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$this->load->library('StundenplanLib');
$result = $this->stundenplanlib->getEventsByLV($lv_id, $start_date, $end_date, $stundenplan);
$this->terminateWithSuccess(hasData($result) ? getData($result) : []);
}
//TODO: delete this function if we don't use the old calendar export endpoints anymore
public function studiensemesterDateInterval($date){
$this->load->model('organisation/Studiensemester_model','StudiensemesterModel');
@@ -0,0 +1,929 @@
<?php
/**
* Copyright (C) 2024 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
use CI3_Events as Events;
class Noten extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getStudentenNoten' => array('lehre/benotungstool:rw'),
'getNoten' => array('lehre/benotungstool:rw'),
'saveStudentenNoten' => array('lehre/benotungstool:rw'),
'getNotenvorschlagStudent' => array('lehre/benotungstool:rw'),
'saveNotenvorschlag' => array('lehre/benotungstool:rw'),
'saveStudentPruefung' => array('lehre/benotungstool:rw'),
'createPruefungen' => array('lehre/benotungstool:rw'),
'saveNotenvorschlagBulk' => array('lehre/benotungstool:rw'),
'savePruefungenBulk' => array('lehre/benotungstool:rw')
]);
$this->load->library('AuthLib', null, 'AuthLib');
$this->load->library('PhrasesLib');
// Loads phrases system
$this->loadPhrases([
'global',
'person',
'benotungstool',
'lehre',
'ui'
]);
require_once(FHCPATH . 'include/mobilitaet.class.php');
$this->load->model('education/LePruefung_model', 'LePruefungModel');
$this->load->model('education/Lvgesamtnote_model', 'LvgesamtnoteModel');
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->load->model('person/Person_model', 'PersonModel');
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
$this->load->model('crm/Student_model', 'StudentModel');
$this->load->helper('hlp_sancho_helper');
}
/**
* GET METHOD
* expects 'lv_id', 'sem_kurzbz'
* returns List of all Students of given lehrveranstaltung and semester and fetches their grades.
* Loads LvGesamtnote aswell as Teilnoten from externalSources via getExternalGrades Event.
* Calculates the Notenvorschlag for every student based on averaging their Teilnoten.
* Finally also fetches all Prüfungen for every student which are linked to lva and semester.
*/
public function getStudentenNoten() {
$lv_id = $this->input->get("lv_id",TRUE);
$sem_kurzbz = $this->input->get("sem_kurzbz",TRUE);
if (!isset($lv_id) || isEmptyString($lv_id)
|| !isset($sem_kurzbz) || isEmptyString($sem_kurzbz))
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
// get studenten for lva & sem with zeugnisnote if available
$studenten = $this->LehrveranstaltungModel->getStudentsByLv($sem_kurzbz, $lv_id);
$studentenData = $this->getDataOrTerminateWithError($studenten);
$func = function ($value) {
return $value->uid;
};
$grades = array();
$student_uids = array_map($func, $studentenData);
foreach($student_uids as $uid) {
$grades[$uid]['grades'] = [];
$res = $this->StudentModel->load([$uid]);
if(!isError($res) && hasData($res)) $student = getData($res)[0];
$prestudent_id = $student->prestudent_id;
// TODO: last class to get rid of but this one is complicated
$mobility = new mobilitaet();
$mobility->loadPrestudent($prestudent_id);
$output = $mobility->result;
$eintrag = '';
foreach ($output as $k)
{
if(($k->mobilitaetstyp_kurzbz == 'GS') && ($k->studiensemester_kurzbz == $sem_kurzbz))
$eintrag = ' (d.d.)';
}
$grades[$uid]['mobility'] = $eintrag;
$result = $this->LvgesamtnoteModel->getLvGesamtNoten($lv_id, $uid, $sem_kurzbz);
if(!isError($result) && hasData($result)) {
$lvgesamtnote = getData($result)[0];
$grades[$uid]['note_lv'] = $lvgesamtnote->note;
$grades[$uid]['freigabedatum'] = $lvgesamtnote->freigabedatum;
$grades[$uid]['benotungsdatum'] = $lvgesamtnote->benotungsdatum;
$grades[$uid]['punkte_lv'] = $lvgesamtnote->punkte;
} else {
$grades[$uid]['note_lv'] = null;
$grades[$uid]['freigabedatum'] = null;
$grades[$uid]['benotungsdatum'] = null;
$grades[$uid]['punkte_lv'] = null;
}
}
// send $grades reference to moodle addon
Events::trigger(
'getExternalGrades',
function & () use (&$grades)
{
return $grades;
},
[
'lvid' => $lv_id,
'stsem' => $sem_kurzbz
]
);
// calculate notenvorschläge from teilnoten
foreach($studentenData as $student) {
$g = $grades[$student->uid]['grades'];
$note_lv = $grades[$student->uid]['note_lv'];
// overwrite any calculation with lv note once available
if(!is_null($note_lv)) {
$student->note_vorschlag = $note_lv;
} else if(count($g) > 0) {
$notensumme = 0;
$notensumme_gewichtet = 0;
$gewichtsumme = 0;
$punktesumme = 0;
$punktesumme_gewichtet = 0;
$anzahlnoten = 0;
foreach($g as $teilnote) {
if (is_numeric($teilnote['grade']) || (is_null($teilnote['grade']) && is_numeric($teilnote['points'])))
{
$notensumme += $teilnote['grade'];
$punktesumme += $teilnote['points'];
$notensumme_gewichtet += $teilnote['grade'] * $teilnote['weight'];
$punktesumme_gewichtet += $teilnote['points'] * $teilnote['weight'];
$gewichtsumme += $teilnote['weight'];
$anzahlnoten += 1;
}
}
// TODO: develop the punkte feature with models
// calculate grades points from notenschlüssel
if (CIS_GESAMTNOTE_PUNKTE)
{
if (defined('CIS_GESAMTNOTE_GEWICHTUNG') && CIS_GESAMTNOTE_GEWICHTUNG)
{
// Lehreinheitsgewichtung
$punkte_vorschlag = round($punktesumme_gewichtet / $gewichtsumme, 2);
$notenschluessel = new notenschluessel();
$note_vorschlag = $notenschluessel->getNote($punkte_vorschlag, $lv_id, $sem_kurzbz);
}
else
{
$punkte_vorschlag = round($punktesumme / $anzahlnoten, 2);
$notenschluessel = new notenschluessel();
$note_vorschlag = $notenschluessel->getNote($punkte_vorschlag, $lv_id, $sem_kurzbz);
}
}
else
{
if (defined('CIS_GESAMTNOTE_GEWICHTUNG') && CIS_GESAMTNOTE_GEWICHTUNG)
{
$note_vorschlag = round($notensumme_gewichtet / $gewichtsumme);
}
else
{
$note_vorschlag = round($notensumme / $anzahlnoten);
}
}
$student->note_vorschlag = $note_vorschlag;
}
}
// get all prüfungen with noten held in that semester in that lva
$pruefungen = $this->LePruefungModel->getPruefungenByLvStudiensemester($lv_id, $sem_kurzbz);
$pruefungenData = getData($pruefungen);
$this->terminateWithSuccess(array($studentenData, $pruefungenData, DOMAIN, $grades));
}
/**
* GET METHOD
* returns List of all available & active NotenOptions
*/
public function getNoten() {
$this->load->model('education/Note_model', 'NoteModel');
$result = $this->NoteModel->getAllActive();
$noten = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($noten);
}
/**
* POST METHOD
* expects 'lv_id', 'sem_kurzbz', 'password', 'noten'
* Notenfreigabe method which checks the users password as a security measure.
* Tries to load Lehrveranstaltung, Studiengang and Person via Model in order to validate the coherency of input parameters
* lv_id & sem_kurzbz in relation to the noten array delivered.
* Updates the LvGesamtnote note, aswell as freigabedatum, which is key in the logic of the freigegeben/offen/changed notenStatus
* Along this process builds a html table to be placed in a confirmation email (uid only and full variant depending on config)
* which is being sent to the Lektor, aswell as the assigned Assistenz.
*/
public function saveStudentenNoten() {
$result = $this->getPostJSON();
if(!property_exists($result, 'sem_kurzbz') || !property_exists($result, 'lv_id') ||
!property_exists($result, 'password') || !property_exists($result, 'noten')) {
$this->terminateWithError($this->p->t('global', 'missingParameters'), 'general');
}
if(!$this->AuthLib->checkUserAuthByUsernamePassword(getAuthUID(), $result->password)->retval) {
$this->terminateWithError($this->p->t('global', 'wrongPassword'), 'general');
}
$lv_id = $result->lv_id;
$sem_kurzbz = $result->sem_kurzbz;
$ret = [];
$res = $this->LehrveranstaltungModel->load($lv_id);
if(isError($res) || !hasData($res)) {
$this->terminateWithError($this->p->t('benotungstool', 'noValidLvFoundForId', [$lv_id]));
}
$lv = getData($res)[0];
$studiengang_kz = $lv->studiengang_kz;
$res = $this->StudiengangModel->load($studiengang_kz);
if(isError($res) || !hasData($res)) {
$this->terminateWithError($this->p->t('benotungstool', 'noValidStudiengangFoundForId', [$studiengang_kz]));
}
$sg = getData($res)[0];
$lvaFullName = $sg->kurzbzlang . ' ' . $lv->semester . '.Semester
' . $lv->bezeichnung . " - " .$lv->lehrform_kurzbz. " " . $lv->orgform_kurzbz . " - " . $sem_kurzbz;
$emails = explode(', ', $sg->email);
$res = $this->PersonModel->load(getAuthPersonId());
if(isError($res) || !hasData($res)) {
$this->terminateWithError($this->p->t('benotungstool', 'noValidPersonFoundForId', [getAuthPersonId()]));
}
$pers = getData($res)[0];
$lektorFullName = $pers->anrede.' '.$pers->vorname.' '.$pers->nachname; //.' ('.$pers->kurzbz.')';
$res = $this->StudienplanModel->getStudienplanByLvaSemKurzbz($lv_id, $sem_kurzbz);
$data = getData($res);
$studienplan_bezeichnung = '';
foreach ($data as $row) {
$studienplan_bezeichnung .= $row->bezeichnung . ' ';
}
$betreff = $this->p->t('benotungstool','notenfreigabe').' ' . $lv->bezeichnung . ' ' . $lv->orgform_kurzbz . ' - ' . $studienplan_bezeichnung;
$studlist = "<table border='1'><tr>";
if (defined('CIS_GESAMTNOTE_FREIGABEMAIL_NOTE') && CIS_GESAMTNOTE_FREIGABEMAIL_NOTE) {
$studlist .= "<td><b>" . $this->p->t('person','personenkennzeichen') . "</b></td>\n
<td><b>" . $this->p->t('lehre','studiengang') . "</b></td>\n
<td><b>" . $this->p->t('benotungstool','c4nachname') . "</b></td>\n
<td><b>" . $this->p->t('benotungstool','c4vorname') . "</b></td>\n";
$studlist .= "<td><b>" . $this->p->t('benotungstool','c4grade') . "</b></td>\n";
$studlist .= "<td><b>" . $this->p->t('ui','bearbeitetVon') . "</b></td></tr>\n";
} else {
$studlist .= "<td><b>" . $this->p->t('person','uid') . "</b></td></tr>\n";
}
foreach($result->noten as $note) {
$resultLVGes = $this->LvgesamtnoteModel->getLvGesamtNoten($lv_id, $note->uid, $sem_kurzbz);
if (!isError($resultLVGes) && hasData($resultLVGes))
{
$lvgesamtnote = getData($resultLVGes)[0];
if ($lvgesamtnote->benotungsdatum > $lvgesamtnote->freigabedatum)
{
$id = $this->LvgesamtnoteModel->update(
[$lvgesamtnote->student_uid, $lvgesamtnote->studiensemester_kurzbz, $lvgesamtnote->lehrveranstaltung_id],
array(
'note' => $note->note,
'freigabevon_uid' => getAuthUID(),
'freigabedatum' => date("Y-m-d H:i:s"),
'updateamum' => date("Y-m-d H:i:s"),
'updatevon' => getAuthUID()
)
);
if($id) {
$res = $this->LvgesamtnoteModel->load($id->retval);
if(hasData($res)) {
$lvgesamtnote = getData($res)[0];
$ret[] = array('uid' => $note->uid, 'freigabedatum' => $lvgesamtnote->freigabedatum, 'benotungsdatum' => $lvgesamtnote->benotungsdatum);
}
}
if (defined('CIS_GESAMTNOTE_FREIGABEMAIL_NOTE') && CIS_GESAMTNOTE_FREIGABEMAIL_NOTE)
{
$studlist .= "<tr><td>" . trim($note->matrikelnr) . "</td>";
$studlist .= "<td>" . trim($note->kuerzel) . "</td>";
$studlist .= "<td>" . trim($note->nachname) . "</td>";
$studlist .= "<td>" . trim($note->vorname) . "</td>";
// TODO: if defined(CIS_PUNKTE) ...
$studlist .= "<td>" .$note->noteBezeichnung. "</td>";
$studlist .= "<td>" . $lvgesamtnote->mitarbeiter_uid;
if ($lvgesamtnote->updatevon != '')
$studlist .= " (" . $lvgesamtnote->updatevon . ")";
$studlist .= "</td></tr>";
} else {
$studlist .= "<tr><td>" . trim($note->uid) . "</td></tr>\n";
}
}
}
}
$studlist .= "</table>";
// always send the mail, config toggles data contents
$this->sendEmail($lektorFullName, $lvaFullName, count($result->noten), $emails, $studlist, $betreff);
$this->terminateWithSuccess($ret);
}
private function sendEmail($lektorFullName, $lvaFullName, $notenCount, $emailAdressen, $studlist, $betreff)
{
$emailAdressen[] = getAuthUID() . "@" . DOMAIN; // also send mail to lektors own adress
$adressen = implode(";", $emailAdressen);
foreach ($emailAdressen as $email)
{
// Prepare mail content
$body_fields = array(
'lektor' => $lektorFullName,
'lvaname' => $lvaFullName,
'studlist' => $studlist,
'neuenotencount' => $notenCount,
'adressen' => $adressen
);
// Send mail
sendSanchoMail(
'Notenfreigabe',
$body_fields,
$email,
$betreff
);
}
}
/**
* GET METHOD
* should return Notenvorschlag for single Students, not yet implemented since it is not needed anywhere right now,
* but could be useful later on.
*/
public function getNotenvorschlagStudent() {
// TODO: Notenvorschlag laden allgemeiner Endpunkt, der im Backend mit Logik (z.B. Moodle) angepasst werden kann.
$this->terminateWithSuccess();
}
/**
* POST METHOD
* expects 'datum', 'lva_id', 'student_uid', 'note'
* Inserts or updates a pruefung for lva & student_uid at given datum (YYYY-MM-DD). When creating a new
* Pruefung, sets the provided (Prüfungs-) Note.
* Updates the LvGesamtnote of student.
* Can return 1 or 2 Prüfungen, since the original grade before the first prüfung is being saved as "Termin1" when
* a "Termin2" is being created.
*/
public function saveStudentPruefung() { // einzelne pruefung speichern
$result = $this->getPostJSON();
if(!property_exists($result, 'datum') || !property_exists($result, 'lva_id') ||
!property_exists($result, 'student_uid') || !property_exists($result, 'note')) {
$this->terminateWithError($this->p->t('global', 'missingParameters'), 'general');
}
$student_uid = $result->student_uid;
$note = $result->note;
$punkte = null;
$datum = $result->datum;
$lva_id = $result->lva_id;
$lehreinheit_id = $result->lehreinheit_id;
$stsem = $result->sem_kurzbz;
$typ = $result->typ;
$jetzt = date("Y-m-d H:i:s");
// nachpruefungeintragen.php script calls query on campus.student_lehrveranstaltung to find a
// lehreinheit_id for lva_id -> lehreinheit should be determined prior to that in new benotungstool
// by retrieving it from students row in campus.vw_student_lehrveranstaltung earlier on
// $lehreinheit_id = getLehreinheit($db, $lvid, $student_uid, $stsem);
// $lehreinheit_id = $result->lehreinheit_id;
$punkte = null;
// if($punkte!='')
// {
// // Bei Punkteeingabe wird die Note nochmals geprueft und ggf korrigiert
// $notenschluessel = new notenschluessel();
// $note_pruef = $notenschluessel->getNote($punkte, $lva_id, $stsem);
// if($note_pruef!=$note)
// {
// $note = $note_pruef;
// $note_dirty=true;
// }
// }
// TODO: more sophisticated empty check
if($note=='')
$note = 9;
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$res = $this->LehrveranstaltungModel->load($lva_id);
if(isError($res) || !hasData($res)) {
$this->terminateWithError('Keine gültige Lehrveranstaltung gefunden für ID: '.$lva_id);
}
$studiengang_kz = getData($res)[0]->studiengang_kz;
$res = $this->StudiengangModel->load($studiengang_kz);
if(isError($res) || !hasData($res)) {
$this->terminateWithError('Kein gültiger Studiengang gefunden für ID: '.$studiengang_kz);
}
$pruefungenChanged = $this->savePruefungstermin($typ, $student_uid, $lva_id, $stsem, $lehreinheit_id, $note, $punkte, $datum);
//Gesamtnote updaten
$result = $this->LvgesamtnoteModel->getLvGesamtNoten($lva_id, $student_uid, $stsem);
if(!isError($result) && !hasData($result)) {
$id = $this->LvgesamtnoteModel->insert(
array(
'student_uid' => $student_uid,
'lehrveranstaltung_id' => $lva_id,
'studiensemester_kurzbz' => $stsem,
'note' => $note,
'punkte' => $punkte,
'mitarbeiter_uid' => getAuthUID(),
'benotungsdatum' => $jetzt,
'freigabedatum' => null,
'freigabevon_uid' => null,
'bemerkung' => null,
'updateamum' => null,
'updatevon' => null,
'insertamum' => $jetzt,
'insertvon' => getAuthUID()
)
);
if($id) {
$res = $this->LvgesamtnoteModel->load($id->retval);
if(hasData($res)) $lvgesamtnote = getData($res)[0];
}
}
else if(!isError($result) && hasData($result))
{
$lvgesamtnote = getData($result)[0];
$id = $this->LvgesamtnoteModel->update(
[$lvgesamtnote->student_uid, $lvgesamtnote->studiensemester_kurzbz, $lvgesamtnote->lehrveranstaltung_id],
array(
'note' => $note,
'punkte' => $punkte,
'benotungsdatum' => $jetzt,
'updateamum' => $jetzt,
'updatevon' => getAuthUID()
)
);
if($id) {
$res = $this->LvgesamtnoteModel->load($id->retval);
if(hasData($res)) $lvgesamtnote = getData($res)[0];
}
}
$savedPruefung = $pruefungenChanged['savedPruefung'] ?? null;
$extraPruefung = $pruefungenChanged['extraPruefung'] ?? null;
$savedPruefungData = count($savedPruefung) > 0 ? $savedPruefung[0] : null;
$extraPruefungData = count($extraPruefung) > 0 ? $extraPruefung[0] : null;
$this->terminateWithSuccess(array($savedPruefungData, $lvgesamtnote, $extraPruefungData));
}
/**
* private helper method to update/insert pruefungstermine
*/
private function savePruefungstermin($typ, $student_uid, $lva_id, $stsem, $lehreinheit_id, $note, $punkte, $datum)
{
$status = [];
// send $grades reference to moodle addon
Events::trigger(
'getEntschuldigungsStatusForStudentOnDate',
function & () use (&$status)
{
return $status;
},
[
'student_uid' => $student_uid,
'datum' => $datum
]
);
if(count($status) > 0 && $status[0] == true) {
$note = 17; //entschuldigt
}
$jetzt = date("Y-m-d H:i:s");
$pruefungenChanged = [];
$this->load->model('education/Lvgesamtnote_model', 'LvgesamtnoteModel');
if($typ == "Termin2" && defined('CIS_GESAMTNOTE_PRUEFUNG_TERMIN2') && CIS_GESAMTNOTE_PRUEFUNG_TERMIN2)
{
// Wenn eine Nachprüfung angelegt wird, wird zuerst eine Pruefung mit 1. Termin angelegt welche für die ursprüngliche Note
// vor den Prüfungsantritten zählt
$result1 = $this->LePruefungModel->getPruefungenByUidTypLvStudiensemester($student_uid, "Termin1", $lva_id, $stsem);
// if there is a termin 1 entry already do nothing
if(!isError($result1) && hasData($result1)) {
} else if(!isError($result1) && !hasData($result1)) {
// new entry termin1
$resultLV = $this->LvgesamtnoteModel->getLvGesamtNoten($lva_id, $student_uid, $stsem);
// update Termin1 note
if (hasData($resultLV))
{
$lvgesamtnote = getData($resultLV)[0];
$pr_note = $lvgesamtnote->note;
$pr_punkte = $lvgesamtnote->punkte;
$benotungsdatum = $lvgesamtnote->benotungsdatum;
}
else if(!hasData($resultLV))// set Termin1 note to "noch nicht eingetragen"
{
$pr_note = 9;
$pr_punkte = null;
$benotungsdatum = $jetzt;
}
$id = $this->LePruefungModel->insert(
array(
'lehreinheit_id' => $lehreinheit_id,
'student_uid' => $student_uid,
'mitarbeiter_uid' => getAuthUID(),
'note' => $pr_note,
// 'punkte' => $pr_punkte,
'pruefungstyp_kurzbz' => "Termin1",
'datum' => $benotungsdatum,
'anmerkung' => "",
'insertamum' => $jetzt,
'insertvon' => getAuthUID(),
'updateamum' => null,
'updatevon' => null,
'ext_id' => null
)
);
if($id) {
$res = $this->LePruefungModel->load($id->retval);
if(hasData($res)) $pruefungenChanged['extraPruefung'] = getData($res);
}
}
// Die Pruefung wird als Termin2 eingetragen
$result2 = $this->LePruefungModel->getPruefungenByUidTypLvStudiensemester($student_uid, "Termin2", $lva_id, $stsem);
// if there is a termin 2 entry already update it
if(!isError($result2) && hasData($result2)) {
// update
$termin2 = getData($result2)[0];
$id = $this->LePruefungModel->update(
$termin2->pruefung_id,
array(
'updateamum' => $jetzt,
'updatevon' => getAuthUID(),
'note' => $note,
// 'punkte' => $punkte,
'datum' => $datum,
'anmerkung' => ""
)
);
if($id) {
$res = $this->LePruefungModel->load($id->retval);
if(hasData($res)) $pruefungenChanged['savedPruefung'] = getData($res);
}
} else if(!isError($result2) && !hasData($result2)) {
// new entry termin 2
$id = $this->LePruefungModel->insert(
array(
'lehreinheit_id' => $lehreinheit_id,
'student_uid' => $student_uid,
'mitarbeiter_uid' => getAuthUID(),
'note' => $note,
// 'punkte' => null,//$punkte,
'pruefungstyp_kurzbz' => $typ,
'datum' => $datum,
'anmerkung' => "",
'insertamum' => $jetzt,
'insertvon' => getAuthUID(),
'updateamum' => null,
'updatevon' => null,
'ext_id' => null
)
);
if($id) {
$res = $this->LePruefungModel->load($id->retval);
if(hasData($res)) $pruefungenChanged['savedPruefung'] = getData($res);
}
}
} else if($typ == "Termin3" && defined('CIS_GESAMTNOTE_PRUEFUNG_TERMIN3') && CIS_GESAMTNOTE_PRUEFUNG_TERMIN3)
{
$result3 = $this->LePruefungModel->getPruefungenByUidTypLvStudiensemester($student_uid, "Termin3", $lva_id, $stsem);
if(!isError($result3) && hasData($result3)) {
// update
$termin3 = getData($result3)[0];
$id = $this->LePruefungModel->update(
$termin3->pruefung_id,
array(
'updateamum' => $jetzt,
'updatevon' => getAuthUID(),
'note' => $note,
// 'punkte' => $punkte,
'datum' => $datum,
'anmerkung' => ""
)
);
if($id) {
$res = $this->LePruefungModel->load($id->retval);
if(hasData($res)) $pruefungenChanged['savedPruefung'] = getData($res);
}
} else if(!isError($result3) && !hasData($result3)) {
// insert new termin3
$id = $this->LePruefungModel->insert(
array(
'lehreinheit_id' => $lehreinheit_id,
'student_uid' => $student_uid,
'mitarbeiter_uid' => getAuthUID(),
'note' => $note,
// 'punkte' => null,//$punkte,
'pruefungstyp_kurzbz' => $typ,
'datum' => $datum,
'anmerkung' => "",
'insertamum' => $jetzt,
'insertvon' => getAuthUID(),
'updateamum' => null,
'updatevon' => null,
'ext_id' => null
)
);
if($id) {
$res = $this->LePruefungModel->load($id->retval);
if(hasData($res)) $pruefungenChanged['savedPruefung'] = getData($res);
}
}
} else {
$this->terminateWithError($this->p->t('benotungstool', 'wrongPruefungType', [$student_uid, $typ]), 'general');
}
return $pruefungenChanged;
}
/**
* POST METHOD
* expects 'sem_kurzbz', 'lv_id', 'student_uid', 'note'
* Method that sets lv_note of student in lva and semester from provided Points/Grade Selection.
* Updates the note & benotungsdatum, which is key in the noten state offen/freigegeben/changed
*/
public function saveNotenvorschlag() {
$result = $this->getPostJSON();
if(!property_exists($result, 'lv_id') || !property_exists($result, 'sem_kurzbz') ||
!property_exists($result, 'student_uid') || !property_exists($result, 'note')) {
$this->terminateWithError($this->p->t('global', 'missingParameters'), 'general');
}
$lv_id = $result->lv_id;
$student_uid = $result->student_uid;
$sem_kurzbz = $result->sem_kurzbz;
$note = $result->note;
$result = $this->LvgesamtnoteModel->getLvGesamtNoten($lv_id, $student_uid, $sem_kurzbz);
if(!isError($result) && hasData($result)) {
$lvgesamtnote = getData($result)[0];
$id = $this->LvgesamtnoteModel->update(
[$lvgesamtnote->student_uid, $lvgesamtnote->studiensemester_kurzbz, $lvgesamtnote->lehrveranstaltung_id],
array(
'note' => $note,
'punkte' => null,
'benotungsdatum' => date("Y-m-d H:i:s"),
'updateamum' => date("Y-m-d H:i:s"),
'updatevon' => getAuthUID()
)
);
if($id) {
$res = $this->LvgesamtnoteModel->load($id->retval);
if(hasData($res)) $lvgesamtnote = getData($res)[0];
}
} else if(!isError($result) && !hasData($result)) {
$id = $this->LvgesamtnoteModel->insert(
array(
'student_uid' => $student_uid,
'lehrveranstaltung_id' => $lv_id,
'studiensemester_kurzbz' => $sem_kurzbz,
'note' => $note,
'punkte' => null,
'mitarbeiter_uid' => getAuthUID(),
'benotungsdatum' => date("Y-m-d H:i:s"),
'freigabedatum' => null,
'freigabevon_uid' => null,
'bemerkung' => null,
'updateamum' => null,
'updatevon' => null,
'insertamum' => date("Y-m-d H:i:s"),
'insertvon' => getAuthUID()
)
);
if($id) {
$res = $this->LvgesamtnoteModel->load($id->retval);
if(hasData($res)) $lvgesamtnote = getData($res)[0];
}
}
$this->terminateWithSuccess(array($lvgesamtnote));
}
/**
* POST METHOD
* expects 'sem_kurzbz', 'lv_id', 'noten'
* Bulk variant of saveNotenvorschlag, used when importing grades from csv.
*/
public function saveNotenvorschlagBulk() {
$result = $this->getPostJSON();
if(!property_exists($result, 'lv_id') || !property_exists($result, 'sem_kurzbz') ||
!property_exists($result, 'noten')) {
$this->terminateWithError($this->p->t('global', 'missingParameters'), 'general');
}
$lv_id = $result->lv_id;
$sem_kurzbz = $result->sem_kurzbz;
$noten = $result->noten;
$retLvNoten = [];
foreach($noten as $note)
{
$result = $this->LvgesamtnoteModel->getLvGesamtNoten($lv_id, $note->uid, $sem_kurzbz);
if(!isError($result) && hasData($result)) {
$lvgesamtnote = getData($result)[0];
$id = $this->LvgesamtnoteModel->update(
[$lvgesamtnote->student_uid, $lvgesamtnote->studiensemester_kurzbz, $lvgesamtnote->lehrveranstaltung_id],
array(
'note' => trim($note->note),
'punkte' => null,
'benotungsdatum' => date("Y-m-d H:i:s"),
'updateamum' => date("Y-m-d H:i:s"),
'updatevon' => getAuthUID()
)
);
if($id) {
$res = $this->LvgesamtnoteModel->load($id->retval);
if(hasData($res)) $lvgesamtnote = getData($res)[0];
}
} else if(!isError($result) && !hasData($result)) {
$id = $this->LvgesamtnoteModel->insert(
array(
'student_uid' => $note->uid,
'lehrveranstaltung_id' => $lv_id,
'studiensemester_kurzbz' => $sem_kurzbz,
'note' => trim($note->note),
'punkte' => null,
'mitarbeiter_uid' => getAuthUID(),
'benotungsdatum' => date("Y-m-d H:i:s"),
'freigabedatum' => null,
'freigabevon_uid' => null,
'bemerkung' => null,
'updateamum' => null,
'updatevon' => null,
'insertamum' => date("Y-m-d H:i:s"),
'insertvon' => getAuthUID()
)
);
if($id) {
$res = $this->LvgesamtnoteModel->load($id->retval);
if(hasData($res)) $lvgesamtnote = getData($res)[0];
}
}
$retLvNoten[] = $lvgesamtnote;
}
$this->terminateWithSuccess($retLvNoten);
}
/**
* POST METHOD
* expects 'uids', 'datum'
* Bulk variant of saveStudentPruefung, used when creating a new Prüfung for several students. Always sets note to
* "noch nicht eingetragen" for the created Prüfung.
*/
public function createPruefungen() {
$result = $this->getPostJSON();
if(!property_exists($result, 'uids') || !property_exists($result, 'datum')) {
$this->terminateWithError($this->p->t('global', 'missingParameters'), 'general');
}
$uids = $result->uids;
$datum = $result->datum;
$lva_id = $result->lva_id;
$stsem = $result->sem_kurzbz;
$ret = [];
foreach ($uids as $student) {
$student_uid = $student->uid;
$typ = $student->typ;
$note = 9; //$result->note; // TODO: parameterize for import maybe
$punkte = ''; // TODO: check punkte feature
$lehreinheit_id = $student->lehreinheit_id;
$ret[$student->uid] = $this->savePruefungstermin($typ, $student_uid, $lva_id, $stsem, $lehreinheit_id, $note, $punkte, $datum);
}
$this->terminateWithSuccess($ret);
}
/**
* POST METHOD
* expects 'lv_id', 'sem_kurzbz', 'pruefungen'
* Bulk variant of saveStudentPruefung, used when importing pruefungsdata from csv with available noten.
*/
public function savePruefungenBulk() {
$result = $this->getPostJSON();
if(!property_exists($result, 'lv_id') || !property_exists($result, 'sem_kurzbz') ||
!property_exists($result, 'pruefungen')) {
$this->terminateWithError($this->p->t('global', 'missingParameters'), 'general');
}
$lv_id = $result->lv_id;
$sem_kurzbz = $result->sem_kurzbz;
$pruefungen = $result->pruefungen;
$ret = [];
foreach ($pruefungen as $pruefung) {
$student_uid = $pruefung->uid;
$typ = $pruefung->typ;
$note = $pruefung->note; // TODO: parameterize for import maybe
$datum = $pruefung->datum;
$punkte = ''; // TODO: check punkte feature
$lehreinheit_id = $pruefung->lehreinheit_id;
$ret[$student_uid] = $this->savePruefungstermin($typ, $student_uid, $lv_id, $sem_kurzbz, $lehreinheit_id, $note, $punkte, $datum);
}
$this->terminateWithSuccess($ret);
}
}
@@ -0,0 +1,67 @@
<?php
/**
* Copyright (C) 2024 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Studiensemester extends FHCAPI_Controller
{
private $_ci;
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getStudiensemester'=> self::PERM_LOGGED,
]);
$this->_ci =& get_instance();
$this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
* GET METHOD
* returns List of all studiensemester as well as current one
*/
public function getStudiensemester()
{
$this->_ci->StudiensemesterModel->addOrder("start", "DESC");
$result = $this->_ci->StudiensemesterModel->load();
$studiensemester = getData($result);
$result = $this->_ci->StudiensemesterModel->getAkt();
$aktuell = getData($result);
$this->terminateWithSuccess(array($studiensemester, $aktuell));
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
}
@@ -0,0 +1,86 @@
<?php
if (!defined('BASEPATH'))
exit('No direct script access allowed');
class Config extends FHCAPI_Controller
{
private $_ci;
private $_uid;
public function __construct()
{
parent::__construct([
'get' => ['admin:r', 'assistenz:r'],
'set' => ['admin:r', 'assistenz:r'],
]);
$this->_ci = &get_instance();
$this->_setAuthUID();
$this->loadPhrases([
'lehre'
]);
$this->_ci->load->library('VariableLib', ['uid' => $this->_uid]);
$this->_ci->load->library('PermissionLib');
}
public function get()
{
if (!($this->permissionlib->isBerechtigt('basis/tempus')) && !($this->permissionlib->isBerechtigt('lv-plan')))
$this->terminateWithSuccess([]);
$ignore_kollision = $this->_ci->variablelib->getVar('ignore_kollision');
$ignore_zeitsperre = $this->_ci->variablelib->getVar('ignore_zeitsperre');
$ignore_reservierung = $this->_ci->variablelib->getVar('ignore_reservierung');
$config['ignore_kollision'] = [
"type" => "checkbox",
"label" => 'ignore_kollision',
"value" => $ignore_kollision,
];
$config['ignore_zeitsperre'] = [
"type" => "checkbox",
"label" => 'ignore_zeitsperre',
"value" => $ignore_zeitsperre,
];
$config['ignore_reservierung'] = [
"type" => "checkbox",
"label" => 'ignore_reservierung',
"value" => $ignore_reservierung,
];
$this->terminateWithSuccess($config);
}
public function set()
{
if (!($this->permissionlib->isBerechtigt('basis/tempus')) && !($this->permissionlib->isBerechtigt('lv-plan')))
$this->terminateWithSuccess([]);
$this->load->model('system/Variable_model', 'VariableModel');
foreach (['ignore_kollision','ignore_zeitsperre','ignore_reservierung'] as $variable)
{
if ($this->_ci->input->post($variable) !== null)
{
$this->VariableModel->update(array('uid' => $this->_uid, 'name' => $variable), array('wert' => $this->input->post($variable)));
}
}
$this->terminateWithSuccess();
}
private function _setAuthUID()
{
$this->_uid = getAuthUID();
if (!$this->_uid)
show_error('User authentification failed');
}
}
@@ -15,6 +15,7 @@ class Gruppe extends FHCAPI_Controller
'getBenutzerSearch' => ['admin:r', 'assistenz:r'],
'getAllSearch' => ['admin:r', 'assistenz:r'],
'getByLehreinheit' => ['admin:r', 'assistenz:r'],
'getGruppe' => ['admin:r', 'assistenz:r'],
]);
$this->_ci = &get_instance();
@@ -67,7 +68,7 @@ class Gruppe extends FHCAPI_Controller
$this->checkPermission($lehreinheit_id);
$result = $this->_ci->LehreinheitgruppeModel->addGroup($lehreinheit_id, $gid, !($lehrverband === 'false'));
$result = $this->_ci->LehreinheitgruppeModel->addGroup($lehreinheit_id, $gid, $lehrverband === true || $lehrverband === 'true');
if (isError($result))
$this->terminateWithError(getError($result));
@@ -75,6 +76,71 @@ class Gruppe extends FHCAPI_Controller
$this->terminateWithSuccess($result);
}
public function getGruppe()
{
$lehrverband = $this->input->post('lehrverband');
$gruppen_result = array();
if ($lehrverband === false)
{
$gruppen_result = $this->_ci->GruppeModel->loadWhere(array(
'studiengang_kz' => $this->input->post('stg_kz'),
'gruppe_kurzbz' => $this->input->post('gruppe_kurzbz'),
'aktiv' => true
));
}
else if ($lehrverband === true)
{
if (!isEmptyString($this->input->post('verband')))
{
$this->LehrverbandModel->db->where('verband', $this->input->post('verband'));
}
else
{
$this->LehrverbandModel->db->group_start();
$this->LehrverbandModel->db->where("trim(verband) = ''");
$this->LehrverbandModel->db->or_where("verband IS NULL");
$this->LehrverbandModel->db->group_end();
}
if (!isEmptyString($this->input->post('gruppe')))
{
$this->LehrverbandModel->db->where('gruppe', $this->input->post('gruppe'));
}
else
{
$this->LehrverbandModel->db->group_start();
$this->LehrverbandModel->db->where("trim(gruppe) = ''");
$this->LehrverbandModel->db->or_where("gruppe IS NULL");
$this->LehrverbandModel->db->group_end();
}
if (!isEmptyString((string)$this->input->post('semester')))
{
$this->LehrverbandModel->db->where('semester', $this->input->post('semester'));
}
else
{
$this->LehrverbandModel->db->group_start();
$this->LehrverbandModel->db->where("semester = ''");
$this->LehrverbandModel->db->or_where("semester IS NULL");
$this->LehrverbandModel->db->group_end();
}
$gruppen_result = $this->LehrverbandModel->loadWhere(array('studiengang_kz' => $this->input->post('stg_kz'), 'aktiv' => true));
}
if (!hasData($gruppen_result))
return $this->terminateWithError('No group found');
$gruppen_array = getData($gruppen_result)[0];
$this->terminateWithSuccess($gruppen_array->gid);
}
public function getByLehreinheit($lehreinheit_id = null)
{
if (is_null($lehreinheit_id) || !ctype_digit((string)$lehreinheit_id))
@@ -134,9 +134,14 @@ class Lektor extends FHCAPI_Controller
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
if (isset($formData['semesterstunden']) && (!is_numeric($formData['semesterstunden']) || $formData['semesterstunden'] === ''))
$nullable_fields = array('semesterstunden', 'stundensatz', 'planstunden');
foreach ($nullable_fields as $nullable_field)
{
$formData['semesterstunden'] = null;
if (isset($formData[$nullable_field]) && (!is_numeric($formData[$nullable_field]) || $formData[$nullable_field] === ''))
{
$formData[$nullable_field] = null;
}
}
$lehreinheit_permission = $this->checkPermission($lehreinheit_id, array('admin', 'assistenz', 'lv-plan'));
@@ -144,6 +149,9 @@ class Lektor extends FHCAPI_Controller
if (!$lehreinheit_permission)
$this->terminateWithError($this->p->t('ui', 'error_fieldWriteAccess'));
if (!is_null($this->getLektorVertrag($lehreinheit_id, $mitarbeiter_uid)) && (array_key_exists('mitarbeiter_uid', $formData) && $mitarbeiter_uid !== $formData['mitarbeiter_uid']))
$this->terminateWithError($this->p->t('ui', 'error_fieldWriteAccess'));
$result = $this->_ci->lektorlib->updateLektorFromLehreinheit($lehreinheit_id, $mitarbeiter_uid, $formData);
if (isError($result)) $this->terminateWithError(getError($result));
@@ -154,7 +162,7 @@ class Lektor extends FHCAPI_Controller
{
$value = str_replace(',', '.', $value);
if (!is_numeric($value))
if (!is_numeric($value) && $value !== "")
{
$this->form_validation->set_message('_check_decimal', 'Das Feld {field} muss eine Zahl sein.');
return false;
@@ -0,0 +1,84 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Noten extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'getCertificate' => 'student/noten:r',
'getTeacherProposal' => 'student/noten:r',
]);
// Load Libraries
$this->load->library('VariableLib', ['uid' => getAuthUID()]);
// Load Phrases
$this->loadPhrases([
'stv',
'person',
'lehre'
]);
}
public function getCertificate($lv_id, $studiensemester_kurzbz = null)
{
if (is_null($lv_id) || !ctype_digit((string)$lv_id))
$this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$this->load->model('education/lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel');
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$result = $this->LehrveranstaltungModel->loadWhere([
'lehrveranstaltung_id' => $lv_id
]);
$lehrveranstaltung = $this->getDataOrTerminateWithError($result);
if (!$lehrveranstaltung)
$this->terminateWithSuccess([]);
if ($studiensemester_kurzbz !== null && !$this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz))
{
$this->terminateWithError($studiensemester_kurzbz . ' - ' . $this->p->t('lehre', 'error_noStudiensemester'));
}
$result = $this->ZeugnisnoteModel->getZeugnisnoten(null, $studiensemester_kurzbz, $lehrveranstaltung[0]->lehrveranstaltung_id);
$grades = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($grades);
}
public function getTeacherProposal($lv_id, $studiensemester_kurzbz = null)
{
if (is_null($lv_id) || !ctype_digit((string)$lv_id))
$this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$this->load->model('education/lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->load->model('education/Lvgesamtnote_model', 'LvgesamtnoteModel');
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$result = $this->LehrveranstaltungModel->loadWhere([
'lehrveranstaltung_id' => $lv_id
]);
$lehrveranstaltung = $this->getDataOrTerminateWithError($result);
if (!$lehrveranstaltung)
$this->terminateWithSuccess([]);
if ($studiensemester_kurzbz !== null && !$this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz))
{
$this->terminateWithError($studiensemester_kurzbz . ' - ' . $this->p->t('lehre', 'error_noStudiensemester'));
}
$result = $this->LvgesamtnoteModel->getLvGesamtNoten($lv_id, null, $studiensemester_kurzbz);
$grades = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($grades);
}
}
@@ -27,7 +27,8 @@ class Setup extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getTabs' => ['admin:r', 'assistenz:r'],
'getLETabs' => ['admin:r', 'assistenz:r'],
'getLVTabs' => ['admin:r', 'assistenz:r'],
'getStudiensemester' => ['admin:r', 'assistenz:r'],
'getSprache' => ['admin:r', 'assistenz:r'],
'getRaumtyp' => ['admin:r', 'assistenz:r'],
@@ -41,9 +42,10 @@ class Setup extends FHCAPI_Controller
$this->_ci->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->_ci->load->library('VariableLib', ['uid' => $this->_uid]);
$this->_ci->load->helper('hlp_document');
}
public function getTabs()
public function getLETabs()
{
$tabs['details'] = array (
'title' => 'Details',
@@ -60,6 +62,11 @@ class Setup extends FHCAPI_Controller
'component' => absoluteJsImportUrl('public/js/components/LVVerwaltung/Tabs/Lektor.js'),
'config' => []
);
$tabs['termine'] = array (
'title' => 'Termine',
'component' => APP_ROOT . 'public/js/components/LVVerwaltung/Tabs/Termine.js',
'config' => []
);
$tabs['notiz'] = array (
'title' => 'Notizen',
'component' => absoluteJsImportUrl('public/js/components/LVVerwaltung/Tabs/Notiz.js'),
@@ -68,6 +75,28 @@ class Setup extends FHCAPI_Controller
$this->terminateWithSuccess($tabs);
}
public function getLVTabs()
{
$tabs['termine'] = array (
'title' => 'Termine',
'component' => APP_ROOT . 'public/js/components/LVVerwaltung/Tabs/LVTermine.js',
'config' => []
);
$tabs['noten'] = array (
'title' => 'Noten',
'component' => APP_ROOT . 'public/js/components/LVVerwaltung/Tabs/Noten.js',
'config' => [
'usePoints' => defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE,
'edit' => 'both', // Possible values: both|header|inline
'delete' => 'inline', // Possible values: both|header|inline
'documents' => 'inline', // Possible values: both|header|inline
'documentslist' => gradesDocumentsList(),
'semesterSelect' => false
]
);
$this->terminateWithSuccess($tabs);
}
public function getStudiensemester()
{
$this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
@@ -25,7 +25,25 @@ class StgTree extends FHCAPI_Controller
return $this->_outputAuthError([$method => ['admin:r', 'assistenz:r']]);
}
return $this->getStudiengang($method);
$count = count($params);
if (!$count)
return $this->getStudiengang($method);
if ($count == 1) {
if (is_numeric($params[0]))
return $this->getSemester($method, $params[0]);
else
return $this->getStudiengang($method, $params[0]);
}
if ($count == 2) {
if (is_numeric($params[0]))
return $this->getVerband($method, $params[0], $params[1]);
else
return $this->getSemester($method, $params[1], $params[0]);
}
show_404();
}
@@ -64,16 +82,27 @@ class StgTree extends FHCAPI_Controller
$this->terminateWithSuccess($list);
}
protected function getStudiengang($studiengang_kz)
protected function getStudiengang($studiengang_kz, $org_form = null)
{
$link = $studiengang_kz . '/';
if ($org_form !== null)
$link .= $org_form . '/';
$this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz');
$this->StudiengangModel->addDistinct();
$this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", semester) AS link", false);
$this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester ORDER BY verband, gruppe LIMIT 1)) AS name", false);
$this->StudiengangModel->addSelect("TRUE AS leaf", false);
$this->StudiengangModel->addSelect("CONCAT(
UPPER(CONCAT(typ, kurzbz)),
'-',
semester,
(
SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END
FROM public.tbl_lehrverband
WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester
ORDER BY verband, gruppe LIMIT 1
)
) AS name", false);
$this->StudiengangModel->addSelect('semester');
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
@@ -111,6 +140,115 @@ class StgTree extends FHCAPI_Controller
$list = array_merge($list, $result);
}
}
$this->terminateWithSuccess($list);
}
protected function getSemester($studiengang_kz, $semester, $org_form = null)
{
$link = $studiengang_kz . '/';
if ($org_form !== null)
$link .= $org_form . '/';
$link .= $semester . '/';
$this->load->model('organisation/Gruppe_model', 'GruppeModel');
$this->GruppeModel->addDistinct();
$this->GruppeModel->addSelect("CONCAT(" . $this->GruppeModel->escape($link . 'grp/') . ", gruppe_kurzbz) AS link", false);
$this->GruppeModel->addSelect("CONCAT(gruppe_kurzbz, ' (', bezeichnung, ')') AS name", false);
$this->GruppeModel->addSelect("TRUE AS leaf", false);
$this->GruppeModel->addSelect('sort');
$this->GruppeModel->addSelect('gruppe_kurzbz');
$this->GruppeModel->addSelect($this->GruppeModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
$this->GruppeModel->addOrder('sort');
$this->GruppeModel->addOrder('gruppe_kurzbz');
$where = [
'studiengang_kz' => $studiengang_kz,
'semester' => $semester,
'lehre' => true,
'sichtbar' => true,
'aktiv' => true,
'direktinskription' => false
];
if ($org_form !== null)
$where['orgform_kurzbz'] = $org_form;
$result = $this->GruppeModel->loadWhere($where);
$list = $this->getDataOrTerminateWithError($result);
$this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz');
$this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", verband) AS link", false);
$this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, verband, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester AND verband=v.verband ORDER BY gruppe LIMIT 1)) AS name", false);
$this->StudiengangModel->addSelect("CASE WHEN MAX(gruppe)='' OR MAX(gruppe)=' ' THEN TRUE ELSE FALSE END AS leaf");
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($semester) . ' AS semester');
$this->StudiengangModel->addSelect('verband');
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
$this->StudiengangModel->addOrder('verband');
$this->StudiengangModel->addGroupBy('link, name, verband');
$where = [
'v.studiengang_kz' => $studiengang_kz,
'v.semester' => $semester,
'v.verband !=' => '',
'v.aktiv' => true
];
if ($org_form !== null && $semester) // NOTE(chris): on semester 0 show all?
$where['v.orgform_kurzbz'] = $org_form;
$result = $this->StudiengangModel->loadWhere($where);
$result = $this->getDataOrTerminateWithError($result);
$list = array_merge($list, $result);
$this->terminateWithSuccess($list);
}
protected function getVerband($studiengang_kz, $semester, $verband, $org_form = null)
{
$link = $studiengang_kz . '/';
if ($org_form !== null)
$link .= $org_form . '/';
$link .= $semester . '/'. $verband . '/';
$this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz');
$this->StudiengangModel->addDistinct();
$this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", gruppe) AS link", false);
$this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, verband, gruppe, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester AND verband=v.verband AND gruppe=v.gruppe ORDER BY gruppe LIMIT 1)) AS name", false);
$this->StudiengangModel->addSelect("TRUE AS leaf", false);
$this->StudiengangModel->addSelect('v.semester');
$this->StudiengangModel->addSelect('v.verband');
$this->StudiengangModel->addSelect('gruppe');
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
$this->StudiengangModel->addOrder('gruppe');
$where = [
'v.studiengang_kz' => $studiengang_kz,
'v.semester' => $semester,
'v.verband' => $verband,
'v.gruppe !=' => '',
'v.aktiv' => true
];
if ($org_form !== null && $semester)
$where['v.orgform_kurzbz'] = $org_form;
$result = $this->StudiengangModel->loadWhere($where);
$list = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($list);
}
@@ -42,22 +42,14 @@ class Messages extends FHCAPI_Controller
]);
}
public function getMessages($id, $type_id, $size=null, $page=null)
public function getMessages($id, $type_id, $size, $page)
{
if($type_id != 'person_id'){
$id = $this->_getPersonId($id, $type_id);
}
if(!(is_null($size) && is_null($page)))
{
$offset = $size * ($page - 1);
$limit = $size;
}
else
{
$offset = null;
$limit = null;
}
$offset = $size * ($page - 1);
$limit = $size;
$result = $this->MessageModel->getMessagesForTable($id, $offset, $limit);
@@ -301,6 +301,7 @@ class Config extends FHCAPI_Controller
public function student()
{
$this->load->helper('hlp_document');
$result = [];
$config = $this->config->item('tabs');
@@ -376,9 +377,9 @@ class Config extends FHCAPI_Controller
'config' => [
'usePoints' => defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE,
'edit' => 'both', // Possible values: both|header|inline
'delete' => 'both', // Possible values: both|header|inline
'documents' => 'both', // Possible values: both|header|inline
'documentslist' => $this->gradesDocumentsList()
'delete' => 'inline', // Possible values: both|header|inline
'documents' => 'inline', // Possible values: both|header|inline
'documentslist' => gradesDocumentsList()
]
];
@@ -622,188 +623,6 @@ class Config extends FHCAPI_Controller
] + $this->kontoColumns();
}
/**
* Helper function to generate the default documentslist config for the
* grades tab.
*
* The resulting array consists of elements which are associative arrays
* that can have the following entries:
* title (required) on the first level this can be HTML code.
* permissioncheck (optional) an URL to an FHCAPI endpoint which returns
* true or false.
* link (optional) an URL that will be called if "action" and
* "children" are not defined.
* action (optional) an associative array that describes an
* POST action that will be called if "children" is
* not defined.
* It can have the following entries:
* - url (required) an URL to an FHCAPI endpoint.
* - post (optional) an associative array with the POST data to
* be sent.
* - response (optional) a string that will be displayed on success.
* children (optional) an array of child elements
*
* All strings that start with { and end with } in the URLs and the
* actions post parameter will be replaced with the corresponding
* attribute of the current dataset (e.G: {uid} will be replaced with the
* uid of the current dataset)
*
* @return array
*/
protected function gradesDocumentsList()
{
$permissioncheck = site_url("api/frontend/v1/documents/permissionAlternativeFormat/{studiengang_kz}");
$title_ger = $this->p->t("global", "deutsch");
$title_eng = $this->p->t("global", "englisch");
$title_ff = $this->p->t("stv", "document_certificate");
$title_lv = $this->p->t("stv", "document_coursecertificate");
$link_ff = "documents/export/" .
"zertifikat.rdf.php/" .
"Zertifikat" .
"?stg_kz={studiengang_kz_lv}" .
"&uid={uid}" .
"&ss={studiensemester_kurzbz}" .
"&lvid={lehrveranstaltung_id}";
$link_lv_ger = "documents/export/" .
"lehrveranstaltungszeugnis.rdf.php/" .
"LVZeugnis" .
"?stg_kz={studiengang_kz}" .
"&uid={uid}" .
"&ss={studiensemester_kurzbz}" .
"&lvid={lehrveranstaltung_id}";
$link_lv_eng = "documents/export/" .
"lehrveranstaltungszeugnis.rdf.php/" .
"LVZeugnisEng" .
"?stg_kz={studiengang_kz}" .
"&uid={uid}" .
"&ss={studiensemester_kurzbz}" .
"&lvid={lehrveranstaltung_id}";
$archive_url = "api/frontend/v1/documents/archiveSigned";
$archive_response = $this->p->t("stv", "document_signed_and_archived");
$archive_post_ff = [
"xml" => "zertifikat.rdf.php",
"xsl" => "Zertifikat",
"stg_kz" => "{studiengang_kz_lv}",
"uid" => "{uid}",
"ss" => "{studiensemester_kurzbz}",
"lvid" => "{lehrveranstaltung_id}"
];
$archive_post_lv_ger = [
"xml" => "lehrveranstaltungszeugnis.rdf.php",
"xsl" => "LVZeugnis",
"stg_kz" => "{studiengang_kz}",
"uid" => "{uid}",
"ss" => "{studiensemester_kurzbz}",
"lvid" => "{lehrveranstaltung_id}"
];
$archive_post_lv_eng = [
"xml" => "lehrveranstaltungszeugnis.rdf.php",
"xsl" => "LVZeugnisEng",
"stg_kz" => "{studiengang_kz}",
"uid" => "{uid}",
"ss" => "{studiensemester_kurzbz}",
"lvid" => "{lehrveranstaltung_id}"
];
$list = [
[
'title' => '<i class="fa fa-download" title="' . $this->p->t("stv", "document_download") . '"></i>',
'children' => [
[
'title' => $title_ff,
'link' => site_url($link_ff)
],
[
'title' => $title_lv,
'children' => [
[
'title' => $title_ger,
'link' => site_url($link_lv_ger),
'children' => [
[
'title' => 'PDF',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_ger)
],
[
'title' => 'DOC',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_ger . "&output=doc")
],
[
'title' => 'ODT',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_ger . "&output=odt")
]
]
],
[
'title' => $title_eng,
'link' => site_url($link_lv_eng),
'children' => [
[
'title' => 'PDF',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_eng)
],
[
'title' => 'DOC',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_eng . "&output=doc")
],
[
'title' => 'ODT',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_eng . "&output=odt")
]
]
]
]
]
]
],
[
'title' => '<i class="fas fa-archive" title="' . $this->p->t("stv", "document_archive") . '"></i>',
'children' => [
[
'title' => $title_ff,
'action' => [
'url' => site_url($archive_url),
'post' => $archive_post_ff,
'response' => $archive_response
]
],
[
'title' => $title_lv,
'children' => [
[
'title' => $title_ger,
'action' => [
'url' => site_url($archive_url),
'post' => $archive_post_lv_ger,
'response' => $archive_response
]
],
[
'title' => $title_eng,
'action' => [
'url' => site_url($archive_url),
'post' => $archive_post_lv_eng,
'response' => $archive_response
]
]
]
]
]
]
];
return $list;
}
/**
* Sort tab list
*
@@ -78,32 +78,52 @@ class Dokumente extends FHCAPI_Controller
$this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Studiengang_kz']), self::ERROR_TYPE_GENERAL);
$resultPreDoc = $this->_getPrestudentDokumente($prestudent_id);
$arrayAccepted = [];
$person_id = $this->_getPersonId($prestudent_id);
$mergedArray = [];
$docNames = array_map(function ($item) {
return $item->dokument_kurzbz;
}, $resultPreDoc);
foreach ($resultPreDoc as $pre)
foreach($docNames as $doc)
{
$result = $this->AkteModel->getAktenFAS($person_id, $pre->dokument_kurzbz, $studiengang_kz, $prestudent_id, true);
$result = $this->AkteModel->getAktenFAS($person_id, $doc, $studiengang_kz, $prestudent_id, true);
if (isError($result))
{
return $this->terminateWithError($result, self::ERROR_TYPE_GENERAL);
}
if (hasData($result))
{
foreach (getData($result) as $doc)
$data = getData($result);
foreach ($data as $value)
{
$merged = clone $doc;
$merged->docdatum = $pre->docdatum;
$merged->insertvonma = $pre->insertvonma;
$merged->bezeichnung = $pre->bezeichnung;
$mergedArray[] = $merged;
array_push($arrayAccepted, $value);
}
}
else
{
$mergedArray[] = $pre;
}
//Mapping with document_kurzbz
$preDocMap = [];
foreach ($resultPreDoc as $pre) {
$preDocMap[$pre->dokument_kurzbz] = $pre;
}
$mergedArray = [];
foreach ($arrayAccepted as $doc) {
$merged = clone $doc;
if (isset($preDocMap[$doc->dokument_kurzbz])) {
$merged->docdatum = $preDocMap[$doc->dokument_kurzbz]->docdatum;
$merged->insertvonma = $preDocMap[$doc->dokument_kurzbz]->insertvonma;
$merged->bezeichnung = $preDocMap[$doc->dokument_kurzbz]->bezeichnung;
} else {
$merged->akzeptiertdatum = null;
$merged->akzeptiertvon = null;
}
$mergedArray[] = $merged;
}
$this->terminateWithSuccess($mergedArray);
@@ -48,8 +48,7 @@ class Konto extends FHCAPI_Controller
// Load language phrases
$this->loadPhrases([
'konto',
'lehre'
'konto'
]);
}
@@ -113,7 +112,7 @@ class Konto extends FHCAPI_Controller
*
* @return void
*/
public function getBuchungstypen($studiensemester_kurzbz = null)
public function getBuchungstypen()
{
$this->load->model('crm/Buchungstyp_model', 'BuchungstypModel');
@@ -123,7 +122,6 @@ class Konto extends FHCAPI_Controller
$data = $this->getDataOrTerminateWithError($result);
$this->_getOEHBeitrag($data, $studiensemester_kurzbz);
$this->terminateWithSuccess($data);
}
@@ -496,43 +494,4 @@ class Konto extends FHCAPI_Controller
$this->terminateWithSuccess();
}
private function _getOEHBeitrag(&$data, $studiensemester_kurzbz = null)
{
if (is_null($studiensemester_kurzbz))
{
$this->load->library('VariableLib', ['uid' => getAuthUID()]);
$studiensemester_akt = $this->variablelib->getVar('semester_aktuell');
}
else
{
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
if ($this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz))
$studiensemester_akt = $studiensemester_kurzbz;
else
$this->terminateWithError($this->p->t('lehre', 'error_noStudiensemester'));
}
$this->load->model('codex/Oehbeitrag_model', 'OehbeitragModel');
$oehBeitrag = $this->OehbeitragModel->getByStudiensemester($studiensemester_akt);
$oehStandardbetrag = null;
if (hasData($oehBeitrag))
{
$oeh = getData($oehBeitrag)[0];
$summe = ($oeh->studierendenbeitrag + $oeh->versicherung) * -1;
$oehStandardbetrag = number_format((float)$summe, 2, '.', '');
}
if ($oehStandardbetrag !== null)
{
$data = array_map(function ($buchungstyp) use ($oehStandardbetrag) {
if (isset($buchungstyp->buchungstyp_kurzbz) && (strtolower($buchungstyp->buchungstyp_kurzbz) === 'oeh'))
{
$buchungstyp->standardbetrag = $oehStandardbetrag;
}
return $buchungstyp;
}, $data);
}
}
}
@@ -34,7 +34,7 @@ class LvTermine extends FHCAPI_Controller
//TODO Build own lib or combine with Controller Stundenplan.php
//here use of logic of Stundenplan.php, extended with parameters uid, grouping, and used dbTable
public function getStundenplan($uid, $start_date = null, $end_date = null, $groupConsecutiveHours = false, $dbStundenplanTable = "stundenplan")
public function getStundenplan($uid, $start_date = null, $end_date = null, $dbStundenplanTable = "stundenplan", $groupConsecutiveHours = false)
{
$student_uid = $uid;
$semester_range = $this->studienSemesterErmitteln($start_date, $end_date);
@@ -80,13 +80,6 @@ class LvTermine extends FHCAPI_Controller
$stundenplan_data = $this->getDataOrTerminateWithError($stundenplan_data) ?? [];
$this->terminateWithSuccess($stundenplan_data);
$this->expand_object_information($stundenplan_data);
$this->returnObj['$stundenplan_query'] = $stundenplan_query;
$this->returnObj['$student_lehrverband'] = $student_lehrverband;
$this->returnObj['$benutzer_gruppen'] = $benutzer_gruppen;
$this->terminateWithSuccess($stundenplan_data);
}
public function getStudiensemester()
@@ -90,15 +90,6 @@ class Projektarbeit extends FHCAPI_Controller
if (!isset($projektarbeit_id) || !is_numeric($projektarbeit_id)) return $this->terminateWithError('Projektarbeit Id missing', self::ERROR_TYPE_GENERAL);
$result = $this->fetchProjektarbeitByID($projektarbeit_id);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(current($data));
}
private function fetchProjektarbeitById($projektarbeit_id) {
$this->ProjektarbeitModel->resetQuery();
$this->ProjektarbeitModel->addSelect(
'lehre.tbl_projektarbeit.projektarbeit_id, titel, titel_english, themenbereich, projekttyp_kurzbz, lehrveranstaltung_id, lehreinheit_id,
firma_id, beginn, ende, gesperrtbis, note, final, freigegeben, tbl_projektarbeit.anmerkung, fa.name AS firma_name'
@@ -106,10 +97,13 @@ class Projektarbeit extends FHCAPI_Controller
$this->ProjektarbeitModel->addJoin('lehre.tbl_lehreinheit le', 'lehreinheit_id');
$this->ProjektarbeitModel->addJoin('lehre.tbl_lehrveranstaltung lv', 'lehrveranstaltung_id');
$this->ProjektarbeitModel->addJoin('public.tbl_firma fa', 'firma_id', 'LEFT');
return $this->ProjektarbeitModel->loadWhere(
$result = $this->ProjektarbeitModel->loadWhere(
array('projektarbeit_id' => $projektarbeit_id)
);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(current($data));
}
/**
@@ -138,8 +132,7 @@ class Projektarbeit extends FHCAPI_Controller
);
$data = $this->getDataOrTerminateWithError($result);
$data = $this->getDataOrTerminateWithError($this->fetchProjektarbeitById($data));
$this->terminateWithSuccess($data);
}
@@ -358,8 +358,7 @@ class AbgabetoolJob extends JOB_Controller
foreach($assistenzMap as $assistenz_person_id => $tupelArr) {
$abgabenString = '<div style="font-family: Arial, sans-serif; color: #333;">';
$hasContent = false;
foreach($tupelArr as $tupel) {
$projektarbeit_id = $tupel[0];
$assistenzRow = $tupel[1];
@@ -378,7 +377,6 @@ class AbgabetoolJob extends JOB_Controller
if(count($relevantAbgaben) == 0) {
continue;
}
$hasContent = true;
// Format the Student Name
$s = $relevantAbgaben[0];
@@ -449,12 +447,7 @@ class AbgabetoolJob extends JOB_Controller
}
$abgabenString .= '</div>';
// skip send entirely
if (!$hasContent) {
continue;
}
// done with building the change list, now send it
$assistenzRow = $tupelArr[0][1];
$anrede = $assistenzRow->anrede;
-1
View File
@@ -417,7 +417,6 @@ abstract class Notiz_Controller extends FHCAPI_Controller
$notiz_id = $this->input->post('notiz_id');
$this->NotizModel->addSelect('campus.tbl_dms_version.*');
$this->NotizModel->addSelect($this->NotizModel->escape(base_url('content/notizdokdownload.php?id=')) . ' || public.tbl_notiz_dokument.dms_id AS preview');
$this->NotizModel->addJoin('public.tbl_notiz_dokument', 'ON (public.tbl_notiz_dokument.notiz_id = public.tbl_notiz.notiz_id)');
$this->NotizModel->addJoin('campus.tbl_dms_version', 'ON (public.tbl_notiz_dokument.dms_id = campus.tbl_dms_version.dms_id)');
+188
View File
@@ -0,0 +1,188 @@
<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
* Helper function to generate the default documentslist config for the
* grades tab.
*
* The resulting array consists of elements which are associative arrays
* that can have the following entries:
* title (required) on the first level this can be HTML code.
* permissioncheck (optional) an URL to an FHCAPI endpoint which returns
* true or false.
* link (optional) an URL that will be called if "action" and
* "children" are not defined.
* action (optional) an associative array that describes an
* POST action that will be called if "children" is
* not defined.
* It can have the following entries:
* - url (required) an URL to an FHCAPI endpoint.
* - post (optional) an associative array with the POST data to
* be sent.
* - response (optional) a string that will be displayed on success.
* children (optional) an array of child elements
*
* All strings that start with { and end with } in the URLs and the
* actions post parameter will be replaced with the corresponding
* attribute of the current dataset (e.G: {uid} will be replaced with the
* uid of the current dataset)
*
* @return array
*/
function gradesDocumentsList()
{
$ci =& get_instance();
$ci->load->library('PhrasesLib', array('stv'), 'p');
$permissioncheck = site_url("api/frontend/v1/documents/permissionAlternativeFormat/{studiengang_kz}");
$title_ger = $ci->p->t("global", "deutsch");
$title_eng = $ci->p->t("global", "englisch");
$title_ff = $ci->p->t("stv", "document_certificate");
$title_lv = $ci->p->t("stv", "document_coursecertificate");
$link_ff = "documents/export/" .
"zertifikat.rdf.php/" .
"Zertifikat" .
"?stg_kz={studiengang_kz_lv}" .
"&uid={uid}" .
"&ss={studiensemester_kurzbz}" .
"&lvid={lehrveranstaltung_id}";
$link_lv_ger = "documents/export/" .
"lehrveranstaltungszeugnis.rdf.php/" .
"LVZeugnis" .
"?stg_kz={studiengang_kz}" .
"&uid={uid}" .
"&ss={studiensemester_kurzbz}" .
"&lvid={lehrveranstaltung_id}";
$link_lv_eng = "documents/export/" .
"lehrveranstaltungszeugnis.rdf.php/" .
"LVZeugnisEng" .
"?stg_kz={studiengang_kz}" .
"&uid={uid}" .
"&ss={studiensemester_kurzbz}" .
"&lvid={lehrveranstaltung_id}";
$archive_url = "api/frontend/v1/documents/archiveSigned";
$archive_response = $ci->p->t("stv", "document_signed_and_archived");
$archive_post_ff = [
"xml" => "zertifikat.rdf.php",
"xsl" => "Zertifikat",
"stg_kz" => "{studiengang_kz_lv}",
"uid" => "{uid}",
"ss" => "{studiensemester_kurzbz}",
"lvid" => "{lehrveranstaltung_id}"
];
$archive_post_lv_ger = [
"xml" => "lehrveranstaltungszeugnis.rdf.php",
"xsl" => "LVZeugnis",
"stg_kz" => "{studiengang_kz}",
"uid" => "{uid}",
"ss" => "{studiensemester_kurzbz}",
"lvid" => "{lehrveranstaltung_id}"
];
$archive_post_lv_eng = [
"xml" => "lehrveranstaltungszeugnis.rdf.php",
"xsl" => "LVZeugnisEng",
"stg_kz" => "{studiengang_kz}",
"uid" => "{uid}",
"ss" => "{studiensemester_kurzbz}",
"lvid" => "{lehrveranstaltung_id}"
];
$list = [
[
'title' => '<i class="fa fa-download" title="' . $ci->p->t("stv", "document_download") . '"></i>',
'children' => [
[
'title' => $title_ff,
'link' => site_url($link_ff)
],
[
'title' => $title_lv,
'children' => [
[
'title' => $title_ger,
'link' => site_url($link_lv_ger),
'children' => [
[
'title' => 'PDF',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_ger)
],
[
'title' => 'DOC',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_ger . "&output=doc")
],
[
'title' => 'ODT',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_ger . "&output=odt")
]
]
],
[
'title' => $title_eng,
'link' => site_url($link_lv_eng),
'children' => [
[
'title' => 'PDF',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_eng)
],
[
'title' => 'DOC',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_eng . "&output=doc")
],
[
'title' => 'ODT',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_eng . "&output=odt")
]
]
]
]
]
]
],
[
'title' => '<i class="fas fa-archive" title="' . $ci->p->t("stv", "document_archive") . '"></i>',
'children' => [
[
'title' => $title_ff,
'action' => [
'url' => site_url($archive_url),
'post' => $archive_post_ff,
'response' => $archive_response
]
],
[
'title' => $title_lv,
'children' => [
[
'title' => $title_ger,
'action' => [
'url' => site_url($archive_url),
'post' => $archive_post_lv_ger,
'response' => $archive_response
]
],
[
'title' => $title_eng,
'action' => [
'url' => site_url($archive_url),
'post' => $archive_post_lv_eng,
'response' => $archive_response
]
]
]
]
]
]
];
return $list;
}
+1 -1
View File
@@ -128,7 +128,7 @@ class AntragLib
return $this->_ci->StudierendenantragstatusModel->resumeAntraegeForAbmeldungStgl($antrag_id);
}
// NOTE(chris): get last status that is not pause
$this->_ci->StudierendenantragstatusModel->addOrder('insertamum', 'DESC');
$this->_ci->StudierendenantragstatusModel->addOrder('insertamum');
$this->_ci->StudierendenantragstatusModel->addLimit(1);
$result = $this->_ci->StudierendenantragstatusModel->loadWhere([
'studierendenantrag_id' => $antrag_id,
+175 -79
View File
@@ -17,7 +17,12 @@ class LektorLib
$this->_ci->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
$this->_ci->load->model('ressource/mitarbeiter_model', 'MitarbeiterModel');
$this->_ci->load->model('person/Benutzer_model', 'BenutzerModel');
$this->_ci->load->model('ressource/Zeitsperre_model', 'ZeitsperreModel');
$this->_ci->load->model('ressource/Reservierung_model', 'ReservierungModel');
$this->_ci->load->model('ressource/stundenplandev_model', 'StundenplandevModel');
$this->_ci->load->library('PhrasesLib', array('lehre'));
$this->_ci->load->library('VariableLib', ['uid' => getAuthUID()]);
}
public function addLektorToLehreinheit($lehreinheit_id, $mitarbeiter_uid)
@@ -78,10 +83,13 @@ class LektorLib
public function updateLektorFromLehreinheit($lehreinheit_id, $mitarbeiter_uid, $new_data)
{
$old_uid = $mitarbeiter_uid;
$new_uid = isset($new_data['mitarbeiter_uid']) ? $new_data['mitarbeiter_uid'] : $mitarbeiter_uid;
$this->_ci->LehreinheitmitarbeiterModel->addSelect('lehre.tbl_lehreinheitmitarbeiter.*, lehre.tbl_lehreinheit.studiensemester_kurzbz, tbl_lehrveranstaltung.studiengang_kz');
$this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehreinheit', 'lehreinheit_id');
$this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehrveranstaltung', 'lehrveranstaltung_id');
$lehreinheit_result = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid));
$lehreinheit_result = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $old_uid));
if (isError($lehreinheit_result)) return $lehreinheit_result;
@@ -89,27 +97,47 @@ class LektorLib
$lehreinheit = getData($lehreinheit_result)[0];
$semesterstunden_alt = $lehreinheit->semesterstunden;
$semesterstunden_neu = isset($new_data['semesterstunden']) ? $new_data['semesterstunden'] : $semesterstunden_alt;
$bismelden_neu = isset($new_data['bismelden']) ? $new_data['bismelden'] : $lehreinheit->bismelden;
$neue_stunden_eingerechnet = (bool)$bismelden_neu;
$alte_stunden_eingerechnet = (bool)$lehreinheit->bismelden;
$stundenplan_update = false;
//TODO kollision check, wird vorerst nicht implementiert -> nur über das FAS möglich
if (isset($new_data['mitarbeiter_uid']) && $new_data['mitarbeiter_uid'] !== $mitarbeiter_uid)
if ($old_uid !== $new_uid)
{
$this->_ci->load->model('ressource/stundenplandev_model', 'StundenplandevModel');
$this->_ci->StundenplandevModel->addGroupBy('stundenplandev_id');
$this->_ci->StundenplandevModel->addGroupBy('mitarbeiter_uid');
$this->_ci->StundenplandevModel->addGroupBy('mitarbeiter_uid');
$verplant = $this->_ci->StundenplandevModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid));
if (hasData($verplant))
return error($this->_ci->phraseslib->t("lehre", "lektorbereitsverplant"));
$lehreinheit_data = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('mitarbeiter_uid' => $new_data['mitarbeiter_uid'], 'lehreinheit_id' => $lehreinheit_id));
$lehreinheit_data = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('mitarbeiter_uid' => $new_uid, 'lehreinheit_id' => $lehreinheit_id));
if (hasData($lehreinheit_data))
return error($this->_ci->phraseslib->t("lehre", "bereitzugeteilt"));
$this->_ci->StundenplandevModel->addGroupBy('stundenplandev_id');
$this->_ci->StundenplandevModel->addGroupBy('mitarbeiter_uid');
$verplant = $this->_ci->StundenplandevModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $old_uid));
if (hasData($verplant))
{
$kollision = $this->hasKollision(getData($verplant), $new_uid);
$ignore_kollision = $this->_ci->variablelib->getVar('ignore_kollision');
if ($kollision === false || $ignore_kollision == 'true')
{
$stundenplan_update = true;
}
else if (is_array($kollision))
{
return error( "Änderung fehlgeschlagen! Die Änderung des Lektors führt zu ".count($kollision)." Kollision(en) im LV-Plan. Deaktivieren Sie die Kollisionspruefung oder wenden Sie sich an die LV-Planung!\n zB. $kollision[0]");
}
else
{
return error($kollision);
}
}
}
$warning = '';
if (isset($new_data['semesterstunden']))
if (($semesterstunden_neu !== '' && $semesterstunden_alt !== '') && (($semesterstunden_neu > $semesterstunden_alt) || $neue_stunden_eingerechnet))
{
$studiengang_result = $this->_ci->StudiengangModel->loadWhere(array('studiengang_kz' => $lehreinheit->studiengang_kz));
if (isError($studiengang_result)) return $studiengang_result;
@@ -120,7 +148,7 @@ class LektorLib
if (isError($studiensemester_result)) return $studiensemester_result;
$studiensemester = getData($studiensemester_result)[0];
$echter_dv_result = $this->_ci->DienstverhaeltnisModel->existsDienstverhaeltnis($mitarbeiter_uid, $studiensemester->start, $studiensemester->ende, 'echterdv');
$echter_dv_result = $this->_ci->DienstverhaeltnisModel->existsDienstverhaeltnis($new_uid, $studiensemester->start, $studiensemester->ende, 'echterdv');
$echter_dv = false;
@@ -129,83 +157,78 @@ class LektorLib
$echter_dv = true;
}
$neue_stunden_eingerechnet = isset($new_data['bismelden']) ? $new_data['bismelden'] : $lehreinheit->bismelden;
$alte_stunden_eingerechnet = $lehreinheit->bismelden;
$stundengrenze_result = $this->_ci->OrganisationseinheitModel->getStundengrenze($studiengang->oe_kurzbz, $echter_dv);
if (isError($stundengrenze_result)) return $stundengrenze_result;
if (($new_data['semesterstunden'] > $lehreinheit->semesterstunden) || $neue_stunden_eingerechnet)
$stundengrenze = getData($stundengrenze_result)[0];
$oe_result = $this->_ci->OrganisationseinheitModel->getChilds($stundengrenze->oe_kurzbz);
$oe_array = hasData($oe_result) ? array_column(getData($oe_result), 'oe_kurzbz') : array();
if ($alte_stunden_eingerechnet && $neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($semesterstunden_alt) + {$this->_ci->LehreinheitmitarbeiterModel->db->escape($semesterstunden_neu)}) as summe");
else if ($alte_stunden_eingerechnet && !$neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($semesterstunden_alt)) as summe");
else if (!$alte_stunden_eingerechnet && $neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) + ({$this->_ci->LehreinheitmitarbeiterModel->db->escape($semesterstunden_neu)})) as summe");
else if (!$alte_stunden_eingerechnet && !$neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden)) as summe");
$this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehreinheit', 'lehreinheit_id');
$this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehrveranstaltung', 'lehrveranstaltung_id');
$this->_ci->LehreinheitmitarbeiterModel->addJoin('public.tbl_studiengang', 'studiengang_kz');
$this->_ci->LehreinheitmitarbeiterModel->db->where('mitarbeiter_uid', $new_uid);
$this->_ci->LehreinheitmitarbeiterModel->db->where('studiensemester_kurzbz', $lehreinheit->studiensemester_kurzbz);
$this->_ci->LehreinheitmitarbeiterModel->db->where('bismelden', true);
$this->_ci->LehreinheitmitarbeiterModel->db->where('lower(mitarbeiter_uid) NOT LIKE', '_dummy%');
if (count($oe_array) > 0)
{
$stundengrenze_result = $this->_ci->OrganisationseinheitModel->getStundengrenze($studiengang->oe_kurzbz, $echter_dv);
if (isError($stundengrenze_result)) return $stundengrenze_result;
$stundengrenze = getData($stundengrenze_result)[0];
$oe_result = $this->_ci->OrganisationseinheitModel->getChilds($stundengrenze->oe_kurzbz);
$oe_array = hasData($oe_result) ? array_column(getData($oe_result), 'oe_kurzbz') : array('');
if ($alte_stunden_eingerechnet && $neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($lehreinheit->semesterstunden) + {$this->_ci->LehreinheitmitarbeiterModel->db->escape($new_data['semesterstunden'])}) as summe");
else if ($alte_stunden_eingerechnet && !$neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($lehreinheit->semesterstunden)) as summe");
else if (!$alte_stunden_eingerechnet && $neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) + ({$this->_ci->LehreinheitmitarbeiterModel->db->escape($new_data['semesterstunden'])})) as summe");
else if (!$alte_stunden_eingerechnet && !$neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden)) as summe");
$this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehreinheit', 'lehreinheit_id');
$this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehrveranstaltung', 'lehrveranstaltung_id');
$this->_ci->LehreinheitmitarbeiterModel->addJoin('public.tbl_studiengang', 'studiengang_kz');
$this->_ci->LehreinheitmitarbeiterModel->db->where('mitarbeiter_uid', (isset($new_data['mitarbeiter_uid']) ? $new_data['mitarbeiter_uid'] : $mitarbeiter_uid));
$this->_ci->LehreinheitmitarbeiterModel->db->where('studiensemester_kurzbz', $lehreinheit->studiensemester_kurzbz);
$this->_ci->LehreinheitmitarbeiterModel->db->where('bismelden', true);
$this->_ci->LehreinheitmitarbeiterModel->db->where('lower(mitarbeiter_uid) NOT LIKE', '_dummy%');
$this->_ci->LehreinheitmitarbeiterModel->db->where_in('tbl_studiengang.oe_kurzbz', $oe_array);
}
if(defined('FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE')
&& is_array(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE)
&& count(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE) > 0)
{
$this->_ci->LehreinheitmitarbeiterModel->db->where_not_in('tbl_studiengang.oe_kurzbz', FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE);
}
if(defined('FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE')
&& is_array(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE)
&& count(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE) > 0)
$summe_result = $this->_ci->LehreinheitmitarbeiterModel->load();
if (isError($summe_result)) return $summe_result;
if (!hasData($summe_result)) return error('Fehler beim Ermitteln der Gesamtstunden');
$summe = getData($summe_result)[0]->summe;
if ($summe > $stundengrenze->stunden)
{
if (!$echter_dv && (!$this->_ci->permissionlib->isBerechtigt('admin')))
{
$this->_ci->LehreinheitmitarbeiterModel->db->where_not_in('tbl_studiengang.oe_kurzbz', FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE);
if (!$this->LehrauftragAufFirma($new_uid))
return error("ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden NICHT gespeichert!\n\n");
}
else
{
$warning .= "ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden gespeichert!\n\n";
}
$summe_result = $this->_ci->LehreinheitmitarbeiterModel->load();
$stunden_limit_result = $this->getStundenInstitut($new_uid, $lehreinheit->studiensemester_kurzbz, $oe_array);
if (isError($summe_result)) return $summe_result;
if (!hasData($summe_result)) return error('Fehler beim Ermitteln der Gesamtstunden');
$summe = getData($summe_result)[0]->summe;
if ($summe > $stundengrenze->stunden)
if (hasData($stunden_limit_result))
{
if (!$echter_dv && (!$this->_ci->permissionlib->isBerechtigt('admin')))
$stunden_limit_array = getData($stunden_limit_result);
foreach ($stunden_limit_array as $stunden_limit)
{
if (!$this->LehrauftragAufFirma(isset($formData['mitarbeiter_uid']) ? $formData['mitarbeiter_uid'] : $mitarbeiter_uid))
return error("ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden NICHT gespeichert!\n\n");
}
else
{
$warning .= "ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden gespeichert!\n\n";
}
$stunden_limit_result = $this->getStundenInstitut($mitarbeiter_uid, $lehreinheit->studiensemester_kurzbz, $oe_array);
if (hasData($stunden_limit_result))
{
$stunden_limit_array = getData($stunden_limit_result);
foreach ($stunden_limit_array as $stunden_limit)
{
$warning .= $stunden_limit->summe . ' Stunden ' . $stunden_limit->bezeichnung . "\n";
}
$warning .= $stunden_limit->summe . ' Stunden ' . $stunden_limit->bezeichnung . "\n";
}
}
}
}
$benutzer_result = $this->_ci->BenutzerModel->load(array(isset($formData['mitarbeiter_uid']) ? $formData['mitarbeiter_uid'] : $mitarbeiter_uid));
$benutzer_result = $this->_ci->BenutzerModel->load(array($new_uid));
if (isError($benutzer_result)) return $benutzer_result;
@@ -227,12 +250,23 @@ class LektorLib
'bismelden'
);
$nullable_fields = array('stundensatz', 'semesterstunden', 'planstunden');
$updateData = array();
foreach ($updatableFields as $field)
{
$value = isset($new_data[$field]) ? $new_data[$field] : null;
if (!array_key_exists($field, $new_data))
{
continue;
}
if ($value !== null)
$value = $new_data[$field];
if (in_array($field, $nullable_fields))
{
$updateData[$field] = $value;
}
elseif ($value !== null)
{
$updateData[$field] = $value;
}
@@ -240,10 +274,24 @@ class LektorLib
$updateData['updatevon'] = getAuthUID();
$updateData['updateamum'] = date('Y-m-d H:i:s');
$result = $this->_ci->LehreinheitmitarbeiterModel->update(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid), $updateData);
$result = $this->_ci->LehreinheitmitarbeiterModel->update(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $old_uid), $updateData);
if (isError($result)) return $result;
if ($stundenplan_update)
{
$update_result = $this->_ci->StundenplandevModel->update([
'lehreinheit_id' => $lehreinheit_id,
'mitarbeiter_uid' => $old_uid,
], [
'mitarbeiter_uid' => $new_uid,
'updateamum' => date('Y-m-d H:i:s'),
'updatevon' => getAuthUID()
]);
if (isError($update_result)) return $update_result;
}
if ($warning !== '') return success(['warning' => $warning]);
return success('Erfolgreich geupdated');
@@ -347,4 +395,52 @@ class LektorLib
$this->_ci->LehreinheitmitarbeiterModel->addGroupBy('tbl_studiengang.bezeichnung');
return $this->_ci->LehreinheitmitarbeiterModel->load();
}
private function hasKollision($stunden, $mitarbeiter)
{
$kollision = array();
$ignore_zeitsperre = $this->_ci->variablelib->getVar('ignore_zeitsperre');
$ignore_reservierung = $this->_ci->variablelib->getVar('ignore_reservierung');
foreach ($stunden as $stunde)
{
$stundenplan_result = $this->_ci->StundenplandevModel->lektorHasStundenplandevEintrag($mitarbeiter, $stunde->datum, $stunde->stunde);
if (isError($stundenplan_result))
return $stundenplan_result;
if (hasData($stundenplan_result))
{
$stundenplan_result = getData($stundenplan_result)[0];
$kollision[] = "Kollision stundenplandev: $stundenplan_result->stundenplandev_id|$stundenplan_result->lektor|$stundenplan_result->ort_kurzbz|$stundenplan_result->stg_kurzbz-$stundenplan_result->semester$stundenplan_result->verband$stundenplan_result->gruppe$stundenplan_result->gruppe_kurzbz - $stundenplan_result->datum/$stundenplan_result->stunde";
}
else
{
if ($ignore_zeitsperre == 'false' && (!defined('KOLLISIONSFREIE_USER') || !in_array($mitarbeiter, unserialize(KOLLISIONSFREIE_USER))))
{
$zeitsperre_result = $this->_ci->ZeitsperreModel->checkIfZeitsperreExists($mitarbeiter, $stunde->datum, $stunde->stunde);
if (hasData($zeitsperre_result))
{
$zeitsperre_result = getData($zeitsperre_result)[0];
$kollision[] = "Kollision (Zeitsperre): $zeitsperre_result->zeitsperre_id|$zeitsperre_result->mitarbeiter_uid|$zeitsperre_result->zeitsperretyp_kurzbz - $zeitsperre_result->vondatum/$zeitsperre_result->vonstunde|$zeitsperre_result->bisdatum/$zeitsperre_result->bisstunde";
}
}
if ($ignore_reservierung == 'false' && (!defined('KOLLISIONSFREIE_USER') || !in_array($mitarbeiter, unserialize(KOLLISIONSFREIE_USER))))
{
$reservierung_result = $this->_ci->ReservierungModel->lektorHasReservierung($mitarbeiter, $stunde->datum, $stunde->stunde);
if (hasData($reservierung_result))
{
$reservierung_result = getData($reservierung_result)[0];
$kollision[] = "Kollision (Reservierung): $reservierung_result->reservierung_id|$reservierung_result->uid|$reservierung_result->ort_kurzbz|$reservierung_result->stg_kurzbz-$reservierung_result->semester$reservierung_result->verband$reservierung_result->gruppe$reservierung_result->gruppe_kurzbz - $reservierung_result->datum/$reservierung_result->stunde";
}
}
}
}
return isEmptyArray($kollision) ? false : $kollision;
}
}
+15
View File
@@ -181,6 +181,21 @@ class StundenplanLib
return success($stundenplan_data);
}
public function getEventsByLE($lehreinheit_id, $start, $end, $stundenplan)
{
$this->_ci =& get_instance();
$this->_ci->load->model('ressource/Stundenplan_model', 'StundenplanModel');
return $this->_ci->StundenplanModel->getStundenplanLE($lehreinheit_id, $start, $end, $stundenplan);
}
public function getEventsByLV($lehrveranstaltung_id, $start, $end, $stundenplan)
{
$this->_ci =& get_instance();
$this->_ci->load->model('ressource/Stundenplan_model', 'StundenplanModel');
return $this->_ci->StundenplanModel->getStundenplanLV($lehrveranstaltung_id, $start, $end, $stundenplan);
}
/**
* Get stundenplan for a room
*
@@ -40,9 +40,7 @@ abstract class AbstractBestandteil implements IValidation
if( is_bool($new_value) && ($old_value !== $new_value) ) {
$this->modifiedcolumns[$columnname] = $columnname;
} else if(is_null($old_value) xor is_null($new_value)) {
$this->modifiedcolumns[$columnname] = $columnname;
} else if($old_value != $new_value) {
} else if($old_value != $new_value) {
$this->modifiedcolumns[$columnname] = $columnname;
}
}
@@ -3,7 +3,6 @@ namespace vertragsbestandteil;
use Exception;
use vertragsbestandteil\VertragsbestandteilStunden;
use vertragsbestandteil\VertragsbestandteilLohnguide;
/**
* Description of VertragsbestandteilFactory
@@ -23,7 +22,6 @@ class VertragsbestandteilFactory
const VERTRAGSBESTANDTEIL_URLAUBSANSPRUCH = 'urlaubsanspruch';
const VERTRAGSBESTANDTEIL_ZEITAUFZEICHNUNG = 'zeitaufzeichnung';
const VERTRAGSBESTANDTEIL_LEHRE = 'lehre';
const VERTRAGSBESTANDTEIL_LOHNGUIDE = 'lohnguide';
public static function getVertragsbestandteil($data, $fromdb=false)
{
@@ -71,11 +69,6 @@ class VertragsbestandteilFactory
$vertragsbestandteil = new VertragsbestandteilZeitaufzeichnung();
$vertragsbestandteil->hydrateByStdClass($data, $fromdb);
break;
case self::VERTRAGSBESTANDTEIL_LOHNGUIDE:
$vertragsbestandteil = new VertragsbestandteilLohnguide();
$vertragsbestandteil->hydrateByStdClass($data, $fromdb);
break;
default:
throw new Exception('Unknown vertragsbestandteiltyp_kurzbz '
@@ -134,12 +127,6 @@ class VertragsbestandteilFactory
$vertragsbestandteildbmodel = $CI->VertragsbestandteilZeitaufzeichnung_model;
break;
case self::VERTRAGSBESTANDTEIL_LOHNGUIDE:
$CI->load->model('vertragsbestandteil/VertragsbestandteilLohnguide_model',
'VertragsbestandteilLohnguide_model');
$vertragsbestandteildbmodel = $CI->VertragsbestandteilLohnguide_model;
break;
default:
throw new Exception('Unknown vertragsbestandteil_kurzbz '
. $vertragsbestandteil_kurzbz);
@@ -10,7 +10,6 @@ require_once __DIR__ . '/VertragsbestandteilKuendigungsfrist.php';
require_once __DIR__ . '/VertragsbestandteilUrlaubsanspruch.php';
require_once __DIR__ . '/VertragsbestandteilFreitext.php';
require_once __DIR__ . '/VertragsbestandteilKarenz.php';
require_once __DIR__ . '/VertragsbestandteilLohnguide.php';
require_once __DIR__ . '/VertragsbestandteilFactory.php';
require_once __DIR__ . '/OverlapChecker.php';
@@ -1,161 +0,0 @@
<?php
namespace vertragsbestandteil;
use vertragsbestandteil\Vertragsbestandteil;
use vertragsbestandteil\VertragsbestandteilFactory;
class VertragsbestandteilLohnguide extends Vertragsbestandteil
{
protected $stellenbezeichnung;
protected $vordienstzeit;
protected $fachrichtung_kurzbz;
protected $modellstelle_kurzbz;
protected $kommentar_person;
protected $kommentar_modellstelle;
public function __construct()
{
parent::__construct();
$this->setVertragsbestandteiltyp_kurzbz(
VertragsbestandteilFactory::VERTRAGSBESTANDTEIL_LOHNGUIDE);
}
public function getStellenbezeichnung()
{
return $this->stellenbezeichnung;
}
public function setStellenbezeichnung($stellenbezeichnung): self
{
$this->markDirty('stellenbezeichnung', $this->stellenbezeichnung, $stellenbezeichnung);
$this->stellenbezeichnung = $stellenbezeichnung;
return $this;
}
public function getVordienstzeit()
{
return $this->vordienstzeit;
}
public function setVordienstzeit($vordienstzeit): self
{
$this->markDirty('vordienstzeit', $this->vordienstzeit, $vordienstzeit);
$this->vordienstzeit = $vordienstzeit;
return $this;
}
public function getFachrichtung_kurzbz()
{
return $this->fachrichtung_kurzbz;
}
public function setFachrichtung_kurzbz($fachrichtung_kurzbz): self
{
$this->markDirty('fachrichtung_kurzbz', $this->fachrichtung_kurzbz, $fachrichtung_kurzbz);
$this->fachrichtung_kurzbz = $fachrichtung_kurzbz;
return $this;
}
public function getModellstelle_kurzbz()
{
return $this->modellstelle_kurzbz;
}
public function setModellstelle_kurzbz($modellstelle_kurzbz): self
{
$this->markDirty('modellstelle_kurzbz', $this->modellstelle_kurzbz, $modellstelle_kurzbz);
$this->modellstelle_kurzbz = $modellstelle_kurzbz;
return $this;
}
public function getKommentar_person()
{
return $this->kommentar_person;
}
public function setKommentar_person($kommentar_person): self
{
$this->markDirty('kommentar_person', $this->kommentar_person, $kommentar_person);
$this->kommentar_person = $kommentar_person;
return $this;
}
public function getKommentar_modellstelle()
{
return $this->kommentar_modellstelle;
}
public function setKommentar_modellstelle($kommentar_modellstelle): self
{
$this->markDirty('kommentar_modellstelle', $this->kommentar_modellstelle, $kommentar_modellstelle);
$this->kommentar_modellstelle = $kommentar_modellstelle;
return $this;
}
public function hydrateByStdClass($data, $fromdb=false)
{
parent::hydrateByStdClass($data, $fromdb);
$this->fromdb = $fromdb;
isset($data->fachrichtung_kurzbz) && $this->setFachrichtung_kurzbz($data->fachrichtung_kurzbz);
isset($data->stellenbezeichnung) && $this->setStellenbezeichnung($data->stellenbezeichnung);
isset($data->vordienstzeit) && $this->setVordienstzeit($data->vordienstzeit);
isset($data->modellstelle_kurzbz) && $this->setModellstelle_kurzbz($data->modellstelle_kurzbz);
isset($data->kommentar_person) && $this->setKommentar_person($data->kommentar_person);
isset($data->kommentar_modellstelle) && $this->setKommentar_modellstelle($data->kommentar_modellstelle);
$this->fromdb = false;
}
public function toStdClass(): \stdClass
{
$tmp = array(
'vertragsbestandteil_id' => $this->getVertragsbestandteil_id(),
'stellenbezeichnung' => $this->getStellenbezeichnung(),
'vordienstzeit' => $this->getVordienstzeit(),
'fachrichtung_kurzbz' => $this->getFachrichtung_kurzbz(),
'modellstelle_kurzbz' => $this->getModellstelle_kurzbz(),
'kommentar_person' => $this->getKommentar_person(),
'kommentar_modellstelle' => $this->getKommentar_modellstelle(),
);
$tmp = array_filter($tmp, function($k) {
return in_array($k, $this->modifiedcolumns);
}, ARRAY_FILTER_USE_KEY);
return (object) $tmp;
}
public function __toString()
{
$txt = <<<EOTXT
modellstelle_kurzbz: {$this->getModellstelle_kurzbz()}
EOTXT;
return parent::__toString() . $txt;
}
public function validate()
{
$value = $this->vordienstzeit;
if ($value === null || $value === '') {
$result = null; // allow null value
} else {
$result = filter_var($value, FILTER_VALIDATE_INT, [
'options' => [
'min_range' => 0,
'max_range' => 100
]
]);
if ($result === false) {
$this->validationerrors[] = 'Vordienstjahre muss eine ganze Zahl (0 bis 100) enthalten oder leer sein.';
}
}
return parent::validate();
}
}
@@ -52,4 +52,53 @@ class LePruefung_model extends DB_Model
'student_uid' => $student_uid
]);
}
public function getPruefungenByLvStudiensemester($lv_id, $sem_kurzbz) {
$qry = "SELECT tbl_pruefung.*, tbl_lehrveranstaltung.bezeichnung as lehrveranstaltung_bezeichnung, tbl_lehrveranstaltung.lehrveranstaltung_id,
tbl_note.bezeichnung as note_bezeichnung, tbl_pruefungstyp.beschreibung as typ_beschreibung, tbl_lehreinheit.studiensemester_kurzbz as studiensemester_kurzbz
FROM lehre.tbl_pruefung, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, lehre.tbl_note, lehre.tbl_pruefungstyp
WHERE tbl_pruefung.lehreinheit_id=tbl_lehreinheit.lehreinheit_id
AND tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id
AND tbl_pruefung.note = tbl_note.note
AND tbl_pruefung.pruefungstyp_kurzbz=tbl_pruefungstyp.pruefungstyp_kurzbz
AND tbl_lehrveranstaltung.lehrveranstaltung_id = ?
AND tbl_lehreinheit.studiensemester_kurzbz = ?
ORDER BY datum DESC;";
return $this->execReadOnlyQuery($qry, array($lv_id, $sem_kurzbz));
}
public function getPruefungenByUidTypLvStudiensemester($uid, $typ = null, $lv_id = null, $sem_kurzbz = null) {
$params = [$uid];
$qry = "SELECT tbl_pruefung.*, tbl_lehrveranstaltung.bezeichnung as lehrveranstaltung_bezeichnung, tbl_lehrveranstaltung.lehrveranstaltung_id,
tbl_note.bezeichnung as note_bezeichnung, tbl_pruefungstyp.beschreibung as typ_beschreibung, tbl_lehreinheit.studiensemester_kurzbz as studiensemester_kurzbz
FROM lehre.tbl_pruefung, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, lehre.tbl_note, lehre.tbl_pruefungstyp
WHERE student_uid= ?
AND tbl_pruefung.lehreinheit_id=tbl_lehreinheit.lehreinheit_id
AND tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id
AND tbl_pruefung.note = tbl_note.note
AND tbl_pruefung.pruefungstyp_kurzbz=tbl_pruefungstyp.pruefungstyp_kurzbz";
if ($typ != null)
{
$qry .= " AND tbl_pruefungstyp.pruefungstyp_kurzbz = ?";
$params[] = $typ;
}
if ($lv_id != null)
{
$qry .= " AND tbl_lehrveranstaltung.lehrveranstaltung_id = ?";
$params[] = $lv_id;
}
if ($sem_kurzbz != null)
{
$qry .= " AND tbl_lehreinheit.studiensemester_kurzbz = ?";
$params[] = $sem_kurzbz;
}
$qry .= " ORDER BY datum DESC";
return $this->execReadOnlyQuery($qry, $params);
}
}
@@ -314,6 +314,28 @@ EOSQL;
return $this->execQuery($query, $params);
}
public function getAllLehreinheitenForLvaAndMaUid($lva_id, $ma_uid, $sem_kurzbz)
{
$query = "SELECT DISTINCT tbl_lehreinheitmitarbeiter.lehreinheit_id, tbl_lehreinheit.lehrveranstaltung_id, tbl_lehreinheit.lehrform_kurzbz,
tbl_lehreinheitmitarbeiter.mitarbeiter_uid,
tbl_lehreinheitgruppe.semester,
tbl_lehreinheitgruppe.verband,
tbl_lehreinheitgruppe.gruppe,
tbl_lehreinheitgruppe.gruppe_kurzbz,
tbl_lehrveranstaltung.kurzbz,
tbl_studiengang.kurzbzlang,
(SELECT COUNT(DISTINCT datum) FROM campus.vw_stundenplan WHERE lehreinheit_id = lehre.tbl_lehreinheit.lehreinheit_id) as termincount,
(SELECT COUNT(*) FROM campus.vw_student_lehrveranstaltung WHERE lehreinheit_id = lehre.tbl_lehreinheit.lehreinheit_id) as studentcount
FROM lehre.tbl_lehreinheit JOIN lehre.tbl_lehreinheitmitarbeiter USING(lehreinheit_id)
JOIN lehre.tbl_lehreinheitgruppe USING(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang ON (tbl_lehreinheitgruppe.studiengang_kz = tbl_studiengang.studiengang_kz)
WHERE lehrveranstaltung_id = ? AND studiensemester_kurzbz = ? AND mitarbeiter_uid = ?
ORDER BY tbl_lehreinheitgruppe.gruppe_kurzbz";
return $this->execQuery($query, [$lva_id, $sem_kurzbz, $ma_uid]);
}
public function getOes($lehreinheit_id)
{
@@ -341,7 +341,7 @@ class Lehreinheitgruppe_model extends DB_Model
$this->db->where('lehreinheit_id', $lehreinheit_id);
$this->db->where('studiengang_kz', $gruppen_array->studiengang_kz);
if (!isEmptyString($gruppen_array->semester))
if (!isEmptyString((string)$gruppen_array->semester))
{
$this->db->where('semester', $gruppen_array->semester);
}
@@ -444,30 +444,37 @@ class Lehreinheitgruppe_model extends DB_Model
)
ELSE tbl_gruppe.beschreibung
END AS beschreibung");
$this->addSelect("CASE
WHEN tbl_lehreinheitgruppe.gruppe_kurzbz IS NULL THEN
(
SELECT EXISTS (
SELECT 1
FROM lehre.tbl_stundenplandev
WHERE lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id
AND studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz
AND semester = tbl_lehreinheitgruppe.semester
AND TRIM(COALESCE(verband, '')) = TRIM(tbl_lehreinheitgruppe.verband)
AND TRIM(COALESCE(gruppe, '')) = TRIM(tbl_lehreinheitgruppe.gruppe)
AND (gruppe_kurzbz IS NULL OR gruppe_kurzbz = '')
)
)
ELSE
$this->addSelect("
CASE
WHEN trim(COALESCE(tbl_lehreinheitgruppe.gruppe_kurzbz, '')) = '' THEN
(
SELECT EXISTS (
SELECT 1
FROM lehre.tbl_stundenplandev
WHERE lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id
AND gruppe_kurzbz = tbl_lehreinheitgruppe.gruppe_kurzbz
FROM lehre.tbl_stundenplandev sp
WHERE sp.lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id
AND sp.studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz
AND sp.semester = tbl_lehreinheitgruppe.semester
AND trim(COALESCE(sp.verband, '')) = trim(COALESCE(tbl_lehreinheitgruppe.verband, ''))
AND trim(COALESCE(sp.gruppe, '')) = trim(COALESCE(tbl_lehreinheitgruppe.gruppe, ''))
AND trim(COALESCE(sp.gruppe_kurzbz, '')) = ''
)
)
END AS verplant");
ELSE
(
SELECT EXISTS (
SELECT 1
FROM lehre.tbl_stundenplandev sp
WHERE sp.lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id
AND sp.studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz
AND sp.semester = tbl_lehreinheitgruppe.semester
AND trim(COALESCE(sp.verband, '')) = trim(COALESCE(tbl_lehreinheitgruppe.verband, ''))
AND trim(COALESCE(sp.gruppe, '')) = trim(COALESCE(tbl_lehreinheitgruppe.gruppe, ''))
AND trim(COALESCE(sp.gruppe_kurzbz, '')) = trim(COALESCE(tbl_lehreinheitgruppe.gruppe_kurzbz, ''))
)
)
END AS verplant
");
$this->addJoin('tbl_studiengang', 'studiengang_kz', 'LEFT');
$this->addJoin('public.tbl_gruppe', 'gruppe_kurzbz', 'LEFT');
@@ -317,7 +317,9 @@ class Lehrveranstaltung_model extends DB_Model
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,
tbl_student.prestudent_id
tbl_student.prestudent_id,
campus.vw_student_lehrveranstaltung.lehreinheit_id
FROM
campus.vw_student_lehrveranstaltung
JOIN public.tbl_benutzer USING(uid)
@@ -1054,6 +1056,26 @@ class Lehrveranstaltung_model extends DB_Model
return $this->execQuery($qry, $params);
}
public function getLvForLektorInSemester($sem_kurzbz, $uid) {
$qry = "SELECT DISTINCT (tbl_lehrveranstaltung.lehrveranstaltung_id),
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as stg_kurzbz,
tbl_lehrveranstaltung.semester as lv_semester,
tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung,
(SELECT kurzbz FROM public.tbl_mitarbeiter
WHERE mitarbeiter_uid=tbl_lehreinheitmitarbeiter.mitarbeiter_uid) as lektor
FROM
lehre.tbl_lehreinheit JOIN lehre.tbl_lehreinheitmitarbeiter USING(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang USING(studiengang_kz)
JOIN lehre.tbl_lehrveranstaltung as lehrfach ON(tbl_lehreinheit.lehrfach_id=lehrfach.lehrveranstaltung_id)
WHERE
tbl_lehreinheit.studiensemester_kurzbz = ?
AND mitarbeiter_uid = ?
ORDER BY stg_kurzbz,lv_semester,lv_bezeichnung";
return $this->execReadOnlyQuery($qry, array($sem_kurzbz, $uid));
}
public function getLvsByOrganization($oe_kurzbz)
{
$qry="
@@ -1245,7 +1267,7 @@ class Lehrveranstaltung_model extends DB_Model
{
return "
SELECT
lehrveranstaltung_id, tbl_lehrveranstaltung.kurzbz as lv_kurzbz, tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung, bezeichnung_english as lv_bezeichnung_english, studiengang_kz,
distinct on (lehrveranstaltung_id) lehrveranstaltung_id, tbl_lehrveranstaltung.kurzbz as lv_kurzbz, tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung, bezeichnung_english as lv_bezeichnung_english, studiengang_kz,
tbl_studienplan_lehrveranstaltung.semester, tbl_lehrveranstaltung.sprache,
ects as lv_ects, semesterstunden, anmerkung, lehre, lehreverzeichnis as lv_lehreverzeichnis, tbl_lehrveranstaltung.aktiv,
planfaktor as lv_planfaktor, planlektoren as lv_planlektoren, planpersonalkosten as lv_planpersonalkosten,
@@ -29,10 +29,14 @@ class Lvgesamtnote_model extends DB_Model
$this->addSelect("lv.bezeichnung AS lehrveranstaltung_bezeichnung");
$this->addSelect("lv.studiengang_kz");
$this->addSelect("UPPER(stg.typ || stg.kurzbz) AS studiengang");
$this->addSelect("person.vorname");
$this->addSelect("person.nachname");
$this->addJoin("lehre.tbl_note n", "note");
$this->addJoin("lehre.tbl_lehrveranstaltung lv", "lehrveranstaltung_id");
$this->addJoin("public.tbl_studiengang stg", "studiengang_kz");
$this->addJoin("public.tbl_benutzer benutzer", "uid = student_uid", "LEFT");
$this->addJoin("public.tbl_person person", "person_id", "LEFT");
$this->db->where($this->dbTable . ".freigabedatum <", "NOW()", false);
@@ -79,10 +79,10 @@ class Paabgabe_model extends DB_Model
JOIN public.tbl_benutzer ON (public.tbl_benutzer.uid = student_uid)
JOIN public.tbl_person USING (person_id)
WHERE (campus.tbl_paabgabe.insertamum::date = CURRENT_DATE - INTERVAL ?
OR campus.tbl_paabgabe.updateamum::date = CURRENT_DATE - INTERVAL ?)
AND campus.tbl_paabgabe.paabgabetyp_kurzbz IN ?";
WHERE (campus.tbl_paabgabe.insertamum >= NOW() - INTERVAL ?
OR campus.tbl_paabgabe.updateamum >= NOW() - INTERVAL ?)
AND campus.tbl_paabgabe.paabgabetyp_kurzbz IN ?";
return $this->execQuery($query, [$interval, $interval, $relevantTypes]);
}
@@ -108,7 +108,7 @@ class Paabgabe_model extends DB_Model
JOIN public.tbl_person ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
WHERE campus.tbl_paabgabe.abgabedatum IS NOT NULL
AND campus.tbl_paabgabe.abgabedatum = CURRENT_DATE - INTERVAL ?";
AND campus.tbl_paabgabe.abgabedatum >= NOW() - INTERVAL ?";
if($relevantTypes !== null) {
$query .= " AND campus.tbl_paabgabe.paabgabetyp_kurzbz IN ?";
@@ -149,6 +149,8 @@ class Projektarbeit_model extends DB_Model
lehre.tbl_projektarbeit.abstract as abstract,
lehre.tbl_projektarbeit.abstract_en as abstract_en,
lehre.tbl_projektarbeit.insertamum as insertamum,
(SELECT abgeschicktvon FROM extension.tbl_projektarbeitsbeurteilung WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id AND betreuer_person_id = tbl_projektbetreuer.person_id) AS babgeschickt,
(SELECT abgeschicktvon FROM extension.tbl_projektarbeitsbeurteilung WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter') LIMIT 1) AS zweitbetreuer_abgeschickt,
(SELECT datum FROM campus.tbl_paabgabe WHERE paabgabetyp_kurzbz = 'end' AND abgabedatum IS NOT NULL AND projektarbeit_id = tbl_projektarbeit.projektarbeit_id LIMIT 1) AS abgegeben
FROM lehre.tbl_projektarbeit
@@ -167,7 +169,7 @@ class Projektarbeit_model extends DB_Model
(projekttyp_kurzbz='Bachelor' OR projekttyp_kurzbz='Diplom')
AND betreuerart_kurzbz IN ('Betreuer', 'Begutachter', 'Erstbegutachter', 'Senatsvorsitz')) as base
ORDER BY insertamum DESC";
return $this->execReadOnlyQuery($betreuerQuery, array($studentUID));
}
@@ -216,13 +218,16 @@ class Projektarbeit_model extends DB_Model
}
public function getProjektbetreuerAnrede($bperson_id) {
$qry_betr="SELECT DISTINCT ON(public.tbl_person.person_id) trim(COALESCE(titelpre,'')||' '||COALESCE(vorname,'')||' '||COALESCE(nachname,'')||' '||COALESCE(titelpost,'')) as first, anrede
FROM public.tbl_person JOIN lehre.tbl_projektbetreuer ON(lehre.tbl_projektbetreuer.person_id=public.tbl_person.person_id)
WHERE public.tbl_person.person_id= ?";
$qry_betr="SELECT distinct trim(COALESCE(titelpre,'')||' '||COALESCE(vorname,'')||' '||COALESCE(nachname,'')||' '||COALESCE(titelpost,'')) as first,
public.tbl_mitarbeiter.mitarbeiter_uid, anrede
FROM public.tbl_person JOIN lehre.tbl_projektbetreuer ON(lehre.tbl_projektbetreuer.person_id=public.tbl_person.person_id)
JOIN public.tbl_benutzer ON(public.tbl_benutzer.person_id=public.tbl_person.person_id)
JOIN public.tbl_mitarbeiter ON(public.tbl_benutzer.uid=public.tbl_mitarbeiter.mitarbeiter_uid)
WHERE public.tbl_person.person_id= ?";
return $this->execReadOnlyQuery($qry_betr, [$bperson_id]);
}
public function getProjektbetreuerEmail($projektarbeit_id) {
$qry = "SELECT (
SELECT kontakt
@@ -244,7 +249,7 @@ class Projektarbeit_model extends DB_Model
return $this->execReadOnlyQuery($qry, [$projektarbeit_id]);
}
public function getProjektbetreuerEmailByPersonID($person_id) {
$qry = "SELECT (
SELECT kontakt
@@ -338,139 +343,298 @@ class Projektarbeit_model extends DB_Model
return $this->execReadOnlyQuery($qry, array($projektarbeit_id));
}
public function getProjektarbeitenForStudiengang($studiengang_kz, $benotet) {
$new_qry = "WITH secondary_betreuer AS (
SELECT DISTINCT ON (pb.projektarbeit_id)
pb.projektarbeit_id,
pb.person_id AS zweitbetreuer_person_id,
pb.betreuerart_kurzbz AS zweitbetreuer_betreuerart_kurzbz,
ba.beschreibung AS zweitbetreuer_betreuerart_beschreibung,
p.titelpre AS zweitbetreuer_titelpre,
p.vorname AS zweitbetreuer_vorname,
p.nachname AS zweitbetreuer_nachname,
p.titelpost AS zweitbetreuer_titelpost,
trim(
COALESCE(p.titelpre, '') || ' ' ||
COALESCE(p.vorname, '') || ' ' ||
COALESCE(p.nachname, '') || ' ' ||
COALESCE(p.titelpost, '')
) AS zweitbetreuer_full_name
FROM lehre.tbl_projektbetreuer pb
JOIN public.tbl_person p ON p.person_id = pb.person_id
LEFT JOIN public.tbl_benutzer b ON b.person_id = p.person_id
LEFT JOIN lehre.tbl_betreuerart ba ON ba.betreuerart_kurzbz = pb.betreuerart_kurzbz
WHERE pb.betreuerart_kurzbz = ANY('{Zweitbetreuer,Zweitbegutachter,Senatsmitglied}')
ORDER BY pb.projektarbeit_id -- DISTINCT ON needs this to be deterministic
)
SELECT DISTINCT ON (tmp.projektarbeit_id)
*,
campus.get_betreuer_details(tmp.zweitbetreuer_person_id) AS zweitbetreuer_full_name,
campus.get_betreuer_details(tmp.betreuer_person_id) AS erstbetreuer_full_name
FROM (
SELECT DISTINCT ON (tbl_projektarbeit.projektarbeit_id)
tbl_projektarbeit.projekttyp_kurzbz,
tbl_projektarbeit.titel,
tbl_projektarbeit.projektarbeit_id,
tbl_studiengang.typ,
tbl_studiengang.kurzbz,
student_benutzer.uid AS student_uid,
student_person.vorname AS student_vorname,
student_person.nachname AS student_nachname,
public.tbl_student.matrikelnr,
tbl_lehreinheit.studiensemester_kurzbz,
betreuer_benutzer.uid AS betreuer_benutzer_uid,
betreuer_person.titelpre AS betreuer_titelpre,
betreuer_person.vorname AS betreuer_vorname,
betreuer_person.nachname AS betreuer_nachname,
betreuer_person.titelpost AS betreuer_titelpost,
lehre.tbl_projektbetreuer.betreuerart_kurzbz AS betreuerart,
lehre.tbl_projektbetreuer.person_id AS betreuer_person_id,
lehre.tbl_projektarbeit.sprache,
lehre.tbl_projektarbeit.seitenanzahl,
lehre.tbl_projektarbeit.kontrollschlagwoerter,
lehre.tbl_projektarbeit.schlagwoerter,
lehre.tbl_projektarbeit.schlagwoerter_en,
lehre.tbl_projektarbeit.abstract,
lehre.tbl_projektarbeit.abstract_en,
lehre.tbl_projektarbeit.insertamum,
lehre.tbl_projektarbeit.note,
sb.zweitbetreuer_person_id,
sb.zweitbetreuer_betreuerart_kurzbz,
sb.zweitbetreuer_betreuerart_beschreibung,
sb.zweitbetreuer_full_name,
sb.zweitbetreuer_titelpre,
sb.zweitbetreuer_vorname,
sb.zweitbetreuer_nachname,
sb.zweitbetreuer_titelpost,
(
SELECT orgform_kurzbz
FROM public.tbl_prestudentstatus
WHERE prestudent_id = (
SELECT prestudent_id FROM public.tbl_student
WHERE student_uid = student_benutzer.uid LIMIT 1
)
ORDER BY datum DESC, insertamum DESC, ext_id DESC
LIMIT 1
) AS organisationsform,
(
SELECT COALESCE(tbl_studienplan.orgform_kurzbz,
tbl_prestudentstatus.orgform_kurzbz,
tbl_studiengang.orgform_kurzbz)
FROM public.tbl_prestudent
JOIN public.tbl_prestudentstatus USING (prestudent_id)
JOIN public.tbl_studiensemester USING (studiensemester_kurzbz)
JOIN public.tbl_studiengang USING (studiengang_kz)
LEFT JOIN lehre.tbl_studienplan USING (studienplan_id)
WHERE prestudent_id = public.tbl_student.prestudent_id
ORDER BY tbl_prestudentstatus.datum DESC
LIMIT 1
) AS orgform,
(
SELECT status_kurzbz
FROM public.tbl_prestudentstatus
WHERE prestudent_id = public.tbl_student.prestudent_id
ORDER BY datum DESC, insertamum DESC, ext_id DESC
LIMIT 1
) AS studienstatus
FROM lehre.tbl_projektarbeit
LEFT JOIN public.tbl_benutzer student_benutzer ON student_benutzer.uid = lehre.tbl_projektarbeit.student_uid
LEFT JOIN public.tbl_person student_person ON student_benutzer.person_id = student_person.person_id
LEFT JOIN public.tbl_student ON student_benutzer.uid = public.tbl_student.student_uid
LEFT JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang ON public.tbl_student.studiengang_kz = public.tbl_studiengang.studiengang_kz
LEFT JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
LEFT JOIN lehre.tbl_projektbetreuer USING (projektarbeit_id)
LEFT JOIN public.tbl_person betreuer_person ON betreuer_person.person_id = lehre.tbl_projektbetreuer.person_id
LEFT JOIN public.tbl_benutzer betreuer_benutzer ON betreuer_person.person_id = betreuer_benutzer.person_id
LEFT JOIN secondary_betreuer sb ON sb.projektarbeit_id = tbl_projektarbeit.projektarbeit_id -- ← THE NEW LINE
WHERE (projekttyp_kurzbz = 'Bachelor' OR projekttyp_kurzbz = 'Diplom')
AND student_benutzer.aktiv
AND lehre.tbl_projektbetreuer.betreuerart_kurzbz IN (
'Erstbegutachter', 'Begutachter', 'Betreuer', 'Erstbetreuer', 'Senatsvorsitz'
)
AND public.tbl_studiengang.studiengang_kz = ?";
if($benotet == 0) {
$new_qry .= " AND lehre.tbl_projektarbeit.note IS NULL ";
} else if ($benotet == 1) {
$new_qry .= " AND lehre.tbl_projektarbeit.note IS NOT NULL ";
}
$new_qry .= " ORDER BY tbl_projektarbeit.projektarbeit_id DESC, student_person.nachname ASC
$new_qry = "SELECT DISTINCT ON(tmp.projektarbeit_id) *, campus.get_betreuer_details(tmp.zweitbetreuer_person_id) as zweitbetreuer_full_name, campus.get_betreuer_details(tmp.betreuer_person_id) as erstbetreuer_full_name
FROM(
SELECT
DISTINCT ON(tbl_projektarbeit.projektarbeit_id)
tbl_projektarbeit.projekttyp_kurzbz,
tbl_projektarbeit.titel,
tbl_projektarbeit.projektarbeit_id,
tbl_studiengang.typ, tbl_studiengang.kurzbz,
student_benutzer.uid as student_uid,
student_person.vorname as student_vorname,
student_person.nachname as student_nachname,
tbl_student.matrikelnr, tbl_lehreinheit.studiensemester_kurzbz,
betreuer_benutzer.uid as betreuer_benutzer_uid,
betreuer_person.titelpre as betreuer_titelpre,
betreuer_person.vorname as betreuer_vorname,
betreuer_person.nachname as betreuer_nachname,
betreuer_person.titelpost as betreuer_titelpost,
lehre.tbl_projektbetreuer.betreuerart_kurzbz as betreuerart,
lehre.tbl_projektbetreuer.person_id as betreuer_person_id,
lehre.tbl_projektarbeit.sprache as sprache,
lehre.tbl_projektarbeit.seitenanzahl as seitenanzahl,
lehre.tbl_projektarbeit.kontrollschlagwoerter as kontrollschlagwoerter,
lehre.tbl_projektarbeit.schlagwoerter as schlagwoerter,
lehre.tbl_projektarbeit.schlagwoerter_en as schlagwoerter_en,
lehre.tbl_projektarbeit.abstract as abstract,
lehre.tbl_projektarbeit.abstract_en as abstract_en,
lehre.tbl_projektarbeit.insertamum as insertamum,
lehre.tbl_projektarbeit.note as note,
(
SELECT orgform_kurzbz
FROM tbl_prestudentstatus
WHERE prestudent_id = (SELECT prestudent_id
FROM tbl_student
WHERE student_uid = student_benutzer.uid
LIMIT 1)
ORDER BY datum DESC, insertamum DESC, ext_id DESC
LIMIT 1
)
as organisationsform,
(
SELECT person_id
FROM lehre.tbl_projektbetreuer
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
AS zweitbetreuer_person_id,
(
SELECT betreuerart_kurzbz
FROM lehre.tbl_projektbetreuer
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
AS zweitbetreuer_betreuerart_kurzbz,
(
SELECT tbl_betreuerart.beschreibung
FROM lehre.tbl_projektbetreuer
JOIN lehre.tbl_betreuerart USING (betreuerart_kurzbz)
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
AS zweitbetreuer_betreuerart_beschreibung,
(
SELECT trim(COALESCE(titelpre, '') || ' ' || COALESCE(vorname, '') || ' ' || COALESCE(nachname, '') || ' ' ||
COALESCE(titelpost, ''))
FROM public.tbl_person
JOIN lehre.tbl_projektbetreuer ON (lehre.tbl_projektbetreuer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_benutzer ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid)
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
as zweitbetreuer_full_name,
(
SELECT titelpre
FROM public.tbl_person
JOIN lehre.tbl_projektbetreuer ON (lehre.tbl_projektbetreuer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_benutzer ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid)
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
as zweitbetreuer_titelpre,
(
SELECT vorname
FROM public.tbl_person
JOIN lehre.tbl_projektbetreuer ON (lehre.tbl_projektbetreuer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_benutzer ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid)
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
as zweitbetreuer_vorname,
(
SELECT nachname
FROM public.tbl_person
JOIN lehre.tbl_projektbetreuer ON (lehre.tbl_projektbetreuer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_benutzer ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid)
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
as zweitbetreuer_nachname,
(
SELECT titelpost
FROM public.tbl_person
JOIN lehre.tbl_projektbetreuer ON (lehre.tbl_projektbetreuer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_benutzer ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid)
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
as zweitbetreuer_titelpost,
(
SELECT
COALESCE(tbl_studienplan.orgform_kurzbz,
tbl_prestudentstatus.orgform_kurzbz, tbl_studiengang.orgform_kurzbz) as
orgform
FROM
public.tbl_prestudent
JOIN public.tbl_prestudentstatus USING(prestudent_id)
JOIN public.tbl_studiensemester USING(studiensemester_kurzbz)
JOIN public.tbl_studiengang USING(studiengang_kz)
LEFT JOIN lehre.tbl_studienplan USING(studienplan_id)
WHERE
prestudent_id=tbl_student.prestudent_id
ORDER BY tbl_prestudentstatus.datum DESC LIMIT 1
) as orgform,
(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 studienstatus
FROM lehre.tbl_projektarbeit
LEFT JOIN public.tbl_benutzer student_benutzer ON (student_benutzer.uid = lehre.tbl_projektarbeit.student_uid)
LEFT JOIN public.tbl_person student_person ON (student_benutzer.person_id = student_person.person_id)
LEFT JOIN public.tbl_student on(student_benutzer.uid = public.tbl_student.student_uid)
LEFT JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang ON (public.tbl_student.studiengang_kz = public.tbl_studiengang.studiengang_kz)
LEFT JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
LEFT JOIN lehre.tbl_projektbetreuer USING (projektarbeit_id)
LEFT JOIN public.tbl_person betreuer_person ON (betreuer_person.person_id = lehre.tbl_projektbetreuer.person_id)
LEFT JOIN public.tbl_benutzer betreuer_benutzer ON (betreuer_person.person_id = betreuer_benutzer.person_id)
WHERE (projekttyp_kurzbz = 'Bachelor' OR projekttyp_kurzbz = 'Diplom')
AND student_benutzer.aktiv AND (
lehre.tbl_projektbetreuer.betreuerart_kurzbz = 'Erstbegutachter'
OR lehre.tbl_projektbetreuer.betreuerart_kurzbz = 'Begutachter'
OR lehre.tbl_projektbetreuer.betreuerart_kurzbz = 'Betreuer'
OR lehre.tbl_projektbetreuer.betreuerart_kurzbz = 'Erstbetreuer'
OR lehre.tbl_projektbetreuer.betreuerart_kurzbz = 'Senatsvorsitz'
)
AND public.tbl_studiengang.studiengang_kz = ?";
if($benotet == 0) {
$new_qry .= " AND lehre.tbl_projektarbeit.note IS NULL ";
} else if ($benotet == 1) {
$new_qry .= " AND lehre.tbl_projektarbeit.note IS NOT NULL ";
}
$new_qry .= " ORDER BY tbl_projektarbeit.projektarbeit_id DESC, student_person.nachname ASC
) as tmp";
return $this->execReadOnlyQuery($new_qry, array($studiengang_kz));
}
/**
* Prüft ob Projektarbeit aktuell ist (also zurzeit online bewertet wird).
* @param $projektarbeit_id
* @return boolean
*/
public function projektarbeitIsCurrent($projektarbeit_id) {
$version = $this->getVersion($projektarbeit_id);
// paarbeit sollte nur ab einem Studiensemester online bewertet werden
return $version === null ? null : $version->isCurrent;
}
/**
* Holt sich Version der Projektarbeit.
* Liefert auch mit, ob die Version die aktuellste ist.
* z.B.: Masterarbeiten waren ab der Änderung zur Gewichtung der Punkte aktuell,
* Bachelorarbeiten waren ab dem Umstieg auf das Online Beurteilungsformular aktuell.
* @param $projektarbeit_id
* @return objekt mit Versionsinfo, null im Fehlerfall
*/
private function getVersion($projektarbeit_id) {
$_versions_query = array(
'Diplom' => array(
'SS2025',
'SS2023',
'SS2022'
),
'Others' => array(
'SS2025',
'SS2022',
)
);
$_versions_check = array(
'Diplom' => array(
'SS2025' => 3,
'SS2023' => 2,
'SS2022' => 1
),
'Others' => array(
'SS2025' => 2,
'SS2022' => 1
)
);
// paarbeit sollte nur ab einem Studiensemester online bewertet werden
$qry="
SELECT
CASE
WHEN semesters_diplom.studiensemester_kurzbz IS NOT NULL
THEN semesters_diplom.studiensemester_kurzbz
ELSE semesters.studiensemester_kurzbz
END AS version_studiensemester_kurzbz,
pa.projekttyp_kurzbz
FROM
lehre.tbl_projektarbeit pa
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
JOIN public.tbl_studiensemester sem USING(studiensemester_kurzbz)
LEFT JOIN (
SELECT
start, studiensemester_kurzbz
FROM
public.tbl_studiensemester
WHERE
studiensemester_kurzbz IN ?
) semesters ON sem.start >= semesters.start AND pa.projekttyp_kurzbz <> 'Diplom'
LEFT JOIN (
SELECT
start, studiensemester_kurzbz
FROM
public.tbl_studiensemester
WHERE
studiensemester_kurzbz IN ?
) semesters_diplom ON sem.start >= semesters_diplom.start AND pa.projekttyp_kurzbz = 'Diplom'
WHERE
projektarbeit_id = ?
ORDER BY
semesters.start DESC, semesters_diplom.start DESC
LIMIT 1";
$resultociniBambini = $this->execReadOnlyQuery($qry, array($_versions_query['Others'], $_versions_query['Diplom'], $projektarbeit_id));
if(hasData($resultociniBambini)) {
$data = getData($resultociniBambini);
if(count($data) > 0) {
$row = $data[0];
// known project types
if (isset($_versions_check[$row->projekttyp_kurzbz][$row->version_studiensemester_kurzbz]))
{
$row->versionNumber = $_versions_check[$row->projekttyp_kurzbz][$row->version_studiensemester_kurzbz];
$row->isCurrent =
$_versions_check[$row->projekttyp_kurzbz][$row->version_studiensemester_kurzbz]
== max($_versions_check[$row->projekttyp_kurzbz]);
}
elseif (isset($_versions_check['Others'][$row->version_studiensemester_kurzbz]))
{
$row->versionNumber = $_versions_check['Others'][$row->version_studiensemester_kurzbz];
$row->isCurrent =
$_versions_check['Others'][$row->version_studiensemester_kurzbz]
== max($_versions_check['Others']);
}
else
{
$row->isCurrent = false;
$row->versionNumber = 0;
}
return $row;
} else {
return null;
}
} else {
return null;
}
}
/*
*
* @param
* @return object success or error
@@ -306,4 +306,5 @@ class Pruefung_model extends DB_Model
return $this->loadWhereCommitteeExamsFailed();
}
}
@@ -148,7 +148,7 @@ class Zeugnisnote_model extends DB_Model
*
* @return object
*/
public function getZeugnisnoten($student_uid, $studiensemester_kurzbz)
public function getZeugnisnoten($student_uid, $studiensemester_kurzbz, $lehrveranstaltung_id = null)
{
$params = array();
$where='';
@@ -163,6 +163,11 @@ class Zeugnisnote_model extends DB_Model
$where.=" AND vw_student_lehrveranstaltung.studiensemester_kurzbz= ?";
$params[] = $studiensemester_kurzbz;
}
if($lehrveranstaltung_id != null)
{
$where .= " AND vw_student_lehrveranstaltung.lehrveranstaltung_id = ?";
$params[] = $lehrveranstaltung_id;
}
$where2='';
@@ -176,6 +181,11 @@ class Zeugnisnote_model extends DB_Model
$where2 .= " AND studiensemester_kurzbz= ?";
$params[] = $studiensemester_kurzbz;
}
if($lehrveranstaltung_id != null)
{
$where2 .=" AND lehrveranstaltung_id = ?";
$params[] = $lehrveranstaltung_id;
}
$qry = "SELECT
a.*,
@@ -188,7 +198,10 @@ class Zeugnisnote_model extends DB_Model
lv.semester AS semester_lv,
lv.ects AS ects_lv,
lv.zeugnis,
lv.bezeichnung_english AS lehrveranstaltung_bezeichnung_english
lv.bezeichnung_english AS lehrveranstaltung_bezeichnung_english,
s.verband,
person.vorname,
person.nachname
FROM (
SELECT vw_student_lehrveranstaltung.lehrveranstaltung_id, uid,
vw_student_lehrveranstaltung.studiensemester_kurzbz, note, punkte, uebernahmedatum, benotungsdatum,
@@ -231,6 +244,8 @@ class Zeugnisnote_model extends DB_Model
ORDER BY sort
) a
LEFT JOIN public.tbl_student s ON (a.uid = s.student_uid)
LEFT JOIN public.tbl_benutzer benutzer ON benutzer.uid = s.student_uid
LEFT JOIN public.tbl_person person ON benutzer.person_id = person.person_id
LEFT JOIN public.tbl_studiengang stg1 ON (s.studiengang_kz = stg1.studiengang_kz)
LEFT JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang stg2 ON (lv.studiengang_kz = stg2.studiengang_kz)";
@@ -594,10 +594,7 @@ class Studiengang_model extends DB_Model
$this->addSelect('p.prestudent_id');
$this->addSelect('pers.vorname');
$this->addSelect('pers.nachname');
$this->addSelect("CONCAT(UPPER(pers.nachname), ' ', pers.vorname, ' (', "
. $this->dbTable . ".bezeichnung, ', ', "
. "UPPER(" . $this->dbTable . ".typ), "
. "UPPER(" . $this->dbTable . ".kurzbz),')') AS name");
$this->addSelect("CONCAT(UPPER(pers.nachname), ' ', pers.vorname, ' (', " . $this->dbTable . ".bezeichnung, ')') AS name");
$this->addJoin('public.tbl_prestudent p', 'studiengang_kz');
$this->addJoin(
@@ -59,6 +59,37 @@ class Studienplan_model extends DB_Model
'tbl_studienplan_lehrveranstaltung.semester' => $semester
));
}
public function getStudienplanByLvaSemKurzbz($lehrveranstaltung_id, $studiensemester_kurzbz) {
$qry= "
SELECT
DISTINCT tbl_studienplan.*
FROM
lehre.tbl_studienplan
JOIN lehre.tbl_studienplan_lehrveranstaltung
USING(studienplan_id)
WHERE
tbl_studienplan_lehrveranstaltung.lehrveranstaltung_id IN (
SELECT
lv.lehrveranstaltung_id
FROM
lehre.tbl_lehrveranstaltung AS lv
LEFT JOIN lehre.tbl_lehrveranstaltung AS t ON t.lehrveranstaltung_id=lv.lehrveranstaltung_template_id
WHERE
lv.lehrtyp_kurzbz<>'tpl'
AND (lv.lehrveranstaltung_id= ? OR (lv.lehrveranstaltung_template_id= ? AND t.lehrtyp_kurzbz='tpl'))
)
AND EXISTS (
SELECT 1
FROM
lehre.tbl_studienplan_semester
WHERE studienplan_id=tbl_studienplan.studienplan_id
AND studiensemester_kurzbz= ?
AND semester = tbl_studienplan_lehrveranstaltung.semester)
ORDER BY bezeichnung";
return $this->execReadOnlyQuery($qry, array($lehrveranstaltung_id, $lehrveranstaltung_id, $studiensemester_kurzbz));
}
public function getStudienplanLehrveranstaltungForPrestudent($studienplan_id, $semester, $prestudent_id)
{
@@ -206,6 +206,7 @@ class Notiz_model extends DB_Model
person_bearbeiter.vorname, person_bearbeiter.nachname
";
return $this->execQuery($qry, array($type, $id));
}
@@ -135,4 +135,15 @@ class Reservierung_model extends DB_Model
return $this->execQuery($query, [$uid, $uid]);
}
public function lektorHasReservierung($uid, $datum, $stunde)
{
$qry = "SELECT reservierung_id, uid, stg_kurzbz, ort_kurzbz, semester, verband, gruppe, gruppe_kurzbz, datum, stunde
FROM lehre.vw_reservierung
WHERE uid = ?
AND datum = ?
AND stunde = ?";
return $this->execReadOnlyQuery($qry, [$uid, $datum, $stunde]);
}
}
@@ -333,6 +333,68 @@ class Stundenplan_model extends DB_Model
", [$start_date, $end_date, $lv_id]);
}
public function getStundenplanLE($lehreinheit, $start_date, $end_date, $stundenplan)
{
$qry = "
WITH lehreinheiten AS (
SELECT lehreinheit_id FROM lehre.tbl_lehreinheit WHERE lehreinheit_id = ?
), " . $this->getStundenplanCTE($stundenplan) . "
SELECT *
FROM stundenplanentries
";
return $this->execReadOnlyQuery($qry, array($lehreinheit, $start_date, $end_date));
}
public function getStundenplanLV($lehrveranstaltung_id, $start_date, $end_date, $stundenplan)
{
$qry = "
WITH lehreinheiten AS (
SELECT lehreinheit_id
FROM lehre.tbl_lehreinheit
JOIN tbl_studiensemester USING(studiensemester_kurzbz)
WHERE lehrveranstaltung_id = ?
AND tbl_studiensemester.start >= ? AND tbl_studiensemester.ende <= ?
), " . $this->getStundenplanCTE($stundenplan) . "
SELECT *
FROM stundenplanentries
";
return $this->execReadOnlyQuery($qry, array($lehrveranstaltung_id, $start_date, $end_date, $start_date, $end_date));
}
private function getStundenplanCTE($stundenplan)
{
return "entries AS (
SELECT
datum, min(stunde) as stunde_beginn, max(stunde) as stunde_ende,
array_agg(DISTINCT(
CASE WHEN gruppe_kurzbz is not null THEN gruppe_kurzbz
ELSE (UPPER(stg_typ || stg_kurzbz) || COALESCE(semester,'0') || COALESCE(verband,'') || COALESCE(gruppe,''))
END)) as gruppen_kuerzel,
array_agg(DISTINCT CONCAT(vorname || ' ' || nachname)) as lektorname,
array_agg(DISTINCT stundenplan.ort_kurzbz) as ort_kurzbz,
array_agg(DISTINCT titel) as titel,
lehrfach_bez, stundenplan.lehreinheit_id, lehrveranstaltung_id
FROM lehre.vw_$stundenplan as stundenplan
JOIN public.tbl_mitarbeiter ON stundenplan.uid = tbl_mitarbeiter.mitarbeiter_uid
JOIN tbl_benutzer ON tbl_mitarbeiter.mitarbeiter_uid = tbl_benutzer.uid
JOIN tbl_person USING(person_id)
JOIN lehreinheiten ON stundenplan.lehreinheit_id = lehreinheiten.lehreinheit_id
WHERE datum >= ? AND datum <= ?
GROUP BY datum, unr, stundenplan.lehreinheit_id, lehrveranstaltung_id, lehrfach_bez, lehrfach_bez
ORDER BY datum, min(stunde), unr, lehreinheit_id
),
stundenplanentries AS (
SELECT
entries.*,
stundeb.beginn AS beginn,
stundee.ende AS ende
FROM entries
JOIN lehre.tbl_stunde stundeb ON stundeb.stunde = entries.stunde_beginn
JOIN lehre.tbl_stunde stundee ON stundee.stunde = entries.stunde_ende
)";
}
/**
* queries Stundenplan and filters by assigned ma_kurzbz, very similar to get by LVA
*
@@ -241,4 +241,15 @@ class Stundenplandev_model extends DB_Model
return $this->delete(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid));
}
public function lektorHasStundenplandevEintrag($uid, $datum, $stunde)
{
$qry = "SELECT stundenplandev_id, lektor, stg_kurzbz, ort_kurzbz, semester, verband, gruppe, gruppe_kurzbz, datum, stunde
FROM lehre.vw_stundenplandev
WHERE uid = ?
AND datum = ?
AND stunde = ?";
return $this->execReadOnlyQuery($qry, [$uid, $datum, $stunde]);
}
}
@@ -61,4 +61,40 @@ class Zeitsperre_model extends DB_Model
return $this->execQuery($qry);
}
public function checkIfZeitsperreExists($uid, $datum, $stunde)
{
$this->db->select("*");
$this->db->where('mitarbeiter_uid', $uid);
$this->db->where('zeitsperretyp_kurzbz !=', 'ZVerfueg');
$this->db->group_start();
$this->db->where('vondatum <', $datum);
$this->db->or_group_start();
$this->db->where('vondatum', $datum);
$this->db->group_start();
$this->db->where('vonstunde <=', $stunde);
$this->db->or_where('vonstunde IS NULL', null, false);
$this->db->group_end();
$this->db->group_end();
$this->db->group_end();
$this->db->group_start();
$this->db->where('bisdatum >', $datum);
$this->db->or_group_start();
$this->db->where('bisdatum', $datum);
$this->db->group_start();
$this->db->where('bisstunde >=', $stunde);
$this->db->or_where('bisstunde IS NULL', null, false);
$this->db->group_end();
$this->db->group_end();
$this->db->group_end();
return $this->load();
}
}
+4 -6
View File
@@ -242,7 +242,6 @@ class Message_model extends DB_Model
*/
public function getMessagesForTable($person_id, $offset, $limit)
{
$limitoffset = (!is_null($offset) && !is_null($limit)) ? 'limit ? offset ?' : '';
$sql = <<<EOSQL
with filtered_messages as (
select
@@ -311,12 +310,11 @@ class Message_model extends DB_Model
public.tbl_person pr on pr.person_id = fm.recipient_id
order by
m.insertamum DESC
{$limitoffset}
limit ?
offset ?;
EOSQL;
$parametersArray = $limitoffset
? array($person_id, $person_id, $limit, $offset)
: array($person_id, $person_id);
$parametersArray = array($person_id, $person_id, $limit, $offset);
$count = 0;
$data = $this->execQuery($sql, $parametersArray);
@@ -327,7 +325,7 @@ EOSQL;
$data = getData($data);
if($data)
{
$count = is_null($limit) ? 1 : ceil($data[0]->total_msgs / $limit);
$count = ceil($data[0]->total_msgs / $limit);
}
return success(['data' => $data, 'count' => $count]);
@@ -1,11 +0,0 @@
<?php
class VertragsbestandteilLohnguide_model extends DB_Model
{
public function __construct()
{
parent::__construct();
$this->dbTable = 'hr.tbl_vertragsbestandteil_lohnguide';
$this->pk = 'vertragsbestandteil_id';
}
}
@@ -37,8 +37,7 @@ class Vertragsbestandteil_model extends DB_Model
kf.arbeitgeber_frist, kf.arbeitnehmer_frist,
s.wochenstunden, s.teilzeittyp_kurzbz,
u.tage,
z.zeitaufzeichnung, z.azgrelevant, z.homeoffice,
lg.stellenbezeichnung, lg.vordienstzeit, lg.fachrichtung_kurzbz, lg.modellstelle_kurzbz, lg.kommentar_person, lg.kommentar_modellstelle
z.zeitaufzeichnung, z.azgrelevant, z.homeoffice
FROM
hr.tbl_vertragsbestandteil v
LEFT JOIN
@@ -64,8 +63,6 @@ class Vertragsbestandteil_model extends DB_Model
hr.tbl_vertragsbestandteil_urlaubsanspruch u USING(vertragsbestandteil_id)
LEFT JOIN
hr.tbl_vertragsbestandteil_zeitaufzeichnung z USING(vertragsbestandteil_id)
LEFT JOIN
hr.tbl_vertragsbestandteil_lohnguide lg USING(vertragsbestandteil_id)
EOSQL;
return $sql;
}
+1 -1
View File
@@ -38,7 +38,7 @@ $includesArray = array(
$this->load->view('templates/FHC-Header', $includesArray);
?>
<div id="abgabetoolroot" class="h-100" style="max-width: 99%" route=<?php echo json_encode($route) ?>
<div id="abgabetoolroot" class="h-100" style="max-width: 95%;" route=<?php echo json_encode($route) ?>
uid=<?php echo $uid ?>
student_uid_prop="<?php echo $student_uid_prop ?? '' ?>"
stg_kz_prop="<?php echo $stg_kz_prop ?? '' ?>"
@@ -11,6 +11,7 @@ $includesArray = array(
'skipID' => '#fhccontent',
'vuedatepicker11' => true,
'customCSSs' => array(
'vendor/vuejs/vuedatepicker_css/main.css',
'public/css/components/verticalsplit.css',
'public/css/components/searchbar/searchbar.css',
'public/css/Fhc.css',
@@ -32,14 +33,17 @@ $includesArray = array(
'vendor/npm-asset/primevue/inputnumber/inputnumber.min.js',
'vendor/npm-asset/primevue/speeddial/speeddial.min.js',
'vendor/npm-asset/primevue/textarea/textarea.min.js',
'vendor/moment/luxonjs/luxon.min.js',
'vendor/npm-asset/primevue/password/password.min.js',
'vendor/npm-asset/primevue/multiselect/multiselect.min.js',
'vendor/npm-asset/primevue/timeline/timeline.min.js',
'vendor/npm-asset/primevue/inplace/inplace.min.js',
'vendor/npm-asset/primevue/message/message.min.js',
'vendor/npm-asset/primevue/tieredmenu/tieredmenu.js',
'vendor/moment/luxonjs/luxon.min.js'
),
'customJSModules' => array(
'public/js/apps/Dashboard/Fhc.js',
'vendor/olifolkerd/tabulator5/src/js/modules/ColumnCalcs/ColumnCalcs.js'
),
);
+2
View File
@@ -31,6 +31,8 @@
lv-root="<?= site_url('LVVerwaltung'); ?>"
:permissions="<?= htmlspecialchars(json_encode($permissions));?>"
:config="<?= htmlspecialchars(json_encode($configs)); ?>"
avatar-url="<?= site_url('Cis/Pub/bild/person/' . getAuthPersonId()); ?>"
logout-url="<?= site_url('Cis/Auth/logout'); ?>"
>
</router-view>
@@ -315,22 +315,15 @@
WHERE tpl.app = '.$APP.'
) pl USING(person_id)
LEFT JOIN (
SELECT
SELECT DISTINCT ON (tbl_rueckstellung.person_id)
tbl_rueckstellung.person_id,
tbl_rueckstellung.datum_bis,
tbl_rueckstellung.status_kurzbz,
array_to_json(bezeichnung_mehrsprachig::varchar[])->>0 as bezeichnung
FROM public.tbl_rueckstellung
JOIN public.tbl_rueckstellung_status USING(status_kurzbz)
JOIN public.tbl_person sp ON tbl_rueckstellung.person_id = sp.person_id
WHERE tbl_rueckstellung.rueckstellung_id =
(
SELECT srueck.rueckstellung_id
FROM public.tbl_rueckstellung srueck
WHERE srueck.person_id = tbl_rueckstellung.person_id
AND datum_bis >= NOW()
ORDER BY srueck.datum_bis DESC LIMIT 1
)
WHERE tbl_rueckstellung.datum_bis >= NOW()
ORDER BY tbl_rueckstellung.person_id, tbl_rueckstellung.datum_bis DESC
) rueck ON rueck.person_id = p.person_id
WHERE
EXISTS (
@@ -24,22 +24,15 @@ $query = '
WHERE tpl.app = '.$APP.'
) pl ON p.person_id = pl.person_id
LEFT JOIN (
SELECT
SELECT DISTINCT ON (tbl_rueckstellung.person_id)
tbl_rueckstellung.person_id,
tbl_rueckstellung.datum_bis,
tbl_rueckstellung.status_kurzbz,
array_to_json(bezeichnung_mehrsprachig::varchar[])->>0 as bezeichnung
FROM public.tbl_rueckstellung
JOIN public.tbl_rueckstellung_status USING(status_kurzbz)
JOIN public.tbl_person sp ON tbl_rueckstellung.person_id = sp.person_id
WHERE tbl_rueckstellung.rueckstellung_id =
(
SELECT srueck.rueckstellung_id
FROM public.tbl_rueckstellung srueck
WHERE srueck.person_id = tbl_rueckstellung.person_id
AND datum_bis >= NOW()
ORDER BY srueck.datum_bis DESC LIMIT 1
)
JOIN public.tbl_rueckstellung_status USING(status_kurzbz)
WHERE tbl_rueckstellung.datum_bis >= NOW()
ORDER BY tbl_rueckstellung.person_id, tbl_rueckstellung.datum_bis DESC
) rueck ON rueck.person_id = p.person_id
WHERE p.person_id NOT IN (SELECT person_id FROM public.tbl_prestudent)';
+5 -5
View File
@@ -41,11 +41,11 @@ ob_start();
<link href="../skin/style.css.php" rel="stylesheet" type="text/css">
<title>Menu</title>
<link href="../skin/flexcrollstyles.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" type="text/css" href="../skin/jquery-ui-1.9.2.custom.min.css">
<script type="text/javascript" src="../vendor/jquery/jquery1/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="../vendor/christianbach/tablesorter/jquery.tablesorter.min.js"></script>
<script type="text/javascript" src="../vendor/components/jqueryui/jquery-ui.min.js"></script>
<script type="text/javascript" src="../include/js/jquery.ui.datepicker.translation.js"></script>
<link rel="stylesheet" type="text/css" href="../skin/jquery-ui-1.9.2.custom.min.css">
<script type="text/javascript" src="../vendor/jquery/jquery1/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="../vendor/christianbach/tablesorter/jquery.tablesorter.min.js"></script>
<script type="text/javascript" src="../vendor/components/jqueryui/jquery-ui.min.js"></script>
<script type="text/javascript" src="../include/js/jquery.ui.datepicker.translation.js"></script>
<script type="text/javascript" src="../vendor/jquery/sizzle/sizzle.js"></script>
<script type="text/javascript">
function treemenu(obj)
+4 -6
View File
@@ -46,13 +46,12 @@ echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<link rel="stylesheet" href="../../../skin/tablesort.css" type="text/css"/>
<link rel="stylesheet" href="../../../skin/style.css.php" type="text/css">
<link rel="stylesheet" type="text/css" href="../../../skin/jquery-ui-1.9.2.custom.min.css">
<script type="text/javascript" src="../../../vendor/jquery/jquery1/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="../../../vendor/christianbach/tablesorter/jquery.tablesorter.min.js"></script>
<script type="text/javascript" src="../../../vendor/components/jqueryui/jquery-ui.min.js"></script>
<script type="text/javascript" src="../../../include/js/jquery.ui.datepicker.translation.js"></script>
<script type="text/javascript" src="../../../vendor/jquery/sizzle/sizzle.js"></script>';
include('../../../include/meta/jquery.php');
include('../../../include/meta/jquery-tablesorter.php');
const MOODLE_ADDON_KURZBZ = 'moodle';
// Load Addons to get Moodle_Path
@@ -72,7 +71,7 @@ echo '
$("#myTable").tablesorter(
{
sortList: [[0,0],[1,0]],
widgets: [\'zebra\',\'filter\']
widgets: [\'zebra\']
});
}
);
@@ -152,9 +151,8 @@ foreach($service->result as $row)
$person = new person();
$person->getPersonFromBenutzer($row->operativ_uid);
$operativ = $person->nachname.' '.$person->vorname;
$oeBez = new organisationseinheit($row->oe_kurzbz);
echo '<tr>';
echo '<td>',$oeBez->bezeichnung,'</td>';
echo '<td>',$row->oe_kurzbz,'</td>';
echo '<td><b>'.$row->bezeichnung.'</b></td>';
echo '<td>',$row->beschreibung,'</td>';
echo '<td><nobr><a href="../profile/index.php?uid='.$row->design_uid.'">',$design,'</a></nobr></td>';
+75 -11
View File
@@ -80,9 +80,17 @@ echo '
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml; charset=UTF-8" />
<link rel="stylesheet" href="../../vendor/components/jqueryui/themes/base/jquery-ui.min.css" type="text/css" />
<link rel="stylesheet" href="../../vendor/twbs/bootstrap3/dist/css/bootstrap.min.css" type="text/css"/>
<link href="../../skin/style.css.php" rel="stylesheet" type="text/css" />
<style>
.ui-dialog-titlebar-close
{
visibility: hidden !important;
}
</style>
<script type="text/javascript" src="../../vendor/components/jquery/jquery.min.js"></script>
<script type="text/javascript" src="../../vendor/components/jqueryui/jquery-ui.min.js"></script>
<script type="text/javascript" src="../../vendor/twbs/bootstrap3/dist/js/bootstrap.min.js"></script>
<script language="Javascript" type="text/javascript">
//<![CDATA[
@@ -131,22 +139,78 @@ echo '
}
}
function GebietStarten(bezeichnung,stunde,minute,sekunde,gebiet_id)
function GebietStarten(bezeichnung, stunde, minute, sekunde, gebiet_id)
{
var check = confirm(<?php echo "'".$p->t('testtool/okKlickenUmZuStarten')."'"?>+' '+stunde+'h '+minute+'m '+sekunde+'s');
if (check == true) {
var sprache_user = <?php echo "'".$sprache_user."'"?>;
document.location.href = 'frage.php?gebiet_id='+gebiet_id+'&start=true';
}
else {
return false;
let message = <?php echo "'".$p->t('testtool/okKlickenUmZuStarten')."'"?> + ' ' + stunde + 'h ' + minute + 'm ' + sekunde + 's';
let title = <?php echo "'".$p->t('testtool/startGebiet')."'"?>;
let abbrechen = <?php echo "'".$p->t('testtool/abbrechen')."'"?>;
if ($('#gebiet-dialog').length === 0)
{
$('body').append(
'<div id="gebiet-dialog" title="' + title + '">' +
'<p id="gebiet-dialog-msg">' + message + '</p>' +
'</div>'
);
}
$('#gebiet-dialog').dialog({
modal: true,
width: 400,
resizable: false,
buttons: [
{
text: 'OK',
click: function() {
$(this).dialog('close');
document.location.href = 'frage.php?gebiet_id=' + gebiet_id + '&start=true';
}
},
{
text: abbrechen,
click: function() {
$(this).dialog('close');
}
}
]
});
}
let letzteFrageBestaetigt = false;
function letzteFrage()
{
alert(<?php echo "'".$p->t("testtool/alleFragenBeantwortet")."'"?>);
return true;
if (letzteFrageBestaetigt)
return true;
let message = <?php echo "'".$p->t("testtool/alleFragenBeantwortet")."'"?>;
if ($('#fertig-dialog').length === 0)
{
$('body').append(
'<div id="fertig-dialog">' +
'<p>' + message + '</p>' +
'</div>'
);
}
$('#fertig-dialog').dialog({
modal: true,
width: 400,
resizable: false,
buttons: [
{
text: 'OK',
click: function() {
$(this).dialog('close');
letzteFrageBestaetigt = true;
$('[name="submitantwort"]').click();
}
}
]
});
return false;
}
$(document).ready(function () {
@@ -647,7 +711,7 @@ if($frage->frage_id!='')
}
$letzte = $frage->getNextFrage($gebiet_id, $_SESSION['pruefling_id'], $frage_id, $demo);
echo "<form action=\"$PHP_SELF?gebiet_id=$gebiet_id&amp;frage_id=$frage->frage_id\" method=\"POST\" ".(!$letzte && !$levelgebiet?"onsubmit=\"letzteFrage()\"":"").">";
echo "<form action=\"$PHP_SELF?gebiet_id=$gebiet_id&amp;frage_id=$frage->frage_id\" method=\"POST\" ".(!$letzte && !$levelgebiet?"onsubmit=\"return letzteFrage()\"":"").">";
echo '
<div class="row text-center">
<table class="table" style="width: 600px; margin-left: auto; margin-right: auto;">
+11 -2
View File
@@ -142,7 +142,9 @@ if (isset($_REQUEST['prestudent']))
}
if ($reihungstest_id != '' && $rt->load($reihungstest_id))
{
if ($rt->freigeschaltet)
$pruefling_exist = new Pruefling();
$alreadyInRT = $pruefling_exist->personAlreadyInRT($ps->person_id, $rt->reihungstest_id, $ps->prestudent_id);
if ($rt->freigeschaltet && !$alreadyInRT)
{
// regenerate Session ID after Login
session_regenerate_id();
@@ -282,7 +284,14 @@ if (isset($_REQUEST['prestudent']))
}
else
{
$alertmsg .= '<div class="alert alert-danger">'.$p->t('testtool/reihungstestNichtFreigeschalten').'</div>';
if ($alreadyInRT)
{
$alertmsg .= '<div class="alert alert-danger">'.$p->t('testtool/reihungstestNichtRegistriert').'</div>';
}
else
{
$alertmsg .= '<div class="alert alert-danger">'.$p->t('testtool/reihungstestNichtFreigeschalten').'</div>';
}
}
}
else
+1 -1
View File
@@ -293,7 +293,7 @@ else if (isset($_SESSION['pruefling_id']))
}
$lastsemester = $row->semester;
echo '<table border="0" cellspacing="0" cellpadding="0" id="Gebiet" style="display: visible; border-collapse: separate; border-spacing: 0 3px; margin-top: 5px;">';
echo '<table border="0" cellspacing="0" cellpadding="0" id="Gebiet" style="display: visible; border-collapse: separate; border-spacing: 0 3px;">';
echo '<tr><td class="HeaderTesttool">'. ($row->semester == '1' ? $p->t('testtool/basisgebiete') : $p->t('testtool/quereinstiegsgebiete')).'</td></tr>';
}
@@ -342,8 +342,6 @@ echo '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>';
<vbox>
<checkbox id="mitarbeiter-entwicklungsteam-detail-checkbox-neu" checked="true" hidden="true" />
<textbox id="mitarbeiter-entwicklungsteam-detail-textbox-studiengang" hidden="true" />
<textbox id="mitarbeiter-entwicklungsteam-detail-entwicklungsteam_id" hidden="true" />
<groupbox id="mitarbeiter-entwicklungsteam-detail-groupbox" flex="1">
<caption label="Details" />
<grid id="mitarbeiter-entwicklungsteam-detail-grid" style="margin:4px;" flex="1">
@@ -1708,7 +1708,6 @@ function MitarbeiterEntwicklungsteamSelect()
document.getElementById('mitarbeiter-entwicklungsteam-detail-textbox-studiengang').value=studiengang_kz;
document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-beginn').value=beginn;
document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-ende').value=ende;
document.getElementById('mitarbeiter-entwicklungsteam-detail-entwicklungsteam_id').value=entwicklungsteam_id;
MitarbeiterEntwicklungsteamDetailDisableFields(false);
}
@@ -1726,7 +1725,6 @@ function MitarbeiterEntwicklungsteamSpeichern()
studiengang_kz_old = document.getElementById('mitarbeiter-entwicklungsteam-detail-textbox-studiengang').value;
beginn = document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-beginn').value;
ende = document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-ende').value;
entwicklungsteam_id = document.getElementById('mitarbeiter-entwicklungsteam-detail-entwicklungsteam_id').value;
if(studiengang_kz=='')
{
-8
View File
@@ -3555,14 +3555,6 @@ function StudentZeugnisDokumentArchivieren()
case 'microcredential_2':
case 'microcredential_3':
case 'microcredential_4':
case 'microdegree_1':
case 'microdegree_2':
case 'microdegree_3':
case 'microdegree_4':
case 'microdegreeabschluss_1':
case 'microdegreeabschluss_2':
case 'microdegreeabschluss_3':
case 'microdegreeabschluss_4':
xml = 'microcredential.xml.php';
break;
+1 -3
View File
@@ -364,10 +364,9 @@ class entwicklungsteam extends basis_db
$bismeldung_jahr = $datetime->format('Y');
//laden des Datensatzes
$qry = "SELECT tbl_entwicklungsteam.*, tbl_besqual.*, tbl_studiengang.studiengang_kz, tbl_studiengang.melderelevant
$qry = "SELECT *
FROM bis.tbl_entwicklungsteam
JOIN bis.tbl_besqual USING(besqualcode)
JOIN public.tbl_studiengang USING(studiengang_kz)
WHERE mitarbeiter_uid=".$this->db_add_param($mitarbeiter_uid)."
AND (beginn is NULL OR beginn <= make_date(". $this->db_add_param($bismeldung_jahr). "::INTEGER, 12, 31))
AND (ende is NULL OR ende >= make_date(". $this->db_add_param($bismeldung_jahr). "::INTEGER, 1, 1))";
@@ -395,7 +394,6 @@ class entwicklungsteam extends basis_db
$obj->insertvon = $row->insertvon;
$obj->ext_id = $row->ext_id;
$obj->besqual = $row->besqualbez;
$obj->melderelevant = $this->db_parse_bool($row->melderelevant);
$this->result[] = $obj;
}
+1 -49
View File
@@ -25,7 +25,6 @@
*/
require_once(dirname(__FILE__).'/basis_db.class.php');
require_once(dirname(__FILE__).'/'.EXT_FKT_PATH.'/generateZahlungsreferenz.inc.php');
require_once(dirname(__FILE__).'/variable.class.php');
class konto extends basis_db
{
@@ -433,8 +432,6 @@ class konto extends basis_db
$qry.=" ORDER BY beschreibung";
$oehBeitrag = $this->_getOEHBeitrag();
if($this->db_query($qry))
{
while($row = $this->db_fetch_object())
@@ -443,15 +440,7 @@ class konto extends basis_db
$typ->buchungstyp_kurzbz = $row->buchungstyp_kurzbz;
$typ->beschreibung = $row->beschreibung;
if (strtolower($typ->buchungstyp_kurzbz) === 'oeh' && $oehBeitrag)
{
$typ->standardbetrag = $oehBeitrag;
}
else
{
$typ->standardbetrag = $row->standardbetrag;
}
$typ->standardbetrag = $row->standardbetrag;
$typ->standardtext = $row->standardtext;
$typ->credit_points = $row->credit_points;
$typ->aktiv = $this->db_parse_bool($row->aktiv);
@@ -1001,43 +990,6 @@ class konto extends basis_db
return false;
}
}
private function _getOEHBeitrag()
{
if(!is_user_logged_in())
{
return false;
}
$variablen_obj = new variable();
$variablen_obj->loadVariables(get_uid());
$qry = "WITH semstart AS (
SELECT start FROM public.tbl_studiensemester
WHERE studiensemester_kurzbz = '". $this->db_escape($variablen_obj->variable->semester_aktuell) . "'
)
SELECT * FROM bis.tbl_oehbeitrag oehb
JOIN public.tbl_studiensemester semvon ON oehb.von_studiensemester_kurzbz = semvon.studiensemester_kurzbz
LEFT JOIN public.tbl_studiensemester sembis ON oehb.bis_studiensemester_kurzbz = sembis.studiensemester_kurzbz
JOIN semstart ON semstart.start::date >= semvon.start::date AND (sembis.studiensemester_kurzbz IS NULL OR semstart.start::date <= sembis.start::date)
ORDER BY semvon.start
LIMIT 1";
if ($this->db_query($qry))
{
if($row = $this->db_fetch_object())
{
$summe = ($row->studierendenbeitrag + $row->versicherung) * -1;
return number_format((float)$summe, 2, '.', '');
}
return false;
}
else
{
$this->errormsg = 'Fehler bei der Abfrage aufgetreten';
return false;
}
}
}
?>
+2 -4
View File
@@ -584,9 +584,8 @@ class lehreinheitmitarbeiter extends basis_db
$qry = '
WITH semester_sws_tbl AS (
SELECT
DISTINCT lehreinheit_id, studiensemester_kurzbz, lema.semesterstunden,
stg.studiengang_kz, stg.melde_studiengang_kz, stg.lgartcode, stg.melderelevant
SELECT DISTINCT lehreinheit_id, studiensemester_kurzbz, lema.semesterstunden,
stg.studiengang_kz, stg.melde_studiengang_kz, stg.lgartcode
FROM lehre.tbl_lehreinheitmitarbeiter lema
JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id)
@@ -599,7 +598,6 @@ class lehreinheitmitarbeiter extends basis_db
AND ss.studiensemester_kurzbz IN ('.$this->implode4SQL($studiensemester_kurzbz_arr).')
-- nur lehre, die bisgemeldet wird
AND lema.bismelden
AND stg.melderelevant
-- keine lehreinheiten ohne semesterstunden
AND lema.semesterstunden != 0
)
+26
View File
@@ -584,6 +584,32 @@ class pruefling extends basis_db
$qry .= " LIMIT 1";
if($result = $this->db_query($qry))
{
if ($this->db_num_rows($result) == 0)
return false;
else
return true;
}
else
{
$this->errormsg = 'Fehler bei einer Abfrage';
return false;
}
}
public function personAlreadyInRT($person_id, $reihungstest_id, $prestudent_id)
{
$qry = "SELECT tbl_prestudent.prestudent_id
FROM public.tbl_rt_person
JOIN public.tbl_prestudent ON tbl_prestudent.person_id = tbl_rt_person.person_id
JOIN public.tbl_prestudentstatus ON tbl_prestudent.prestudent_id = tbl_prestudentstatus.prestudent_id AND status_kurzbz = 'Bewerber'
AND tbl_prestudentstatus.studienplan_id = tbl_rt_person.studienplan_id
WHERE tbl_rt_person.person_id = " . $this->db_add_param($person_id) . "
AND tbl_rt_person.rt_id = " . $this->db_add_param($reihungstest_id) . "
AND tbl_prestudent.prestudent_id != " . $this->db_add_param($prestudent_id) . "
AND get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL) = 'Bewerber'
LIMIT 1";
if($result = $this->db_query($qry))
{
if ($this->db_num_rows($result) == 0)
+3
View File
@@ -17,6 +17,7 @@ $this->phrasen['testtool/basic']='Basic';
$this->phrasen['testtool/basisgebiete']='Basisgebiete';
$this->phrasen['testtool/semester']='Semester';
$this->phrasen['testtool/reihungstestNichtFreigeschalten']='Der zuteilte Reihungstest ist noch nicht freigeschaltet';
$this->phrasen['testtool/reihungstestNichtRegistriert']='Sie sind für den Reihungstest nicht registriert';
$this->phrasen['testtool/reihungstestKannNichtGeladenWerden']='Der Reihungstest dem Sie zugeteilt sind, kann nicht geladen werden. Melden Sie sich bitte bei der Reihungstestaufsicht.';
$this->phrasen['testtool/geburtsdatumStimmtNichtUeberein']='Ihr Geburtsdatum stimmt nicht mit unseren Daten überein. Bitte wenden Sie sich an die Aufsichtsperson';
$this->phrasen['testtool/home']='Home';
@@ -35,6 +36,8 @@ $this->phrasen['testtool/spracheDerTestfragen']='Gewünschte Sprache der Testfra
$this->phrasen['testtool/einleitung']='Einleitung';
$this->phrasen['testtool/blaettern']='Blättern';
$this->phrasen['testtool/demo']='Demobeispiel ansehen';
$this->phrasen['testtool/abbrechen']='Abbrechen';
$this->phrasen['testtool/startGebiet']='Gebiet starten';
$this->phrasen['testtool/okKlickenUmZuStarten']='Klicken Sie OK um dieses Gebiet zu starten. \nSie haben für die Bearbeitung ein Zeitlimit von';
$this->phrasen['testtool/bitteZuerstAnmelden']='Bitte zuerst anmelden!';
$this->phrasen['testtool/fehlerBeimGenerierenDesFragenpools']='Fehler beim generieren des Fragenpools';
+3
View File
@@ -17,6 +17,7 @@ $this->phrasen['testtool/basic']='Basic';
$this->phrasen['testtool/basisgebiete']='Basic test';
$this->phrasen['testtool/semester']='Semester';
$this->phrasen['testtool/reihungstestNichtFreigeschalten']='The entrance examination assigned has not yet been activated.';
$this->phrasen['testtool/reihungstestNichtRegistriert']='You are not registered for the placement test.';
$this->phrasen['testtool/reihungstestKannNichtGeladenWerden']='The placement test you are assigned to could not be loaded. Please contact the placement test supervisior.';
$this->phrasen['testtool/geburtsdatumStimmtNichtUeberein']='Your date of birth does not correspond to the data we have. Please speak to the supervisor. ';
$this->phrasen['testtool/home']='Home';
@@ -35,6 +36,8 @@ $this->phrasen['testtool/spracheDerTestfragen']='Desired language of questions';
$this->phrasen['testtool/einleitung']='Introduction';
$this->phrasen['testtool/blaettern']='Browse';
$this->phrasen['testtool/demo']='See an example';
$this->phrasen['testtool/abbrechen']='Cancel';
$this->phrasen['testtool/startGebiet']='Start the section';
$this->phrasen['testtool/okKlickenUmZuStarten']='Click OK to start this section. \nYou have a timelimit of';
$this->phrasen['testtool/bitteZuerstAnmelden']='Please log in first!';
$this->phrasen['testtool/fehlerBeimGenerierenDesFragenpools']='Error in generating the pool of questions.';
+19 -1
View File
@@ -407,6 +407,7 @@ html {
background-color: var(--fhc-background);
border-color: var(--fhc-border);
padding: var(--fhc-cis-main-py) var(--fhc-cis-main-px);
min-width: 0; /* fix flex-grow with tabulator exceeding width */
}
#cis-main .fa-arrow-up-right-from-square {
@@ -856,8 +857,25 @@ html {
}
/* styling for editable dropdown column of notenvorschläge in benotungstool*/
#notentable .tabulator-tableholder .tabulator-editable {
position: relative;
background-color: rgba(255, 255, 157, 0.73);
cursor: pointer;
}
#notentable .tabulator-tableholder .tabulator-editable::after {
content: "▾";
position: absolute;
right: 6px;
color: rgba(176, 176, 106, 0.73);;
font-size: x-large;
bottom: 6px;
pointer-events: none;
}
.bordered-modal {
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 0.5rem;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
}
}
+6
View File
@@ -38,3 +38,9 @@ textarea[name="anmerkung"] {
{
border-color: black;
}
.node-verband {
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
}
+4 -4
View File
@@ -197,6 +197,10 @@ html.fs_huge {
margin-bottom: -1px;
}
.tiny-90 div.tox.tox-tinymce {
height: 90% !important;
}
/* slim begin */
.stv .form-label {
margin-bottom: .15rem;
@@ -277,7 +281,3 @@ html.fs_huge {
}
*/
/* slim ende */
.fhc-xxl-modal {
min-width: 80vw;
}
+1 -91
View File
@@ -305,94 +305,4 @@
/* If you use hover rows, you need to ensure the sticky cell matches the hover color */
#abgabetable .tabulator-row:hover .tabulator-cell.sticky-col {
background-color: #ccc; /* Match your existing hover color */
}
.tabulator-cell {
container-type: inline-size;
}
.tabulator-col-title {
container-type: inline-size;
}
@container (max-width: 100px) {
.full-text {
display: none !important;
}
.short-text {
display: inline-block !important;
width: 100%;
}
}
/*conditional tooltips fix*/
.p-tooltip.custom-tooltip {
z-index: 8001 !important;
}
/* Shrinks font and table rows for desktop users who have zoomed in their browser (150%+).
Does not affect mobile/touchscreen devices, which use touch input instead of a mouse. */
@media (pointer: fine) and (min-resolution: 1.5dppx) {
html.abgabetool {
font-size: 0.5rem;
}
.abgabetool .tabulator-cell,
.abgabetool .tabulator-row {
height: 20px;
max-height: 20px;
}
}
/*confetti celebration on endupload - impossible to miss*/
#confetti-container {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
z-index: 9999;
overflow: hidden;
}
.confetti-piece {
position: absolute;
opacity: 0;
will-change: top, transform, opacity;
}
/* Background Rain */
@keyframes fallAndSpin {
0% {
top: var(--start-y);
transform: translate3d(0, 0, 0) rotateX(0deg) rotateY(0deg);
opacity: 1;
}
100% {
top: 105vh;
transform: translate3d(var(--drift), 0, 0) rotateX(720deg) rotateY(360deg);
opacity: 0.3;
}
}
/* Corner Cannons*/
@keyframes cannonBlast {
0% {
transform: translate3d(0, 0, 0) scale(0.3) rotate(0deg);
opacity: 1;
animation-timing-function: cubic-bezier(0.1, 0.8, 0.2, 1);
}
30% {
transform: translate3d(var(--blast-x), var(--blast-y), 0) scale(1.2) rotate(270deg);
opacity: 1;
animation-timing-function: linear;
}
100% {
transform: translate3d(calc(var(--blast-x) * 1.4), 15vh, 0) scale(0.4) rotate(630deg);
opacity: 0;
}
}
}
-22
View File
@@ -77,13 +77,6 @@ export default {
}
};
},
patchProjektarbeitAbgabeMultiple(payload) {
return {
method: 'post',
url: '/api/frontend/v1/Abgabe/patchProjektarbeitAbgabeMultiple',
params: payload
};
},
deleteProjektarbeitAbgabe(paabgabe_id) {
return {
method: 'post',
@@ -91,13 +84,6 @@ export default {
params: { paabgabe_id }
};
},
deleteProjektarbeitAbgabeMultiple(paabgabe_ids) {
return {
method: 'post',
url: '/api/frontend/v1/Abgabe/deleteProjektarbeitAbgabeMultiple',
params: { paabgabe_ids }
};
},
postSerientermin(datum, paabgabetyp_kurzbz, bezeichnung, kurzbz, upload_allowed, projektarbeit_ids, fixtermin) {
return {
method: 'post',
@@ -153,14 +139,6 @@ export default {
url: '/api/frontend/v1/Abgabe/getSignaturStatusForProjektarbeitAbgaben',
params: {paabgabe_ids, student_uid},
};
},
postStudentProjektarbeitTitel(projektarbeit_id, titel) {
return {
method: 'post',
url: '/api/frontend/v1/Abgabe/postStudentProjektarbeitTitel',
params: {projektarbeit_id, titel},
};
}
};
+14
View File
@@ -35,5 +35,19 @@ export default {
method: 'get',
url: `/api/frontend/v1/Lehre/Pruefungen/${lehrveranstaltung_id}`
};
},
getZugewieseneLv(uid, sem_kurzbz){
return {
method: 'get',
url: '/api/frontend/v1/Lehre/getZugewieseneLv',
params: { uid, sem_kurzbz}
};
},
getLeForLv(lv_id, sem_kurzbz) {
return {
method: 'get',
url: '/api/frontend/v1/Lehre/getLeForLv',
params: { lv_id, sem_kurzbz }
};
}
};
+5 -8
View File
@@ -17,16 +17,13 @@
export default {
getMessages(params) {
let url = 'api/frontend/v1/messages/messages/getMessages'
+ '/' + params.id
+ '/' + params.type;
if(params.size && params.page) {
url += '/' + params.size
+ '/' + params.page;
}
return {
method: 'get',
url: url
url: 'api/frontend/v1/messages/messages/getMessages/'
+ params.id + '/'
+ params.type + '/'
+ params.size + '/'
+ params.page
};
},
getVorlagen(){
+74
View File
@@ -0,0 +1,74 @@
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export default {
getStudentenNoten(lv_id, sem_kurzbz) {
return {
method: 'get',
url: '/api/frontend/v1/Noten/getStudentenNoten',
params: { lv_id, sem_kurzbz }
};
},
getNoten(){
return {
method: 'get',
url: '/api/frontend/v1/Noten/getNoten'
};
},
saveStudentenNoten(password, noten, lv_id, sem_kurzbz) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveStudentenNoten',
params: { password, noten, lv_id, sem_kurzbz }
};
},
saveNotenvorschlag(lv_id, sem_kurzbz, student_uid, note) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveNotenvorschlag',
params: { lv_id, sem_kurzbz, student_uid, note }
};
},
saveStudentPruefung(student_uid, note, punkte, datum, lva_id, lehreinheit_id, sem_kurzbz, typ){
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveStudentPruefung',
params: { student_uid, note, punkte, datum, lva_id, lehreinheit_id, sem_kurzbz, typ }
};
},
createPruefungen(uids, datum, lva_id, sem_kurzbz){
return {
method: 'post',
url: '/api/frontend/v1/Noten/createPruefungen',
params: { uids, datum, lva_id, sem_kurzbz }
};
},
saveNotenvorschlagBulk(lv_id, sem_kurzbz, noten) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveNotenvorschlagBulk',
params: { lv_id, sem_kurzbz, noten }
};
},
saveStudentPruefungBulk(lv_id, sem_kurzbz, pruefungen) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/savePruefungenBulk',
params: { lv_id, sem_kurzbz, pruefungen }
};
}
};
+6
View File
@@ -16,6 +16,12 @@
*/
export default {
getStudiensemester() {
return {
method: 'get',
url: '/api/frontend/v1/Studiensemester/getStudiensemester'
};
},
getAllStudiensemesterAndAktOrNext() {
return {
method: 'get',
+10 -6
View File
@@ -16,16 +16,20 @@
*/
export default {
getCourselist(params) {
getCourselist(student_uid, start_date, end_date, stundenplan) {
return {
method: 'get',
url: 'api/frontend/v1/stv/LvTermine/getStundenplan/' + params.student_uid + '/'
+ params.start_date + '/'
+ params.end_date + '/'
+ params.group_consecutiveHours + '/'
+ params.dbStundenplanTable
url: 'api/frontend/v1/stv/LvTermine/getStundenplan/' + encodeURIComponent(student_uid) + '/'
+ encodeURIComponent(start_date) + '/'
+ encodeURIComponent(end_date) + '/'
+ encodeURIComponent(stundenplan) + '/'
+ encodeURIComponent(true)
};
},
exportCalendar(student_uid, stundenplan)
{
return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'content/statistik/termine.xls.php?student_uid=' + encodeURIComponent(student_uid) + '&db_stpl_table='+encodeURIComponent(stundenplan);
},
getStudiensemester(){
return {
method: 'get',
+3 -15
View File
@@ -38,10 +38,6 @@ export default {
};
},
insert(params) {
if(params.betrag)
{
params.betrag = params.betrag.replace(',', '.');
}
return {
method: 'post',
url: 'api/frontend/v1/stv/konto/insert',
@@ -56,10 +52,6 @@ export default {
};
},
edit(params) {
if(params.betrag)
{
params.betrag = params.betrag.replace(',', '.');
}
return {
method: 'post',
url: 'api/frontend/v1/stv/konto/update',
@@ -73,14 +65,10 @@ export default {
params: { buchungsnr }
};
},
getBuchungstypen(studiensemester_kurzbz) {
let url = 'api/frontend/v1/stv/konto/getBuchungstypen'
if (!!studiensemester_kurzbz)
url = url + '/' + encodeURIComponent(studiensemester_kurzbz);
getBuchungstypen() {
return {
method: 'get',
url: url
url: 'api/frontend/v1/stv/konto/getBuchungstypen'
};
},
}
};
+15
View File
@@ -0,0 +1,15 @@
export default {
get() {
return {
method: 'get',
url: '/api/frontend/v1/lv/config/get'
};
},
set(params) {
return {
method: 'post',
url: 'api/frontend/v1/lv/config/set',
params
};
}
};
@@ -18,6 +18,14 @@ export default {
params: newData
};
},
getGruppe(gruppe)
{
return {
method: 'post',
url: '/api/frontend/v1/lv/gruppe/getGruppe/',
params: gruppe
};
},
getByLehreinheit(lehreinheit_id)
{
return {
@@ -0,0 +1,19 @@
export default {
getCourselist(le_id, start_date, end_date, stundenplan)
{
return {
method: 'get',
url: '/api/frontend/v1/lvPlan/getLeEvents/' + encodeURIComponent(le_id) + "/" + encodeURIComponent(start_date) + "/" + encodeURIComponent(end_date) + "/" + encodeURIComponent(stundenplan),
};
},
exportCalendar(le_id, stundenplan)
{
return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'content/statistik/termine.xls.php?lehreinheit_id=' + encodeURIComponent(le_id) + '&db_stpl_table='+encodeURIComponent(stundenplan);
},
getStudiensemester(){
return {
method: 'get',
url: '/api/frontend/v1/lv/setup/getStudiensemester/'
};
},
};
@@ -0,0 +1,19 @@
export default {
getCourselist(lv_id, start_date, end_date, stundenplan)
{
return {
method: 'get',
url: '/api/frontend/v1/lvPlan/getLvEvents/' + encodeURIComponent(lv_id) + "/" + encodeURIComponent(start_date) + "/" + encodeURIComponent(end_date) + "/" + encodeURIComponent(stundenplan),
};
},
exportCalendar(lv_id, stundenplan)
{
return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'content/statistik/termine.xls.php?lehrveranstaltung_id=' + encodeURIComponent(lv_id) + '&db_stpl_table='+encodeURIComponent(stundenplan);
},
getStudiensemester(){
return {
method: 'get',
url: '/api/frontend/v1/lv/setup/getStudiensemester/'
};
},
};
+25
View File
@@ -0,0 +1,25 @@
import Grades from '../factory/stv/grades.js';
export default {
...Grades,
getCertificate(lv_id, studiensemester_kurzbz) {
let url = 'api/frontend/v1/lv/noten/getCertificate/' + encodeURIComponent(lv_id);
if (!!studiensemester_kurzbz) {
url = url + '/' + encodeURIComponent(studiensemester_kurzbz);
}
return {
method: 'get',
url: url
};
},
getTeacherProposal(lv_id, studiensemester_kurzbz) {
let url = 'api/frontend/v1/lv/noten/getTeacherProposal/' + encodeURIComponent(lv_id);
if (!!studiensemester_kurzbz) {
url = url + '/' + encodeURIComponent(studiensemester_kurzbz);
}
return {
method: 'get',
url: url
};
},
};
+9 -2
View File
@@ -1,9 +1,16 @@
export default {
getTabs()
getLETabs()
{
return {
method: 'get',
url: '/api/frontend/v1/lv/setup/getTabs/'
url: '/api/frontend/v1/lv/setup/getLETabs/'
};
},
getLVTabs()
{
return {
method: 'get',
url: '/api/frontend/v1/lv/setup/getLVTabs/'
};
},
}
+7 -4
View File
@@ -25,6 +25,9 @@ const app = Vue.createApp({
},
computed: {
viewData() {
return { uid: this.uid}
},
student_uid_computed() {
return this.student_uid ?? this.uid
},
@@ -52,10 +55,10 @@ const app = Vue.createApp({
},
template: `
<template v-if="comp && uid">
<AbgabetoolStudent v-if="comp == 'AbgabetoolStudent'" :student_uid_prop="student_uid_computed"></AbgabetoolStudent>
<AbgabetoolMitarbeiter v-if="comp == 'AbgabetoolMitarbeiter'"></AbgabetoolMitarbeiter>
<AbgabetoolAssistenz v-if="comp == 'AbgabetoolAssistenz'" :stg_kz_prop="stg_kz_computed"></AbgabetoolAssistenz>
<DeadlineOverview v-if="comp == 'DeadlinesOverview'"></DeadlineOverview>
<AbgabetoolStudent v-if="comp == 'AbgabetoolStudent'" :viewData="viewData" :student_uid_prop="student_uid_computed"></AbgabetoolStudent>
<AbgabetoolMitarbeiter v-if="comp == 'AbgabetoolMitarbeiter'" :viewData="viewData"></AbgabetoolMitarbeiter>
<AbgabetoolAssistenz v-if="comp == 'AbgabetoolAssistenz'" :viewData="viewData" :stg_kz_prop="stg_kz_computed"></AbgabetoolAssistenz>
<DeadlineOverview v-if="comp == 'DeadlinesOverview'" :viewData="viewData"></DeadlineOverview>
</template>
`
});
+7
View File
@@ -20,6 +20,7 @@ import Studium from "../../components/Cis/Studium/Studium.js";
import ApiRenderers from '../../api/factory/renderers.js';
import ApiRouteInfo from '../../api/factory/routeinfo.js';
import Benotungstool from "../../components/Cis/Benotungstool/Benotungstool.js";
import {capitalize} from "../../helpers/StringHelpers.js";
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
@@ -75,6 +76,12 @@ const router = VueRouter.createRouter({
component: Raumsuche,
props: true
},
{
path: `/Cis/Benotungstool/:lv_id/:sem_kurzbz`,
name: 'Benotungstool',
component: Benotungstool,
props: true
},
// Redirect old links to new format
{
path: "/CisVue/Cms/getRoomInformation/:ort_kurzbz",
+2 -31
View File
@@ -4,9 +4,7 @@ export default {
name: 'BootstrapModal',
data: () => ({
modal: null,
fullscreen: false,
expandBtnHovered: false,
expandBtnFocused: false,
fullscreen: false
}),
props: {
backdrop: {
@@ -72,29 +70,6 @@ export default {
this.$emit('toggleFullscreen')
}
},
computed: {
getExpandButtonStyles() {
const hovered = this.expandBtnHovered;
const focused = this.expandBtnFocused;
return `display: flex;
align-items: center;
justify-content: center;
width: 1em;
height: 1em;
padding: 0;
border: 0;
background: transparent;
font-size: 1em;
opacity: 0.5;
color: inherit;
cursor: pointer;
line-height: 1;
transition: opacity 0.15s ease;
opacity: ${focused ? '1' : hovered ? '0.75' : '0.5'};
outline: ${focused ? '1px solid currentColor' : 'none'};
outline-offset: 2px;`
}
},
mounted() {
if (this.$refs.modal)
this.modal = new bootstrap.Modal(this.$refs.modal, {
@@ -165,13 +140,9 @@ export default {
<div class="d-flex align-items-center ms-auto gap-2">
<button
type="button"
:style="getExpandButtonStyles"
class="btn mb-1"
v-if="allowFullscreenExpand"
@click="toggleFullscreen"
@mouseenter="expandBtnHovered = true"
@mouseleave="expandBtnHovered = false"
@focusin="expandBtnFocused = true"
@focusout="expandBtnFocused = false"
:aria-label="fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'"
>
<i v-if="!fullscreen" class="fa-solid fa-expand"></i>
@@ -2,7 +2,6 @@ import BsModal from '../../Bootstrap/Modal.js';
import VueDatePicker from '../../vueDatepicker.js.php';
import ApiAbgabe from '../../../api/factory/abgabe.js'
import { getDateStyleClass } from "./getDateStyleClass.js";
import { compareISODateValues, formatISODate, getViennaTodayISO } from "./dateUtils.js";
export const AbgabeMitarbeiterDetail = {
name: "AbgabeMitarbeiterDetail",
@@ -47,7 +46,12 @@ export const AbgabeMitarbeiterDetail = {
eidAkzeptiert: false,
enduploadTermin: null,
allActiveLanguages: FHC_JS_DATA_STORAGE_OBJECT.server_languages,
speedDialItems: [
speedDialItems: [{
label: Vue.computed(() => this.$p.t('abgabetool/c4newAbgabetermin')),
icon: "fa fa-plus",
command: this.openCreateNewAbgabeModal,
disabled: Vue.computed(() => !this.getAllowedToCreateNewTermin)
},
{
label: Vue.computed(() => this.$p.t('abgabetool/c4benoten')),
icon: "fa fa-user-check",
@@ -77,9 +81,6 @@ export const AbgabeMitarbeiterDetail = {
}
},
methods: {
terminIsInvalid(termin) {
return termin.note?.positiv == false && !termin.beurteilungsnotiz
},
getNoteBezeichnung(termin){
if(termin.noteBackend?.bezeichnung) {
return termin.noteBackend?.positiv ? this.$capitalize(this.$p.t('abgabetool/c4positivBenotet')) + ' ✅' : this.$capitalize(this.$p.t('abgabetool/c4negativBenotet')) + ' ❌'
@@ -112,8 +113,6 @@ export const AbgabeMitarbeiterDetail = {
if(newTerminRes.note) {
newTerminRes.note = noteOpt
newTerminRes.noteBackend = noteOpt // certain UI elements should only reflect persisted state
termin.allowedToDelete = false
newTerminRes.allowedToDelete = false
}
newTerminRes.invertedFixtermin = !newTerminRes.fixtermin
const existingTerminRes = res.data[1]
@@ -133,13 +132,13 @@ export const AbgabeMitarbeiterDetail = {
} else {
const noteOptExisting = this.allowedNotenOptions.find(opt => opt.note == existingTerminRes.note)
existingTerminRes.note = noteOptExisting
termin.paabgabetyp_kurzbz = newTerminRes.paabgabetyp_kurzbz
termin.noteBackend = noteOpt // do NOT take noteOptExisting -> should reflect the "yes the qgate grade is confirmed in backend ux behaviour"
termin.dateStyle = getDateStyleClass(termin, this.notenOptions)
}
this.projektarbeit.abgabetermine.sort((a, b) => compareISODateValues(a.datum, b.datum))
this.projektarbeit.abgabetermine.sort((a, b) =>new Date(a.datum) - new Date(b.datum))
const index = this.projektarbeit.abgabetermine.findIndex(t => termin.paabgabe_id == t.paabgabe_id)
@@ -160,7 +159,7 @@ export const AbgabeMitarbeiterDetail = {
'fixtermin': false,
'invertedFixtermin': true,
'kurzbz': '', // todo kurzbz textfield value vorschlag für qualgates
'datum': getViennaTodayISO(),
'datum': new Date().toISOString().split('T')[0],
'note': this.allowedNotenOptions.find(opt => opt.note == 9),
'beurteilungsnotiz': '',
'upload_allowed': false,
@@ -338,7 +337,16 @@ export const AbgabeMitarbeiterDetail = {
}
},
formatDate(dateParam) {
return formatISODate(dateParam)
// unsafe for datepickers, dont use there
const date = new Date(dateParam)
// handle missing leading 0
const padZero = (num) => String(num).padStart(2, '0');
const month = padZero(date.getMonth() + 1); // Months are zero-based
const day = padZero(date.getDate());
const year = date.getFullYear();
return `${day}.${month}.${year}`
},
openCreateNewAbgabeModal() {
if(this.projektarbeit?.betreuerart_kurzbz == 'Zweitbegutachter') {
@@ -356,7 +364,7 @@ export const AbgabeMitarbeiterDetail = {
'fixtermin': false,
'invertedFixtermin': true,
'kurzbz': '',
'datum': getViennaTodayISO(),
'datum': new Date().toISOString().split('T')[0],
'note': this.allowedNotenOptions.find(opt => opt.note == 9),
'beurteilungsnotiz': '',
'upload_allowed': typ.upload_allowed_default,
@@ -390,7 +398,7 @@ export const AbgabeMitarbeiterDetail = {
'fixtermin': false,
'invertedFixtermin': true,
'kurzbz': '',
'datum': getViennaTodayISO(),
'datum': new Date().toISOString().split('T')[0],
'note': this.allowedNotenOptions.find(opt => opt.note == 9),
'beurteilungsnotiz': '',
'upload_allowed': false,
@@ -438,7 +446,7 @@ export const AbgabeMitarbeiterDetail = {
}
},
getMessagePtStyle() {
// adjust outer spacing and internal padding to appear similar to download button in size
// adjust outer spacing and internal padding to appear similar to doenload button in size
return {
root: {
style: {
@@ -553,12 +561,6 @@ export const AbgabeMitarbeiterDetail = {
class: "custom-tooltip"
}
},
getTooltipBeurteilungsnotiz() {
return {
value: this.$p.t('abgabetool/c4beurteilungsnotizBeiNegNote'),
class: "custom-tooltip"
}
},
getProjektarbeitTitel() {
if(this.projektarbeit?.titel) return this.$capitalize(this.$p.t('abgabetool/c4titel')) + ': ' + this.projektarbeit.titel
@@ -589,7 +591,7 @@ export const AbgabeMitarbeiterDetail = {
'fixtermin': false,
'invertedFixtermin': true,
'kurzbz': '',
'datum': getViennaTodayISO(),
'datum': new Date().toISOString().split('T')[0],
'note': this.allowedNotenOptions.find(opt => opt.note == 9),
'beurteilungsnotiz': '',
'upload_allowed': typ.upload_allowed_default,
@@ -625,7 +627,6 @@ export const AbgabeMitarbeiterDetail = {
dialogClass="bordered-modal modal-lg"
:backdrop="true"
@hideBsModal="showAutomagicModalPhrase=false;"
bodyClass="px-4 py-4"
>
<template v-slot:title>
<div>
@@ -655,7 +656,6 @@ export const AbgabeMitarbeiterDetail = {
:enable-time-picker="false"
locale="de"
format="dd.MM.yyyy"
model-type="yyyy-MM-dd"
:text-input="true"
auto-apply>
</VueDatePicker>
@@ -806,7 +806,6 @@ export const AbgabeMitarbeiterDetail = {
:enable-time-picker="false"
locale="de"
format="dd.MM.yyyy"
model-type="yyyy-MM-dd"
:text-input="true"
auto-apply>
</VueDatePicker>
@@ -817,7 +816,7 @@ export const AbgabeMitarbeiterDetail = {
<div class="col-12 col-md-9">
<Dropdown
:style="{'width': '100%'}"
:disabled="!termin.allowedToSave || termin.abgabedatum !== null || termin.noteBackend"
:disabled="!termin.allowedToSave"
:placeholder="getPlaceholderTermin(termin)"
v-model="termin.bezeichnung"
@change="handleChangeAbgabetyp(termin)"
@@ -852,10 +851,8 @@ export const AbgabeMitarbeiterDetail = {
</div>
<div class="row mt-2" v-if="termin.bezeichnung?.benotbar">
<div class="col-12 col-md-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4notizQualGatev2') )}}</div>
<div class="col-12 col-md-9" v-tooltip.right="terminIsInvalid(termin) && getTooltipBeurteilungsnotiz">
<Textarea style="margin-bottom: 4px;" v-model="termin.beurteilungsnotiz"
:class="{ 'p-invalid': terminIsInvalid(termin) }"
rows="1" class="w-100" :disabled="!termin.allowedToSave"></Textarea>
<div class="col-12 col-md-9">
<Textarea style="margin-bottom: 4px;" v-model="termin.beurteilungsnotiz" rows="1" class="w-100" :disabled="!termin.allowedToSave"></Textarea>
</div>
</div>
@@ -877,7 +874,6 @@ export const AbgabeMitarbeiterDetail = {
:disabled="true"
locale="de"
format="dd.MM.yyyy"
model-type="yyyy-MM-dd"
>
</VueDatePicker>
</div>
@@ -893,6 +889,9 @@ export const AbgabeMitarbeiterDetail = {
<Message v-else-if="termin?.signatur == false" severity="error" :closable="false" :pt="getMessagePtStyle"> {{ $capitalize($p.t('abgabetool/c4keineSignatur')) }} </Message>
<Message v-else-if="termin?.signatur == 'error'" severity="warn" :closable="false" :pt="getMessagePtStyle"> {{ $capitalize($p.t('abgabetool/c4signaturServerError')) }} </Message>
</div>
<!-- <div v-else class="col-auto">-->
<!-- <Message severity="info" :closable="false" :pt="getMessagePtStyle"> {{ $p.t('abgabetool/c4noFileFound') }} </Message>-->
<!-- </div>-->
</div>
</template>
@@ -908,7 +907,7 @@ export const AbgabeMitarbeiterDetail = {
<div class="col-12 col-md-9">
<div class="row">
<div class="col-auto">
<button v-if="termin.allowedToSave && !terminIsInvalid(termin)" style="max-height: 40px;" class="btn btn-primary border-0" @click="saveTermin(termin)">
<button v-if="termin.allowedToSave" style="max-height: 40px;" class="btn btn-primary border-0" @click="saveTermin(termin)">
{{ $capitalize( $p.t('abgabetool/c4save') )}}
<i class="fa-solid fa-floppy-disk"></i>
</button>
@@ -945,8 +944,7 @@ export const AbgabeMitarbeiterDetail = {
<bs-modal
ref="modalContainerZusatzdaten"
class="bootstrap-prompt"
dialogClass="bordered-modal modal-lg"
bodyClass="px-4 py-4">
dialogClass="bordered-modal modal-lg">
<template v-slot:title>
<div>
{{$capitalize( $p.t('abgabetool/c4enduploadZusatzdaten') )}}
@@ -3,9 +3,6 @@ import BsModal from '../../Bootstrap/Modal.js';
import VueDatePicker from '../../vueDatepicker.js.php';
import ApiAbgabe from '../../../api/factory/abgabe.js'
import FhcOverlay from "../../Overlay/FhcOverlay.js";
import { formatISODate, getViennaTodayISO } from "./dateUtils.js";
import { validateThesisTitle } from './titleValidation.js'
export const AbgabeStudentDetail = {
name: "AbgabeStudentDetail",
@@ -23,16 +20,7 @@ export const AbgabeStudentDetail = {
VueDatePicker,
FhcOverlay
},
inject: [
'notenOptions',
'isMobile',
'isViewMode',
'moodle_link',
'confetti_on_endupload',
'title_edit_allowed',
'siginfolink_german',
'siginfolink_english'
],
inject: ['notenOptions', 'isMobile', 'isViewMode', 'moodle_link'],
props: {
projektarbeit: {
type: Object,
@@ -43,14 +31,12 @@ export const AbgabeStudentDetail = {
default: false
}
},
emits: ['titel-updated'],
data() {
return {
loading: false,
eidAkzeptiert: false,
enduploadTermin: null,
allActiveLanguages: FHC_JS_DATA_STORAGE_OBJECT.server_languages,
editingTitel: '',
form: Vue.reactive({
sprache: '',
abstract: '',
@@ -63,120 +49,9 @@ export const AbgabeStudentDetail = {
}
},
methods: {
confettiCannons() {
const container = document.getElementById('confetti-container');
if (!container) return;
const colors = ['#FFC107', '#FF5722', '#E91E63', '#00BCD4', '#4CAF50', '#9C27B0'];
const shapes = ['50%', '0%'];
const fragment = document.createDocumentFragment();
// Corner Cannons - Slowed Down)
const cannonCount = 150;
for (let i = 0; i < cannonCount; i++) {
const leftConfetti = document.createElement('div');
leftConfetti.classList.add('confetti-piece');
leftConfetti.style.left = '0px';
leftConfetti.style.top = '100%';
const rightConfetti = document.createElement('div');
rightConfetti.classList.add('confetti-piece');
rightConfetti.style.left = '100vw';
rightConfetti.style.top = '100%';
const colorL = colors[Math.floor(Math.random() * colors.length)];
const colorR = colors[Math.floor(Math.random() * colors.length)];
const shapeL = shapes[Math.floor(Math.random() * shapes.length)];
const shapeR = shapes[Math.floor(Math.random() * shapes.length)];
// Left Styles
leftConfetti.style.background = colorL;
leftConfetti.style.borderRadius = shapeL;
leftConfetti.style.width = `${Math.random() * 10 + 6}px`;
leftConfetti.style.height = `${Math.random() * 14 + 6}px`;
leftConfetti.style.setProperty('--blast-x', `${Math.random() * 50 + 10}vw`);
leftConfetti.style.setProperty('--blast-y', `-${Math.random() * 65 + 30}vh`);
// Right Styles
rightConfetti.style.background = colorR;
rightConfetti.style.borderRadius = shapeR;
rightConfetti.style.width = `${Math.random() * 10 + 6}px`;
rightConfetti.style.height = `${Math.random() * 14 + 6}px`;
rightConfetti.style.setProperty('--blast-x', `-${Math.random() * 50 + 10}vw`);
rightConfetti.style.setProperty('--blast-y', `-${Math.random() * 65 + 30}vh`);
// Increased durations to 3s - 5s for a floating gravity effect
const durationL = Math.random() * 2 + 3;
const durationR = Math.random() * 2 + 3;
const delayL = Math.random() * 0.2;
const delayR = Math.random() * 0.2;
leftConfetti.style.animation = `cannonBlast ${durationL}s linear ${delayL}s both`;
rightConfetti.style.animation = `cannonBlast ${durationR}s linear ${delayR}s both`;
fragment.appendChild(leftConfetti);
fragment.appendChild(rightConfetti);
setTimeout(() => leftConfetti.remove(), (delayL + durationL) * 1000);
setTimeout(() => rightConfetti.remove(), (delayR + durationR) * 1000);
}
container.appendChild(fragment);
},
openTitelEdit() {
this.editingTitel = this.projektarbeit.titel ?? '';
this.$refs.modalTitelEdit.show();
},
async saveTitel() {
const validation = validateThesisTitle(this.editingTitel);
if (!validation.isValid) {
if (validation.error === 'empty') {
this.$fhcAlert.alertWarning(this.$p.t('abgabetool/c4emptyThesisTitle'))
} else if (validation.error === 'invalid_characters') {
this.$fhcAlert.alertWarning(this.$p.t('abgabetool/c4invalidCharactersThesisTitle'))
}
return false;
}
const confirmed = await this.$fhcAlert.confirm({
message: this.$p.t('abgabetool/c4confirmTitelSpeichern'),
acceptLabel: this.$capitalize(this.$p.t('ui/speichern')),
acceptClass: 'p-button-primary',
rejectLabel: this.$capitalize(this.$p.t('abgabetool/c4Cancel')),
rejectClass: 'p-button-secondary'
});
if (confirmed === false) return;
this.loading = true;
this.$api.call(
ApiAbgabe.postStudentProjektarbeitTitel(
this.projektarbeit.projektarbeit_id,
validation.cleanedTitle
)
).then(res => {
if (res.meta.status === 'success') {
this.projektarbeit.titel = res.data;
this.$emit('titel-updated', {
projektarbeit_id: this.projektarbeit.projektarbeit_id,
titel: res.data
});
this.$fhcAlert.alertSuccess(this.$capitalize(this.$p.t('abgabetool/c4titelSavedSuccess')));
this.$refs.modalTitelEdit.hide();
} else {
this.$fhcAlert.alertError(this.$capitalize(this.$p.t('abgabetool/c4titelSaveError')));
}
}).finally(() => {
this.loading = false;
});
},
getNoteBezeichnung(termin){
const noteOpt = this.notenOptions.find(opt => opt.note == termin.note)
if(noteOpt?.bezeichnung) {
return noteOpt?.positiv ? this.$capitalize(this.$p.t('abgabetool/c4positivBenotet')) + ' ✅' : this.$capitalize(this.$p.t('abgabetool/c4negativBenotet')) + ' ❌'
} else if(noteOpt?.benotbar === true && !termin.note) {
@@ -190,7 +65,7 @@ export const AbgabeStudentDetail = {
this.$fhcAlert.alertWarning(this.$capitalize(this.$p.t('global/warningChooseFile')));
return false
}
if(endupload) {
if(await this.$fhcAlert.confirm({
message: this.$p.t('abgabetool/confirmEnduploadSpeichern'),
@@ -202,16 +77,16 @@ export const AbgabeStudentDetail = {
return false
}
}
return true;
},
async triggerEndupload() {
if (!await this.validate(this.enduploadTermin, true))
{
return false;
}
// post endabgabe
const formData = new FormData();
formData.append('paabgabetyp_kurzbz', this.enduploadTermin.paabgabetyp_kurzbz)
@@ -219,14 +94,14 @@ export const AbgabeStudentDetail = {
formData.append('paabgabe_id', this.enduploadTermin.paabgabe_id)
formData.append('student_uid', this.projektarbeit.student_uid)
formData.append('bperson_id', this.projektarbeit.bperson_id)
formData.append('sprache', this.form['sprache'].sprache)
formData.append('abstract', this.form['abstract'])
formData.append('abstract_en', this.form['abstract_en'])
formData.append('schlagwoerter', this.form['schlagwoerter'])
formData.append('schlagwoerter_en', this.form['schlagwoerter_en'])
formData.append('seitenanzahl', this.form['seitenanzahl'])
for (let i = 0; i < this.enduploadTermin.file.length; i++) {
formData.append('file', this.enduploadTermin.file[i]);
}
@@ -234,31 +109,39 @@ export const AbgabeStudentDetail = {
this.$api.call(ApiAbgabe.postStudentProjektarbeitEndupload(formData))
.then(res => {
this.handleUploadRes(res, this.enduploadTermin)
if(this.confetti_on_endupload && res.meta.status == "success") {
this.confettiCannons()
}
}).finally(()=> {
this.loading = false
this.loading = false
})
this.$refs.modalContainerEnduploadZusatzdaten.hide()
},
downloadAbgabe(termin) {
const url = `/api/frontend/v1/Abgabe/getStudentProjektarbeitAbgabeFile?paabgabe_id=${termin.paabgabe_id}&student_uid=${this.projektarbeit.student_uid}&projektarbeit_id=${this.projektarbeit.projektarbeit_id}`;
window.open(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + url)
// this.$api.call(ApiAbgabe.getStudentProjektarbeitAbgabeFile(termin.paabgabe_id, this.projektarbeit.student_uid))
},
formatDate(dateParam) {
return formatISODate(dateParam)
const date = new Date(dateParam)
// handle missing leading 0
const padZero = (num) => String(num).padStart(2, '0');
const month = padZero(date.getMonth() + 1); // Months are zero-based
const day = padZero(date.getDate());
const year = date.getFullYear();
return `${day}.${month}.${year}`
},
async upload(termin) {
// only do this on endupload
if (! await this.validate(termin))
{
return false;
}
if(termin.bezeichnung?.paabgabetyp_kurzbz === 'end') {
// open endupload form modal for further inputs
this.enduploadTermin = termin
this.$refs.modalContainerEnduploadZusatzdaten.show()
} else {
@@ -268,7 +151,7 @@ export const AbgabeStudentDetail = {
formData.append('paabgabe_id', termin.paabgabe_id)
formData.append('student_uid', this.projektarbeit.student_uid)
formData.append('bperson_id', this.projektarbeit.bperson_id)
for (let i = 0; i < termin.file.length; i++) {
formData.append('file', termin.file[i]);
}
@@ -278,7 +161,7 @@ export const AbgabeStudentDetail = {
.then(res => {
this.handleUploadRes(res, termin)
}).finally(()=> {
this.loading = false
this.loading = false
})
}
},
@@ -286,18 +169,21 @@ export const AbgabeStudentDetail = {
if(res.meta.status == "success") {
this.$fhcAlert.alertSuccess(this.$capitalize(this.$p.t('abgabetool/c4fileUploadSuccessv3')))
termin.abgabedatum = getViennaTodayISO();
// update 'abgabedatum' for successful upload -> shows the pdf icon and date once set
termin.abgabedatum = new Date().toISOString().split('T')[0];
if(res?.data?.signatur !== undefined) {
termin.signatur = res.data.signatur
}
} else {
this.$fhcAlert.alertError(this.$capitalize(this.$p.t('abgabetool/c4fileUploadErrorv3')))
}
if(res.meta.signaturInfo) {
this.$fhcAlert.alertInfo(res.meta.signaturInfo)
}
},
getOptionLabel(option) {
return option.sprache
@@ -309,6 +195,7 @@ export const AbgabeStudentDetail = {
},
watch: {
projektarbeit(newVal) {
// default select german if projektarbeit sprache was null
this.form.sprache = newVal.sprache ? this.allActiveLanguages.find(lang => lang.sprache == newVal.sprache) : this.allActiveLanguages.find(lang => lang.sprache == 'German')
this.form.abstract = newVal.abstract ?? ''
this.form.abstract_en = newVal.abstract_en ?? ''
@@ -316,22 +203,15 @@ export const AbgabeStudentDetail = {
this.form.schlagwoerter_en = newVal.schlagwoerter_en ?? ''
this.form.kontrollschlagwoerter = newVal.kontrollschlagwoerter ?? ''
this.form.seitenanzahl = newVal.seitenanzahl ?? 1
}
},
computed: {
getSignaturInfoLink() {
if(this.$p.user_language.value == 'German' && this.siginfolink_german) return this.siginfolink_german
else if (this.$p.user_language.value == 'English' && this.siginfolink_english) return this.siginfolink_english
},
getSignaturInfoAvailable() {
if(this.$p.user_language.value == 'German' && this.siginfolink_german) return true
else if (this.$p.user_language.value == 'English' && this.siginfolink_english) return true
else return false
},
getMoodleLink() {
return this.moodle_link + this.projektarbeit.studiengang_kz
return this.moodle_link + this.projektarbeit.studiengang_kz
},
getMessagePtStyle() {
// adjust outer spacing and internal padding to appear similar to doenload button in size
return {
root: {
style: {
@@ -364,84 +244,102 @@ export const AbgabeStudentDetail = {
})
return qgatefound
},
isTitelEditAllowed() {
return this.title_edit_allowed && !this.isViewMode && !this.projektarbeit?.note;
},
getTooltipVerspaetet() {
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipVerspaetet')), class: "custom-tooltip" }
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipVerspaetet')),
class: "custom-tooltip"
}
},
getTooltipVerpasst() {
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipVerpasst')), class: "custom-tooltip" }
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipVerpasst')),
class: "custom-tooltip"
}
},
getTooltipAbzugeben() {
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipAbzugeben')), class: "custom-tooltip" }
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipAbzugeben')),
class: "custom-tooltip"
}
},
getTooltipStandard() {
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipStandardv2')), class: "custom-tooltip" }
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipStandardv2')),
class: "custom-tooltip"
}
},
getTooltipAbgegeben() {
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipAbgegeben')), class: "custom-tooltip" }
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipAbgegeben')),
class: "custom-tooltip"
}
},
getTooltipFixtermin() {
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipFixtermin')), class: "custom-tooltip" }
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipFixtermin')),
class: "custom-tooltip"
}
},
getTooltipAbgabeDetected() {
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipAbgabeDetected')), class: "custom-tooltip" }
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipAbgabeDetected')),
class: "custom-tooltip"
}
},
getTooltipNotAllowedToUpload() {
if(this.isViewMode) {
return { value: this.$capitalize(this.$p.t('abgabetool/c4studentAbgabeNotAllowedInViewMode')), class: "custom-tooltip" }
return {
value: this.$capitalize(this.$p.t('abgabetool/c4studentAbgabeNotAllowedInViewMode')),
class: "custom-tooltip"
}
} else {
return { value: this.$capitalize(this.$p.t('abgabetool/c4studentAbgabeNotAllowedRegular')), class: "custom-tooltip" }
return {
value: this.$capitalize(this.$p.t('abgabetool/c4studentAbgabeNotAllowedRegular')),
class: "custom-tooltip"
}
}
},
getTooltipBeurteilungerforderlich() {
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipBeurteilungerforderlich')), class: "custom-tooltip" }
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipBeurteilungerforderlich')),
class: "custom-tooltip"
}
},
getTooltipBestanden() {
return { value: this.$p.t('abgabetool/c4tooltipBestanden'), class: "custom-tooltip" }
return {
value: this.$p.t('abgabetool/c4tooltipBestanden'),
class: "custom-tooltip"
}
},
getTooltipNichtBestanden() {
return { value: this.$p.t('abgabetool/c4tooltipNichtBestanden'), class: "custom-tooltip" }
return {
value: this.$p.t('abgabetool/c4tooltipNichtBestanden'),
class: "custom-tooltip"
}
},
},
created() {
},
mounted() {
},
template: `
<FhcOverlay :active="loading"></FhcOverlay>
<div v-if="projektarbeit">
<h5>{{$capitalize( $p.t('abgabetool/c4abgabeStudentenbereichv2') )}}</h5>
<h5>{{$capitalize( $p.t('abgabetool/c4abgabeStudentenbereich') )}}</h5>
<div class="row">
<div class="col-8">
<p>{{$capitalize( $p.t('person/student') ) }}: {{projektarbeit?.student}}</p>
<p class="d-flex align-items-center gap-2 mb-2" style="min-width: 0;">
<span
:title="projektarbeit.titel"
style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 480px;"
>{{$capitalize( $p.t('abgabetool/c4titel') ) }}: {{projektarbeit?.titel}}</span>
<button
v-if="isTitelEditAllowed"
class="btn btn-sm btn-outline-secondary border-0 p-1"
v-tooltip.right="{ value: $capitalize($p.t('abgabetool/c4titelBearbeiten')), class: 'custom-tooltip' }"
@click="openTitelEdit"
>
<i class="fa-solid fa-pen"></i>
</button>
</p>
<p>{{$capitalize( $p.t('abgabetool/c4betreuerv2') ) }}: {{projektarbeit ? $p.t('abgabetool/c4betrart' + projektarbeit.betreuerart_kurzbz) + ' ' + projektarbeit.betreuer : ''}}</p>
<p> {{$capitalize( $p.t('person/student') ) }}: {{projektarbeit?.student}}</p>
<p> {{$capitalize( $p.t('abgabetool/c4titel') ) }}: {{projektarbeit?.titel}}</p>
<p> {{$capitalize( $p.t('abgabetool/c4betreuerv2') ) }}: {{projektarbeit ? $p.t('abgabetool/c4betrart' + projektarbeit.betreuerart_kurzbz) + ' ' + projektarbeit.betreuer : ''}}</p>
</div>
<div class="col-4">
<div class="row">
<p>{{ $p.t('abgabetool/c4checkoutStgMoodleInfos') }}
<a :href="getMoodleLink" target="_blank">Moodle</a>
</p>
</div>
<div class="row" v-if="getSignaturInfoAvailable">
<a :href="getSignaturInfoLink" target="_blank">{{$p.t('abgabetool/c4signaturinfo')}} <i class="fa-solid fa-circle-info"></i></a>
</div>
<p>{{ $p.t('abgabetool/c4checkoutStgMoodleInfos') }}
<a :href="getMoodleLink" target="_blank">Moodle</a>
</p>
</div>
</div>
@@ -459,6 +357,7 @@ export const AbgabeStudentDetail = {
<i v-else-if="termin.dateStyle == 'beurteilungerforderlich'" v-tooltip.right="getTooltipBeurteilungerforderlich" class="fa-solid fa-list-check"></i>
<i v-else-if="termin.dateStyle == 'bestanden'" v-tooltip.right="getTooltipBestanden" class="fa-solid fa-check"></i>
<i v-else-if="termin.dateStyle == 'nichtbestanden'" v-tooltip.right="getTooltipNichtBestanden" class="fa-solid fa-circle-exclamation"></i>
</div>
<div class="text-start px-2" style="min-width: 150px; max-width: 300px; margin-left: 40px">
<span>{{ termin ? $p.t('abgabetool/c4paatyp' + termin.paabgabetyp_kurzbz) : '' }}</span>
@@ -509,6 +408,8 @@ export const AbgabeStudentDetail = {
</div>
</template>
</Inplace>
</div>
<div class="row mt-2">
@@ -529,7 +430,7 @@ export const AbgabeStudentDetail = {
</VueDatePicker>
</div>
</div>
<div class="row mt-2">
<div class="col-12 col-md-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4abgabetyp') )}}</div>
<div class="col-12 col-md-9">
@@ -565,7 +466,7 @@ export const AbgabeStudentDetail = {
<div class="col-12 col-md-9">
<template v-if="termin?.abgabedatum">
<div class="row">
<div style="width:100px; align-content: end;">
<div style="width:100px; align-content: center;">
<h6>{{ termin.abgabedatum?.split("-").reverse().join(".") }}</h6>
</div>
@@ -580,6 +481,9 @@ export const AbgabeStudentDetail = {
<Message v-else-if="termin?.signatur == false" severity="error" :closable="false" :pt="getMessagePtStyle"> {{ $p.t('abgabetool/c4keineSignatur') }} </Message>
<Message v-else-if="termin?.signatur == 'error'" severity="warn" :closable="false" :pt="getMessagePtStyle"> {{ $p.t('abgabetool/c4signaturServerError') }} </Message>
</div>
<!-- <div v-else class="col-auto">-->
<!-- <Message severity="info" :closable="false" :pt="getMessagePtStyle"> {{ $p.t('abgabetool/c4noFileFound') }} </Message>-->
<!-- </div>-->
</template>
</div>
</template>
@@ -638,66 +542,25 @@ export const AbgabeStudentDetail = {
<h5>{{ $capitalize( $p.t('abgabetool/c4keineAbgabetermineGefunden') )}}</h5>
</div>
</div>
<div v-if="confetti_on_endupload" id="confetti-container"></div>
<bs-modal
ref="modalTitelEdit"
class="bootstrap-prompt"
dialogClass="bordered-modal"
bodyClass="px-4 py-4"
>
<template v-slot:title>
{{$capitalize( $p.t('abgabetool/c4titelBearbeiten') )}}
</template>
<template v-slot:default>
<div class="mb-2">
<label class="form-label fw-bold">
{{$capitalize( $p.t('abgabetool/c4titel') )}}
</label>
<Textarea
v-model="editingTitel"
rows="10"
maxlength="1024"
class="form-control w-100"
@keydown.enter.prevent="saveTitel"
/>
<div class="form-text text-end">{{ editingTitel.length }} / 1024</div>
</div>
</template>
<template v-slot:footer>
<button
class="btn btn-secondary"
@click="$refs.modalTitelEdit.hide()"
>
{{$capitalize( $p.t('abgabetool/c4Cancel') )}}
</button>
<button
class="btn btn-primary"
:disabled="!editingTitel.trim()"
@click="saveTitel"
>
<i class="fa-solid fa-floppy-disk me-1"></i>
{{$capitalize( $p.t('ui/speichern') )}}
</button>
</template>
</bs-modal>
</div>
<bs-modal
ref="modalContainerEnduploadZusatzdaten"
class="bootstrap-prompt"
dialogClass="bordered-modal modal-lg"
bodyClass="px-4 py-4">
dialogClass="bordered-modal modal-lg">
<template v-slot:title>
<div>
{{$capitalize( $p.t('abgabetool/c4enduploadZusatzdaten') )}}
</div>
<div class="row mb-3 align-items-start">
<p class="ml-4 mr-4">Student UID: {{ projektarbeit?.student_uid}}</p>
</div>
<div class="row mb-3 align-items-start">
<p class="ml-4 mr-4">{{$capitalize( $p.t('abgabetool/c4titel') )}}: {{ projektarbeit?.titel }}</p>
</div>
</template>
<template v-slot:default>
@@ -713,6 +576,15 @@ export const AbgabeStudentDetail = {
</div>
</div>
<!-- lektor fills these out-->
<!-- <div class="row mb-3 align-items-start">-->
<!-- <div class="row">Kontrollierte Schlagwörter</div>-->
<!-- <div class="row">-->
<!-- <Textarea v-model="form.kontrollschlagwoerter"></Textarea>-->
<!-- </div>-->
<!-- -->
<!-- -->
<!-- </div>-->
<div class="row mb-3 align-items-start">
<div class="row">{{$capitalize( $p.t('abgabetool/c4schlagwoerterGer') )}}</div>
<div class="row">
@@ -759,6 +631,7 @@ export const AbgabeStudentDetail = {
<div class="col-9"></div>
<div class="col-2"><p>{{$capitalize( $p.t('abgabetool/c4gelesenUndAkzeptiert') )}}</p></div>
<div class="col-1">
<Checkbox
v-model="eidAkzeptiert"
:binary="true"
@@ -774,8 +647,8 @@ export const AbgabeStudentDetail = {
<div v-show="!allowedToSaveZusatzdaten">{{ $p.t('abgabetool/c4zusatzdatenausfuellen') }}</div>
<button class="btn btn-primary" :disabled="!getAllowedToSendEndupload" @click="triggerEndupload">{{$capitalize( $p.t('ui/hochladen') )}}</button>
</template>
</bs-modal>
`,
};
File diff suppressed because it is too large Load Diff
@@ -7,7 +7,6 @@ import FhcOverlay from "../../Overlay/FhcOverlay.js";
import { getDateStyleClass } from "./getDateStyleClass.js";
import { dateFilter } from '../../../tabulator/filters/Dates.js';
import {splitMailsHelper} from "../../../helpers/EmailHelpers.js";
import { formatISODate, getViennaTodayISO, toViennaDate } from "./dateUtils.js";
export const AbgabetoolMitarbeiter = {
name: "AbgabetoolMitarbeiter",
@@ -32,14 +31,19 @@ export const AbgabetoolMitarbeiter = {
old_abgabe_beurteilung_link: Vue.computed(() => this.old_abgabe_beurteilung_link)
}
},
props: {
viewData: {
type: Object,
required: true,
default: () => ({name: '', uid: ''}),
validator(value) {
return value && value.uid // && value.name -> extensive viewData use only for cis4 onwards
}
}
},
data() {
return {
filteredRows: null,
count: 0,
filteredcount: 0,
selectedcount: 0,
qgate1FilterSelected: [],
qgate2FilterSelected: [],
tableData: null,
abgabetypenBetreuer: null,
detailIsFullscreen: false,
phrasenPromise: null,
@@ -54,7 +58,7 @@ export const AbgabetoolMitarbeiter = {
allowedNotenOptions: null,
notenOptionsNonFinal: null,
serienTermin: Vue.reactive({
datum: getViennaTodayISO(),
datum: new Date(),
bezeichnung: {
paabgabetyp_kurzbz: 'zwischen',
bezeichnung: 'Zwischenabgabe'
@@ -76,7 +80,7 @@ export const AbgabetoolMitarbeiter = {
abgabeTableOptions: {
minHeight: 250,
index: 'projektarbeit_id',
layout: 'fitData',
layout: 'fitDataStretch',
placeholder: Vue.computed(() => this.$p.t('global/noDataAvailable')),
selectable: true,
selectableCheck: this.selectionCheck,
@@ -134,65 +138,38 @@ export const AbgabetoolMitarbeiter = {
handleClick: this.selectAllHandler
},
width: 50,
cssClass: 'sticky-col',
visible: true
cssClass: 'sticky-col'
},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', formatter: this.formAction, headerFilter: false, headerSort: false, minWidth: 85, visible: true, tooltip: false, cssClass: 'sticky-col'},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4personenkennzeichen'))), headerFilter: true, field: 'pkz', formatter: this.pkzTextFormatter, minWidth: 140, visible: false,tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'vorname', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 100,visible: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'nachname', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 100,visible: true},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4projekttyp'))), field: 'projekttyp_kurzbz', formatter: this.centeredTextFormatter, minWidth: 100,visible: true},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4stg'))), field: 'stg', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 50, visible: true},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4sem'))), field: 'studiensemester_kurzbz', headerFilter: true, formatter: this.centeredTextFormatter, visible: true, minWidth: 100},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4titel'))), field: 'titel', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 100, width: 500, visible: true},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4betreuerartv2'))), field: 'betreuerart_beschreibung',formatter: this.centeredTextFormatter, visible: true, minWidth: 100, width: 200},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4prevAbgabetermin'))),
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', formatter: this.detailFormatter, headerFilter: false, headerSort: false, widthGrow: 1, tooltip: false, cssClass: 'sticky-col'},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4personenkennzeichen'))), headerFilter: true, field: 'pkz', formatter: this.pkzTextFormatter, widthGrow: 1, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'vorname', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4projekttyp'))), field: 'projekttyp_kurzbz', formatter: this.centeredTextFormatter, widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4stg'))), field: 'stg', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4sem'))), field: 'studiensemester_kurzbz', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4titel'))), field: 'titel', headerFilter: true, formatter: this.centeredTextFormatter, maxWidth: 500, widthGrow: 8},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4betreuerartv2'))), field: 'betreuerart_beschreibung',formatter: this.centeredTextFormatter, widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4prevAbgabetermin'))), field: 'prevTermin',
headerFilter: dateFilter,
headerFilterFunc: this.headerFilterTerminCol,
sorter: this.sortFuncTerminCol,
tooltip: this.toolTipFuncPrevTermin,
field: 'prevTermin', formatter: this.abgabterminFormatter, width: 250, visible: false},
formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nextAbgabetermin'))), field: 'nextTermin',
headerFilter: dateFilter,
headerFilterFunc: this.headerFilterTerminCol,
sorter: this.sortFuncTerminCol,
tooltip: this.toolTipFuncNextTermin,
formatter: this.abgabterminFormatter, width: 250, visible: true},
formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate1Status'))),
headerFilter: this.qgateHeaderFilterEditor,
headerFilterFunc: this.qgateHeaderFilterFunc,
headerFilterParams: {},
field: 'qgate1Status',
formatter: this.centeredTextFormatter,
titleFormatter: this.shortLongTitleFormatter,
titleFormatterParams: {
shortForm: 'QG1'
},
width: 50,
tooltip: (e, cell) => {
const data = cell.getData();
return data.qgate1Status
}
},
headerFilter: 'list',
headerFilterParams: { valuesLookup: this.getQGateStatusList },
field: 'qgate1Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate2Status'))),
headerFilter: this.qgateHeaderFilterEditor,
headerFilterFunc: this.qgateHeaderFilterFunc,
headerFilterParams: {},
field: 'qgate2Status',
formatter: this.centeredTextFormatter,
titleFormatter: this.shortLongTitleFormatter,
titleFormatterParams: {
shortForm: 'QG2'
},
width: 50,
tooltip: (e, cell) => {
const data = cell.getData();
return data.qgate2Status
}
}
headerFilter: 'list',
headerFilterParams: { valuesLookup: this.getQGateStatusList },
field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false}
],
persistence: false,
persistenceID: 'abgabeTableBetreuer2026-05-26'
persistenceID: 'abgabeTableBetreuer2026-02-26'
},
abgabeTableEventHandlers: [{
event: "tableBuilt",
@@ -223,364 +200,11 @@ export const AbgabetoolMitarbeiter = {
})
this.selectedData = data
this.selectedcount = data.length;
}
},
{
event: 'dataFiltered',
handler: (filters, rows) => {
this.filteredRows = rows;
this.filteredcount = rows.length;
if (!this.selectedData.length) return;
const visibleData = new Set(rows.map(r => r.getData()));
const filteredOut = this.selectedData.filter(sd => !visibleData.has(sd));
if (!filteredOut.length) return;
const filteredOutSet = new Set(filteredOut);
this.$refs.abgabeTable.tabulator.getSelectedRows()
.filter(r => filteredOutSet.has(r.getData()))
.forEach(r => r.deselect());
}
}
]};
},
methods: {
async openBenotung(type, link) {
if(type === 'new') {
window.open(link, '_blank')
} else if(type === 'old') {
if(await this.$fhcAlert.confirm({
message: this.$p.t('abgabetool/c4aeltereParbeitBenotenv2'),
acceptLabel: this.$capitalize(this.$p.t('abgabetool/c4AcceptAndProceed')),
acceptClass: 'btn btn-danger',
rejectLabel: this.$capitalize(this.$p.t('abgabetool/c4Cancel')),
rejectClass: 'btn btn-outline-secondary'
}) === false) {
return false
}
window.open(link, '_blank')
} else {
// show info text that no endupload with abgabe has been found
if(await this.$fhcAlert.confirm({
message: this.$p.t('abgabetool/c4keinEnduploadErfolgt'),
acceptLabel: this.$capitalize(this.$p.t('abgabetool/c4AcceptAndProceed')),
acceptClass: 'btn btn-danger',
rejectLabel: this.$capitalize(this.$p.t('abgabetool/c4Cancel')),
rejectClass: 'btn btn-outline-secondary'
}) === false) {
return false
}
}
},
formAction(cell) {
const actionButtons = document.createElement('div');
actionButtons.className = "d-flex gap-3";
actionButtons.style.display = "flex";
actionButtons.style.alignItems = "stretch";
actionButtons.style.justifyContent = "start";
actionButtons.style.height = "100%";
const val = cell.getValue();
const data = cell.getRow().getData()
const createButton = (iconClass, titleKey, clickHandler) => {
const btn = document.createElement('button');
btn.className = 'btn btn-outline-secondary';
btn.style.display = "flex";
btn.style.alignItems = "center"; // center icon vertically
btn.style.justifyContent = "center"; // center icon horizontally
btn.style.height = "100%"; // fill parent container height
btn.style.aspectRatio = "1 / 1"; // keep square shape (optional)
btn.style.padding = "0"; // remove extra padding for compactness
if(iconClass == 'fa fa-timeline') btn.style.transform = "rotate(90deg)";
btn.innerHTML = `<i class="${iconClass}" style="color:#00649C; font-size:1.1rem;"></i>`;
btn.title = this.$capitalize(this.$p.t(titleKey));
btn.addEventListener('click', (e) => {
e.stopPropagation();
e.stopImmediatePropagation();
clickHandler();
});
return btn;
};
actionButtons.append(
createButton('fa fa-folder-open', 'abgabetool/c4details', () => this.setDetailComponent(val)),
);
if(data.isCurrent && data.abgabetermine?.find(termin => termin.paabgabetyp_kurzbz == 'end' && termin.abgabedatum !== null) && data.beurteilungLinkNew) {
actionButtons.append(createButton('fa fa-user-check', 'abgabetool/c4benoten', () => this.openBenotung('new', data.beurteilungLinkNew)))
} else if(data.abgabetermine?.find(termin => termin.paabgabetyp_kurzbz == 'end' && termin.abgabedatum !== null) && data.beurteilungLinkOld) {
actionButtons.append(createButton('fa fa-user-check', 'abgabetool/c4benoten', () => this.openBenotung('old', data.beurteilungLinkOld)))
}
return actionButtons;
},
getDateStyleHtml(dateStyle) {
const iconMap = {
'verspaetet': '<i class="fa-solid fa-triangle-exclamation"></i>',
'verpasst': '<i class="fa-solid fa-calendar-xmark"></i>',
'abzugeben': '<i class="fa-solid fa-hourglass-half"></i>',
'standard': '<i class="fa-solid fa-clock"></i>',
'abgegeben': '<i class="fa-solid fa-paperclip"></i>',
'beurteilungerforderlich': '<i class="fa-solid fa-list-check"></i>',
'bestanden': '<i class="fa-solid fa-check"></i>',
'nichtbestanden': '<i class="fa-solid fa-circle-exclamation"></i>',
};
return iconMap[dateStyle] ?? '';
},
statusHeaderFilterEditor(cell, onRendered, success, cancel, editorParams) {
const options = [
{ label: this.$p.t('abgabetool/c4positivBenotet'), value: 'bestanden', dateStyle: 'bestanden' },
{ label: this.$p.t('abgabetool/c4negativBenotet'), value: 'nichtbestanden', dateStyle: 'nichtbestanden' },
{ label: this.$p.t('abgabetool/c4tooltipVerspaetet'), value: 'verspaetet', dateStyle: 'verspaetet' },
{ label: this.$p.t('abgabetool/c4tooltipVerpasst'), value: 'verpasst', dateStyle: 'verpasst' },
{ label: this.$p.t('abgabetool/c4tooltipAbzugeben'), value: 'abzugeben', dateStyle: 'abzugeben' },
{ label: this.$p.t('abgabetool/c4tooltipAbgegeben'), value: 'abgegeben', dateStyle: 'abgegeben' },
{ label: this.$p.t('abgabetool/c4tooltipBeurteilungerforderlich'), value: 'beurteilungerforderlich', dateStyle: 'beurteilungerforderlich' },
{ label: this.$p.t('abgabetool/c4tooltipStandardv2'), value: 'standard', dateStyle: 'standard' },
];
const field = cell.getField();
const stateKey = field + 'FilterSelected'; // e.g. dateStyleFilterSelected
let selected = [...(this[stateKey] || [])];
const wrapper = document.createElement('div');
wrapper.style.cssText = 'position: relative; width: 100%;';
const display = document.createElement('input');
display.readOnly = true;
display.placeholder = '';
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: #fff; appearance: none; caret-color: transparent;';
const dropdown = document.createElement('div');
dropdown.style.cssText = 'display: none; position: fixed; background: #fff; border: 1px solid; z-index: 9999; min-width: 220px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
const updateDisplay = () => {
display.value = options
.filter(o => selected.includes(o.value))
.map(o => o.label)
.join(', ');
};
options.forEach(opt => {
const row = document.createElement('label');
row.style.cssText = 'display: flex; align-items: center; gap: 0; cursor: pointer; white-space: nowrap; padding-right: 8px;';
row.addEventListener('mousedown', e => e.preventDefault());
const cb = document.createElement('input');
cb.type = 'checkbox';
cb.value = opt.value;
cb.checked = selected.includes(opt.value);
cb.style.cssText = 'margin: 0 6px;';
cb.addEventListener('change', () => {
selected = cb.checked
? [...selected, opt.value]
: selected.filter(v => v !== opt.value);
this[stateKey] = [...selected];
updateDisplay();
success([...selected]);
});
// icon badge — same look as cell
const badge = document.createElement('div');
badge.className = opt.dateStyle + '-header';
badge.style.cssText = `min-width: 36px; height: 36px; display: flex; align-items: center;
justify-content: center; flex-shrink: 0; padding: 0px 17px 0px 17px;`;
badge.innerHTML = this.getDateStyleHtml(opt.dateStyle);
const labelText = document.createElement('span');
labelText.textContent = opt.label;
labelText.style.cssText = 'margin-left: 6px;';
row.appendChild(cb);
row.appendChild(badge);
row.appendChild(labelText);
dropdown.appendChild(row);
});
updateDisplay();
display.addEventListener('click', () => {
if (dropdown.style.display === 'none') {
const rect = display.getBoundingClientRect();
dropdown.style.top = rect.bottom + 'px';
dropdown.style.left = rect.left + 'px';
dropdown.style.display = 'block';
} else {
dropdown.style.display = 'none';
}
});
display.addEventListener('blur', () => {
setTimeout(() => { dropdown.style.display = 'none'; }, 150);
});
document.body.appendChild(dropdown);
wrapper.appendChild(display);
cell.getElement().addEventListener('remove', () => dropdown.remove());
onRendered(() => display.focus());
return wrapper;
},
statusHeaderFilterFunc(filterVal, rowVal, rowData, filterParams) {
if (!filterVal || !filterVal.length) return true;
// rowVal is the raw dateStyle string on the flat table
return filterVal.some(val => val === rowVal);
},
qgateHeaderFilterEditor(cell, onRendered, success, cancel, editorParams) {
const options = [
{ label: '[+] ' + this.$p.t('abgabetool/c4positivBenotet'), value: 'positive' },
{ label: '[-] ' + this.$p.t('abgabetool/c4negativBenotet'), value: 'negative' },
{ label: '[~] ' + this.$p.t('abgabetool/c4notYetGraded'), value: 'not_graded' },
{ label: '[?] ' + this.$p.t('abgabetool/c4notSubmitted'), value: 'not_submitted' },
{ label: '[o] ' + this.$p.t('abgabetool/c4notHappenedYet'), value: 'not_happened' },
{ label: '[--] ' + this.$p.t('abgabetool/c4keinTerminVorhanden'), value: 'no_termin' },
];
const field = cell.getField();
const stateKey = field === 'qgate1Status' ? 'qgate1FilterSelected' : 'qgate2FilterSelected';
let selected = [...(this[stateKey] || [])]; // restore persistence state
const wrapper = document.createElement('div');
wrapper.style.cssText = 'position: relative; width: 100%;';
const display = document.createElement('input');
display.readOnly = true;
display.placeholder = '';
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: #fff; appearance: none; caret-color: transparent;';
const dropdown = document.createElement('div');
dropdown.style.cssText = 'display: none; position: fixed; background: #fff; border: 1px solid; z-index: 9999; min-width: 180px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
options.forEach(opt => {
const row = document.createElement('label');
row.style.cssText = 'display: flex; align-items: center; gap: 6px; padding: 4px 8px; cursor: pointer; white-space: nowrap;';
row.addEventListener('mousedown', e => e.preventDefault());
const cb = document.createElement('input');
cb.type = 'checkbox';
cb.value = opt.value;
cb.checked = selected.includes(opt.value); // sync with persistence
cb.addEventListener('change', () => {
if (cb.checked) {
selected.push(opt.value);
} else {
selected = selected.filter(v => v !== opt.value);
}
this[stateKey] = [...selected]; // sync with persistence
display.value = options.filter(o => selected.includes(o.value)).map(o => o.label).join(', ');
success([...selected]);
});
row.appendChild(cb);
row.appendChild(document.createTextNode(opt.label));
dropdown.appendChild(row);
});
display.value = options.filter(o => selected.includes(o.value)).map(o => o.label).join(', ');
display.addEventListener('click', () => {
if (dropdown.style.display === 'none') {
const rect = display.getBoundingClientRect();
dropdown.style.top = rect.bottom + 'px';
dropdown.style.left = rect.left + 'px';
dropdown.style.display = 'block';
} else {
dropdown.style.display = 'none';
}
});
display.addEventListener('blur', () => {
setTimeout(() => { dropdown.style.display = 'none'; }, 150);
});
document.body.appendChild(dropdown);
wrapper.appendChild(display);
cell.getElement().addEventListener('remove', () => dropdown.remove());
onRendered(() => display.focus());
return wrapper;
},
qgateHeaderFilterFunc(filterVal, rowVal, rowData, filterParams) {
if (!filterVal || !filterVal.length) return true;
const matches = (val) => {
switch (val) {
case 'positive': return rowVal === this.$p.t('abgabetool/c4positivBenotet');
case 'negative': return rowVal === this.$p.t('abgabetool/c4negativBenotet');
case 'not_graded': return rowVal === this.$p.t('abgabetool/c4notYetGraded');
case 'not_submitted':return rowVal === this.$p.t('abgabetool/c4notSubmitted');
case 'not_happened': return rowVal === this.$p.t('abgabetool/c4notHappenedYet');
case 'no_termin': return rowVal === this.$p.t('abgabetool/c4keinTerminVorhanden');
default: return true;
}
};
// OR logic — row passes if it matches any selected filter
return filterVal.some(val => matches(val));
},
shortLongTitleFormatter(cell, formatterParams, onRendered) {
const longForm = cell.getValue()
const shortForm = formatterParams?.shortForm
if(longForm && shortForm) {
return `<span class="full-text" style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; margin: 0px;">
${longForm}
</span>
<span class="short-text" style="font-weight: bold; display: none;">
${shortForm}
</span>`
} else {
return `<span class="full-text" style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; margin: 0px;">
${longForm}
</span>`
}
},
toolTipFuncPrevTermin(e, cell, onRendered) {
const data = cell.getData();
return this.mapDateStyleToTabulatorTooltip(data.prevTermin.dateStyle);
},
toolTipFuncNextTermin(e, cell, onRendered) {
const data = cell.getData();
return this.mapDateStyleToTabulatorTooltip(data.nextTermin.dateStyle);
},
mapDateStyleToTabulatorTooltip(dateStyleString) {
switch(dateStyleString) {
case 'bestanden':
return this.$p.t('abgabetool/c4tooltipBestanden')
break;
case 'nichtbestanden':
return this.$p.t('abgabetool/c4tooltipNichtBestanden')
break;
case 'beurteilungerforderlich':
return this.$p.t('abgabetool/c4tooltipBeurteilungerforderlich')
break;
case 'verspaetet':
return this.$p.t('abgabetool/c4tooltipVerspaetet')
break;
case 'abgegeben':
return this.$p.t('abgabetool/c4tooltipAbgegeben')
break;
case 'verpasst':
return this.$p.t('abgabetool/c4tooltipVerpasst')
break;
case 'abzugeben':
return this.$p.t('abgabetool/c4tooltipAbzugeben')
break;
case 'standard':
return this.$p.t('abgabetool/c4tooltipStandardv2')
break;
default: return ''
}
},
handlePaUpdated(projektarbeit) {
this.checkAbgabetermineProjektarbeit(projektarbeit)
this.$refs.abgabeTable.tabulator.redraw(true)
@@ -593,7 +217,7 @@ export const AbgabetoolMitarbeiter = {
})
const uniqueRecipients = [...new Set(recipientList)];
const subject = ""; // empty subject line
splitMailsHelper(uniqueRecipients, param.originalEvent, subject, null, this.$fhcAlert, this.$p)
splitMailsHelper(uniqueRecipients, param.originalEvent, subject, this.$fhcAlert, this.$p)
},
getQGateStatusList() {
return [
@@ -633,7 +257,7 @@ export const AbgabetoolMitarbeiter = {
if (val instanceof Date) {
dt = luxon.DateTime.fromJSDate(val);
} else if (typeof val === "string") {
dt = toViennaDate(val);
dt = luxon.DateTime.fromISO(val);
} else { // fallback
dt = luxon.DateTime.fromMillis(Number(val));
}
@@ -762,9 +386,6 @@ export const AbgabetoolMitarbeiter = {
}
this.stateRestored = true
// ensure that the filterCollapseables thingy has the correct values
this.$refs.abgabeTable.setSelectedFields();
}
});
@@ -830,32 +451,6 @@ export const AbgabetoolMitarbeiter = {
projekt.qgate2StatusRank = 1
}
})
// set shorthand statuscode once real status has been determined
projekt.qgate1StatusShort = this.mapRankToShortStatus(projekt.qgate1StatusRank)
projekt.qgate2StatusShort = this.mapRankToShortStatus(projekt.qgate2StatusRank)
},
mapRankToShortStatus(rank) {
switch(rank){
case 0: // kein termin vorhanden
return '--'
break;
case 1: // noch nicht stattgefunden
return 'o'
break;
case 2: // noch nicht abgegeben
return '?'
break;
case 3: // noch nicht benotet
return '~'
break;
case 4: // negativ benotet
return '-'
break;
case 5: // positiv benotet
return '+'
break;
}
},
checkAbgabetermineProjektarbeit(projekt) {
const now = luxon.DateTime.now()
@@ -865,7 +460,7 @@ export const AbgabetoolMitarbeiter = {
// while already looping through each termin, calculate datestyle beforehand
termin.dateStyle = getDateStyleClass(termin, this.notenOptions)
const date = toViennaDate(termin.datum).endOf('day')
const date = luxon.DateTime.fromISO(termin.datum).endOf('day')
termin.luxonDate = date
termin.diffMs = date.toMillis() - now.toMillis(); // positive = future, negative = past
@@ -922,11 +517,11 @@ export const AbgabetoolMitarbeiter = {
const bezeichnung = val.bezeichnung?.bezeichnung ?? val.bezeichnung
return '<div style="display: flex; height: 100%">' +
'<div class=' + val.dateStyle + "-header" + ' style="min-width:48px; height: 100%; padding: 0px; display: flex; align-items: center; justify-content: center;">' +
icon +
'<div class=' + val.dateStyle + "-header" + ' style="width:48px; height: 100%; padding: 0px; display: flex; align-items: center; justify-content: center;">' +
icon +
'</div>' +
'<div style="margin-left: 4px;">' +
'<p style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;">'+bezeichnung+' - '+ this.formatDate(val.datum)+'</p>' +
'<p style="max-width: 100%; word-wrap: break-word; white-space: normal;">'+bezeichnung+' - '+ this.formatDate(val.datum)+'</p>' +
'</div>'+
'</div>'
@@ -950,19 +545,16 @@ export const AbgabetoolMitarbeiter = {
},
selectAllHandler(e, cell) {
const table = cell.getTable();
const rows = this.filteredRows ?? table.getRows();
const rows = table.getRows();
// custom select all logic
const allowed = rows.filter(r => r.getData().selectable);
// since betreuerpage acctually has logic behind selectable flag, it is important to go over allowed only here
const selected = allowed.every(r => r.isSelected());
if(selected){
if(selected) {
allowed.forEach(r => r.deselect());
e.target.checked = false;
} else {
allowed.forEach(r => r.select());
e.target.checked = true;
}
// stop built-in handler
@@ -976,7 +568,15 @@ export const AbgabetoolMitarbeiter = {
return option.bezeichnung
},
formatDate(dateParam) {
return formatISODate(dateParam);
const date = new Date(dateParam)
// handle missing leading 0
const padZero = (num) => String(num).padStart(2, '0');
const month = padZero(date.getMonth() + 1); // Months are zero-based
const day = padZero(date.getDate());
const year = date.getFullYear();
return `${day}.${month}.${year}`;
},
undoSelection(cell) {
// checks if cells row is selected and unselects -> imitates columns which dont trigger row selection
@@ -989,8 +589,6 @@ export const AbgabetoolMitarbeiter = {
},
selectionCheck(row) {
const data = row.getData()
// zweitbetreuer cant select projektarbeiten for serientermine
if(data?.betreuerart_kurzbz == 'Zweitbegutachter') return false
return true
},
@@ -1014,7 +612,7 @@ export const AbgabetoolMitarbeiter = {
addSeries() {
this.saving = true
this.$api.call(ApiAbgabe.postSerientermin(
this.serienTermin.datum,
this.serienTermin.datum.toISOString(),
this.serienTermin.bezeichnung.paabgabetyp_kurzbz,
this.serienTermin.bezeichnung.bezeichnung,
this.serienTermin.kurzbz,
@@ -1112,7 +710,7 @@ export const AbgabetoolMitarbeiter = {
termin.allowedToSave = paIsBenotet ? false : true
// lektoren are not allowed to delete deadlines with existing submissions
termin.allowedToDelete = termin.allowedToSave && !termin.abgabedatum && !termin.note
termin.allowedToDelete = termin.allowedToSave && !termin.abgabedatum
termin.bezeichnung = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === termin.paabgabetyp_kurzbz)
@@ -1129,37 +727,28 @@ export const AbgabetoolMitarbeiter = {
},
centeredTextFormatter(cell) {
const longForm = cell.getValue()
if(!longForm) return
const data = cell.getData()
const entry = Object.entries(data).find(entry => entry[1] == longForm)
// shortFormKey must have same keyname as longForm but with 'Short' appended
const shortForm = data[entry[0]+'Short']
if(shortForm && longForm) {
return `<div style="display: flex; justify-content: start; align-items: center; height: 100%; width: 100%;">
<span class="full-text" style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; margin: 0px;">
${longForm}
</span>
<span class="short-text" style="font-weight: bold; display: none;">
${shortForm}
</span>
</div>`;
} else {
return '<div style="display: flex; justify-content: start; align-items: center; height: 100%">' +
'<p style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; margin: 0px;">'+longForm+'</p></div>'
}
const val = cell.getValue()
if(!val) return
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
'<p style="max-width: 100%; width: 100%; overflow-wrap: break-word; word-break: break-word; white-space: normal; margin: 0px; text-align: center">'+val+'</p></div>'
},
detailFormatter(cell) {
return '<div style="display: flex; justify-content: start; align-items: center; height: 100%">' +
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
'<a><i class="fa fa-folder-open" style="color:#00649C"></i></a></div>'
},
beurteilungFormatter(cell) {
const val = cell.getValue()
if(val) {
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
'<a><i class="fa fa-file-pdf" style="color:#00649C"></i></a></div>'
} else return '-'
},
pkzTextFormatter(cell) {
const val = cell.getValue()
return '<div style="display: flex; justify-content: start; align-items: center; height: 100%">' +
'<a style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;">'+val+'</a></div>'
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
'<a style="max-width: 100%; word-wrap: break-word; white-space: normal;">'+val+'</a></div>'
},
tableResolve(resolve) {
this.tableBuiltResolve = resolve
@@ -1176,9 +765,10 @@ export const AbgabetoolMitarbeiter = {
setupData(data){
this.projektarbeiten = data[0]
this.domain = data[1]
this.projektarbeiten = data[0]?.retval?.map(projekt => {
this.tableData = data[0]?.retval?.map(projekt => {
this.checkAbgabetermineProjektarbeit(projekt)
projekt.selectable = projekt.betreuerart_kurzbz !== 'Zweitbegutachter'
@@ -1197,10 +787,9 @@ export const AbgabetoolMitarbeiter = {
titel: projekt.titel
}
})
this.count = this.projektarbeiten.length
this.$refs.abgabeTable.tabulator.setColumns(this.abgabeTableOptions.columns)
this.$refs.abgabeTable.tabulator.setData(this.projektarbeiten);
this.$refs.abgabeTable.tabulator.setData(this.tableData);
},
loadProjektarbeiten(all = false, callback) {
this.$api.call(ApiAbgabe.getMitarbeiterProjektarbeiten(all))
@@ -1252,17 +841,6 @@ export const AbgabetoolMitarbeiter = {
},
},
computed: {
countsToHTML() {
return this.$p.t('global/ausgewaehlt')
+ ': <strong>' + (this.selectedcount || 0) + '</strong>'
+ ' | '
+ this.$p.t('global/gefiltert')
+ ': '
+ '<strong>' + (this.filteredcount || 0) + '</strong>'
+ ' | '
+ this.$p.t('global/gesamt')
+ ': <strong>' + (this.count || 0) + '</strong>';
},
emailItems() {
const menu = []
@@ -1291,8 +869,6 @@ export const AbgabetoolMitarbeiter = {
}
},
created() {
document.documentElement.classList.add('abgabetool');
this.phrasenPromise = this.$p.loadCategory(['abgabetool', 'global'])
this.phrasenPromise.then(()=> {this.phrasenResolved = true})
// fetch config to avoid hard coded links
@@ -1334,16 +910,12 @@ export const AbgabetoolMitarbeiter = {
mounted() {
this.setupMounted()
},
beforeUnmount() {
document.documentElement.classList.remove('abgabetool');
},
template: `
<template v-if="phrasenResolved">
<FhcOverlay :active="loading || saving"></FhcOverlay>
<bs-modal ref="modalContainerAddSeries" class="bootstrap-prompt"
dialogClass="modal-lg"
bodyClass="px-4 py-4">
dialogClass="modal-lg">
<template v-slot:title>
<div>
{{ $p.t('abgabetool/neueTerminserie') }}
@@ -1363,7 +935,6 @@ export const AbgabetoolMitarbeiter = {
:enable-time-picker="false"
locale="de"
format="dd.MM.yyyy"
model-type="yyyy-MM-dd"
:text-input="true"
auto-apply>
</VueDatePicker>
@@ -1411,8 +982,7 @@ export const AbgabetoolMitarbeiter = {
<bs-modal ref="modalContainerAbgabeDetail" class="bootstrap-prompt"
dialogClass="modal-xl" :allowFullscreenExpand="true"
@toggle-fullscreen="handleToggleFullscreenDetail"
bodyClass="px-4 py-4">
@toggle-fullscreen="handleToggleFullscreenDetail">
<template v-slot:title>
<div>
{{$p.t('abgabetool/c4abgabeMitarbeiterDetailTitle')}}
@@ -1431,13 +1001,12 @@ export const AbgabetoolMitarbeiter = {
<!-- low max height on this vsplit wrapper to avoid padding scrolls, elements have their inherent height anyways -->
<div id="abgabetable" style="max-height:40vw;">
<h2>{{$p.t('abgabetool/abgabetoolTitleBetreuer')}}</h2>
<h2>{{$p.t('abgabetool/abgabetoolTitle')}}</h2>
<hr>
<core-filter-cmpt
:title="''"
@uuidDefined="handleUuidDefined"
ref="abgabeTable"
:description="countsToHTML"
:newBtnShow="true"
:newBtnLabel="$p.t('abgabetool/neueTerminserie')"
:newBtnDisabled="!selectedData.length"
@@ -1,17 +1,14 @@
import AbgabeDetail from "./AbgabeStudentDetail.js";
import ApiAbgabe from '../../../api/factory/abgabe.js'
import ApiAuthinfo from '../../../api/factory/authinfo.js';
import BsModal from "../../Bootstrap/Modal.js";
import FhcOverlay from "../../Overlay/FhcOverlay.js";
import { getDateStyleClass} from "./getDateStyleClass.js";
import { validateThesisTitle } from './titleValidation.js'
export const AbgabetoolStudent = {
name: "AbgabetoolStudent",
components: {
Accordion: primevue.accordion,
AccordionTab: primevue.accordiontab,
Textarea: primevue.textarea,
BsModal,
AbgabeDetail,
FhcOverlay
@@ -20,16 +17,20 @@ export const AbgabetoolStudent = {
return {
notenOptions: Vue.computed(() => this.notenOptions),
isViewMode: Vue.computed(() => this.isViewMode),
moodle_link: Vue.computed(() => this.moodle_link),
title_edit_allowed: Vue.computed(() => this.title_edit_allowed),
confetti_on_endupload: Vue.computed(() => this.confetti_on_endupload),
siginfolink_german: Vue.computed(() => this.siginfolink_german),
siginfolink_english: Vue.computed(() => this.siginfolink_english)
moodle_link: Vue.computed(() => this.moodle_link)
}
},
props: {
student_uid_prop: {
default: null
},
viewData: {
type: Object,
required: true,
default: () => ({uid: ''}),
validator(value) {
return value && value.uid
}
}
},
data() {
@@ -43,78 +44,14 @@ export const AbgabetoolStudent = {
detail: null,
projektarbeiten: null,
selectedProjektarbeit: null,
moodle_link: null,
title_edit_allowed: null,
confetti_on_endupload: null,
siginfolink_german: null,
siginfolink_english: null,
editingTitel: '',
editingProjektarbeit: null,
uid: null
moodle_link: null
};
},
methods: {
openTitelEdit(projektarbeit, event) {
// stop the click from toggling the accordion tab
event.stopPropagation();
this.editingProjektarbeit = projektarbeit;
this.editingTitel = projektarbeit.titel ?? '';
this.$refs.modalTitelEdit.show();
},
async saveTitel() {
const validation = validateThesisTitle(this.editingTitel);
if (!validation.isValid) {
if (validation.error === 'empty') {
this.$fhcAlert.alertWarning(this.$p.t('abgabetool/c4emptyThesisTitle'))
} else if (validation.error === 'invalid_characters') {
this.$fhcAlert.alertWarning(this.$p.t('abgabetool/c4invalidCharactersThesisTitle'))
}
return false;
}
const confirmed = await this.$fhcAlert.confirm({
message: this.$p.t('abgabetool/c4confirmTitelSpeichern'),
acceptLabel: this.$capitalize(this.$p.t('ui/speichern')),
acceptClass: 'p-button-primary',
rejectLabel: this.$capitalize(this.$p.t('abgabetool/c4Cancel')),
rejectClass: 'p-button-secondary'
});
if (confirmed === false) return;
this.loading = true;
this.$api.call(
ApiAbgabe.postStudentProjektarbeitTitel(
this.editingProjektarbeit.projektarbeit_id,
validation.cleanedTitle
)
).then(res => {
if (res.meta.status === 'success') {
// update the local list entry in-place so the accordion header reflects it immediately
this.editingProjektarbeit.titel = res.data;
// keep the open detail modal in sync if it happens to be showing this projektarbeit
if (this.selectedProjektarbeit?.projektarbeit_id === this.editingProjektarbeit.projektarbeit_id) {
this.selectedProjektarbeit.titel = res.data;
}
this.$fhcAlert.alertSuccess(this.$capitalize(this.$p.t('abgabetool/c4titelSavedSuccess')));
this.$refs.modalTitelEdit.hide();
} else {
this.$fhcAlert.alertError(this.$capitalize(this.$p.t('abgabetool/c4titelSaveError')));
}
}).finally(() => {
this.loading = false;
});
},
handleTitelUpdated(projektarbeit_id, titel) {
const pa = this.projektarbeiten?.find(p => p.projektarbeit_id === projektarbeit_id);
if (pa) pa.titel = titel;
},
checkQualityGatesStrict(termine) {
let qgate1Passed = false
let qgate2Passed = false
termine.forEach(t => {
const noteOption = this.notenOptions?.find(opt => opt.note == t.note)
if(noteOption && noteOption.positiv) {
@@ -131,7 +68,7 @@ export const AbgabetoolStudent = {
checkQualityGatesOptional(termine) {
const qgate1found = termine.find(t => t.paabgabetyp_kurzbz == 'qualgate1')
const qgate2found = termine.find(t => t.paabgabetyp_kurzbz == 'qualgate2')
let qgate1positiv = true
if(qgate1found) {
qgate1positiv = false
@@ -172,35 +109,47 @@ export const AbgabetoolStudent = {
this.loadAbgaben(details).then((res)=> {
const pa = this.projektarbeiten?.find(projekarbeit => projekarbeit.projektarbeit_id == details.projektarbeit_id)
pa.abgabetermine = res.data[0].retval
const paIsBenotet = pa.note !== null
pa.abgabetermine.forEach(termin => {
termin.file = []
termin.allowedToUpload = false
if(termin.paabgabetyp_kurzbz == 'end') {
// old assumed production logic when qgates are required
// termin.allowedToUpload = !this.isPastDate(termin.datum) && this.checkQualityGatesStrict(pa.abgabetermine)
const inTime = termin.fixtermin ? !this.isPastDate(termin.datum) : true
termin.allowedToUpload = inTime && this.checkQualityGatesOptional(pa.abgabetermine)
// development purposes
// termin.allowedToUpload = this.checkQualityGatesStrict(pa.abgabetermine)
// termin.allowedToUpload = true
} else if(termin.fixtermin) {
termin.allowedToUpload = !this.isPastDate(termin.datum)
} else {
termin.allowedToUpload = termin.upload_allowed
// this could confuse people since we should dont show people this flag
termin.allowedToUpload = termin.upload_allowed
}
// blocks client upload button if projektarbeitet is already beurteilt und thus further abgaben on any termin should be blocked
if(paIsBenotet) termin.allowedToUpload = false
termin.bezeichnung = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === termin.paabgabetyp_kurzbz)
termin.dateStyle = getDateStyleClass(termin, this.notenOptions)
})
pa.betreuer = this.buildBetreuer(pa)
pa.student_uid = this.student_uid
this.selectedProjektarbeit = pa
this.$refs.modalContainerAbgabeDetail.show()
}).finally(()=>{this.loading=false})
},
centeredTextFormatter(cell) {
@@ -222,8 +171,8 @@ export const AbgabetoolStudent = {
},
mailFormatter(cell) {
const val = cell.getValue()
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%;">' +
'<a href='+val+'><i class="fa fa-envelope" style="color:#00649C"></i></a></div>'
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%;">' +
'<a href='+val+'><i class="fa fa-envelope" style="color:#00649C"></i></a></div>'
},
beurteilungFormatter(cell) {
const val = cell.getValue()
@@ -233,17 +182,19 @@ export const AbgabetoolStudent = {
} else return '-'
},
buildMailToLink(projekt) {
// should always be "projekt.mitarbeiter_uid +'@'+ this.domain", built in backend
return 'mailto:' + projekt.email
},
buildBetreuer(abgabe) {
return (abgabe.btitelpre ? abgabe.btitelpre + ' ' : '') + abgabe.bvorname + ' ' + abgabe.bnachname + (abgabe.btitelpost ? ' ' + abgabe.btitelpost : '')
},
async setupData(data){
// this.projektarbeiten = data[0]
const projektarbeiten = data[0] ?? null
if(!projektarbeiten) return
this.projektarbeiten = projektarbeiten.map(projekt => {
let mode = 'detailTermine'
return {
...projekt,
details: {
@@ -277,14 +228,16 @@ export const AbgabetoolStudent = {
.then(res => {
resolve(res)
})
})
})
},
async setupMounted() {
this.loadProjektarbeiten()
},
getAccTabHeaderForProjektarbeit(projektarbeit) {
let title = ''
title += projektarbeit.titel ?? this.$p.t('abgabetool/keinTitel')
return title
},
getMailLink(projektarbeit) {
@@ -307,25 +260,23 @@ export const AbgabetoolStudent = {
window.open(projektarbeit.beurteilung2)
}
},
watch: {},
watch: {
},
computed: {
isViewMode() {
return this.student_uid !== this.uid
return this.student_uid !== this.viewData.uid
},
student_uid() {
return this.student_uid_prop || this.uid || null
return this.student_uid_prop || this.viewData?.uid || null
}
},
async created() {
// make sure zoom media query doesnt spill ever to other CIS4 sites
document.documentElement.classList.add('abgabetool');
this.$api.call(ApiAuthinfo.getAuthUID()).then(res => this.uid = res.data.uid)
this.phrasenPromise = this.$p.loadCategory(['abgabetool', 'global'])
this.phrasenPromise.then(()=> {this.phrasenResolved = true})
this.loading = true
//TODO: SWITCH TO NOTEN API ONCE NOTENTOOL IS IN MASTER TO AVOID DUPLICATE API
await this.$api.call(ApiAbgabe.getNoten()).then(res => {
if(res.meta.status == 'success') {
this.notenOptions = res.data[0]
@@ -338,18 +289,16 @@ export const AbgabetoolStudent = {
this.loading = false
})
// fetch abgabetypen options
this.$api.call(ApiAbgabe.getPaAbgabetypen()).then(res => {
this.abgabeTypeOptions = res.data
}).catch(e => {
this.loading = false
})
// fetch config to avoid hard coded links
this.$api.call(ApiAbgabe.getConfigStudent()).then(res => {
this.moodle_link = res.data?.moodle_link
this.title_edit_allowed = res.data?.title_edit_allowed
this.confetti_on_endupload = res.data?.confetti_on_endupload
this.siginfolink_german = res.data?.siginfolink_german
this.siginfolink_english = res.data?.siginfolink_english
}).catch(e => {
this.loading = false
})
@@ -357,73 +306,26 @@ export const AbgabetoolStudent = {
mounted() {
this.setupMounted()
},
beforeUnmount() {
document.documentElement.classList.remove('abgabetool');
},
template: `
<template v-if="phrasenResolved">
<FhcOverlay :active="loading"></FhcOverlay>
<bs-modal ref="modalContainerAbgabeDetail" class="bootstrap-prompt"
dialogClass="modal-xl" :allowFullscreenExpand="true" bodyClass="px-4 py-4">
dialogClass="modal-xl" :allowFullscreenExpand="true">
<template v-slot:title>
<div>
{{$capitalize( $p.t('abgabetool/c4abgabeStudentDetailTitle') )}}
</div>
</template>
<template v-slot:default>
<AbgabeDetail
:projektarbeit="selectedProjektarbeit"
@titel-updated="handleTitelUpdated"
></AbgabeDetail>
</template>
</bs-modal>
<bs-modal
ref="modalTitelEdit"
class="bootstrap-prompt"
dialogClass="bordered-modal"
bodyClass="px-4 py-4"
>
<template v-slot:title>
{{$capitalize( $p.t('abgabetool/c4titelBearbeiten') )}}
</template>
<template v-slot:default>
<div class="mb-2">
<label class="form-label fw-bold">
{{$capitalize( $p.t('abgabetool/c4titel') )}}
</label>
<Textarea
v-model="editingTitel"
rows="10"
maxlength="1024"
class="form-control w-100"
@keydown.enter.prevent="saveTitel"
/>
<div class="form-text text-end">{{ editingTitel.length }} / 1024</div>
</div>
</template>
<template v-slot:footer>
<button
class="btn btn-secondary"
@click="$refs.modalTitelEdit.hide()"
>
{{$capitalize( $p.t('abgabetool/c4Cancel') )}}
</button>
<button
class="btn btn-primary"
:disabled="!editingTitel.trim()"
@click="saveTitel"
>
<i class="fa-solid fa-floppy-disk me-1"></i>
{{$capitalize( $p.t('ui/speichern') )}}
</button>
<AbgabeDetail :projektarbeit="selectedProjektarbeit"></AbgabeDetail>
</template>
</bs-modal>
<h2>{{$capitalize( $p.t('abgabetool/abgabetoolTitle') )}}</h2>
<hr>
<div v-if="projektarbeiten === null || projektarbeiten?.length == 0">
<div v-if="projektarbeiten === null">
{{$capitalize( $p.t('abgabetool/c4abgabeStudentNoProjectsFound') )}}
</div>
@@ -433,12 +335,8 @@ export const AbgabetoolStudent = {
<template #header>
<div class="d-flex row w-100">
<div class="text-start" :class="projektarbeit.note != null ? 'col-6' : 'col-12'"
style="min-width: 0;">
<span
:title="getAccTabHeaderForProjektarbeit(projektarbeit)"
style="display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 600px;"
>{{getAccTabHeaderForProjektarbeit(projektarbeit)}}</span>
<div class="text-start" :class="projektarbeit.note != null ? 'col-6' : 'col-12'">
<span>{{getAccTabHeaderForProjektarbeit(projektarbeit)}}</span>
</div>
<div class="col-6 text-end">
<span>{{getNoteBezeichnung(projektarbeit)}}</span>
@@ -504,32 +402,11 @@ export const AbgabetoolStudent = {
{{ projektarbeit.projekttypbezeichnung }}
</div>
</div>
<div class="row mt-2">
<div class="col-4 col-md-3 fw-bold">{{$capitalize( $p.t('abgabetool/c4titel') )}}</div>
<div class="col-8 col-md-9 d-flex align-items-center gap-2" style="min-width: 0;">
<span
:title="projektarbeit.titel"
style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"
>{{ projektarbeit.titel }}</span>
<button
v-if="title_edit_allowed && !isViewMode && projektarbeit.note == null"
class="btn btn-sm btn-outline-secondary border-0 p-1"
v-tooltip.right="{ value: $capitalize($p.t('abgabetool/c4titelBearbeiten')), class: 'custom-tooltip' }"
@click="openTitelEdit(projektarbeit, $event)"
>
<i class="fa-solid fa-pen"></i>
</button>
</div>
</div>
<div class="row mt-2">
<div class="col-4 col-md-3 fw-bold">{{$capitalize( $p.t('abgabetool/c4note') )}}</div>
<div class="col-8 col-md-9">
<span>{{getNoteBezeichnung(projektarbeit)}}</span>
{{ projektarbeit.titel }}
</div>
</div>
</AccordionTab>
</template>
@@ -538,4 +415,4 @@ export const AbgabetoolStudent = {
`,
};
export default AbgabetoolStudent;
export default AbgabetoolStudent;
@@ -1,26 +0,0 @@
const zone = 'Europe/Vienna';
export function getViennaTodayISO() {
return luxon.DateTime.now().setZone(zone).toISODate();
}
export function formatISODate(dateParam) {
if (!dateParam) return '';
const date = luxon.DateTime.fromISO(String(dateParam), { zone });
return date.isValid ? date.toFormat('dd.MM.yyyy') : '';
}
export function toViennaDate(dateParam) {
if (!dateParam) return null;
return luxon.DateTime.fromISO(String(dateParam), { zone });
}
export function compareISODateValues(a, b) {
if (!a && !b) return 0;
if (!a) return 1;
if (!b) return -1;
return String(a).localeCompare(String(b));
}
@@ -1,8 +1,8 @@
const zone = 'Europe/Vienna';
const today = luxon.DateTime.now().setZone(zone);
export function getDateStyleClass(termin, notenOptions) {
const today = luxon.DateTime.now().setZone(zone);
const datum = luxon.DateTime.fromISO(termin.datum, { zone }).endOf('day');
const abgabedatum = termin.abgabedatum ? luxon.DateTime.fromISO(termin.abgabedatum, { zone }) : null;
termin.diffindays = datum.diff(today, 'days').days;
@@ -28,11 +28,10 @@ export function getDateStyleClass(termin, notenOptions) {
// no submission yet
if (datum < today) return 'verpasst';
if (termin.diffindays <= 12) return 'abzugeben';
return 'standard';
}
// GENERIC STATUS — applies to all termine
if (datum < today) return 'verpasst';
if (termin.diffindays <= 12) return 'abzugeben';
return 'standard';
}
// GENERIC STATUS
return datum < today ? 'verpasst' : 'standard';
}
@@ -1,25 +0,0 @@
/**
* Validates the thesis title on the frontend
* @param {string} title - The raw input from the title field
* @returns {object} Validation result containing status and cleaned title
*/
export function validateThesisTitle(title) {
if (!title) {
return { isValid: false, error: 'empty' };
}
// Replicate strip_tags / trim
const cleanedTitle = title.replace(/<\/?[^>]+(>|$)/g, "").trim();
if (cleanedTitle === '') {
return { isValid: false, error: 'empty' };
}
// Replicate the emoji/pictograph rejection
const emojiRegex = /\p{Extended_Pictographic}/u;
if (emojiRegex.test(cleanedTitle)) {
return { isValid: false, error: 'invalid_characters' };
}
return { isValid: true, cleanedTitle: cleanedTitle };
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,71 @@
import ApiLehre from "../../api/factory/lehre.js";
const options = Vue.ref([]);
const params = Vue.ref({});
let appContext = null;
export function setupContext(globalProps) {
appContext = globalProps
}
// bind and watch api params via reference
export function bindParams(paramsRef) {
Vue.watch(
paramsRef,
(newVal) => {
params.value = { ...newVal };
fetchLehreinheiten(newVal.lv_id, newVal.sem_kurzbz);
},
{ immediate: true, deep: true }
);
}
async function fetchLehreinheiten(lv_id, sem_kurzbz) {
appContext.$api.call(ApiLehre.getLeForLv(lv_id, sem_kurzbz)).then(res => {
const data = []
// TODO: could be done on server in some shared function, copied from anw extension for now
res.data?.retval?.forEach(entry => {
const existing = data.find(e => e.lehreinheit_id === entry.lehreinheit_id)
if (existing) {
// supplement info
existing.infoString += ', '
if (entry.gruppe_kurzbz !== null && entry.direktinskription == false) {
existing.infoString += entry.gruppe_kurzbz
} else {
existing.infoString += entry.kurzbzlang + '-' + entry.semester
+ (entry.verband ? entry.verband : '')
+ (entry.gruppe ? entry.gruppe : '')
}
} else {
// entries are supposed to be fetched ordered by non null gruppe_kurzbz first
// so a new entry will always start with those groups, others are appended afterwards
entry.infoString = entry.kurzbz + ' - ' + entry.lehrform_kurzbz + ' - '
if (entry.gruppe_kurzbz !== null && entry.direktinskription == false) {
entry.infoString += entry.gruppe_kurzbz
} else {
entry.infoString += entry.kurzbzlang + '-' + entry.semester
+ (entry.verband ? entry.verband : '')
+ (entry.gruppe ? entry.gruppe : '')
}
data.push(entry)
}
})
options.value = [...data]
})
}
// export the module and relevant fields via reactive
const LehreinheitenModule = Vue.reactive({
options,
optionLabel: 'infoString',
placeholder: Vue.computed(()=>appContext?.$p.t('lehre/lehreinheit')),
setupContext,
bindParams
});
export default LehreinheitenModule;
@@ -2,6 +2,7 @@ import {CoreFilterCmpt} from "../../filter/Filter.js";
import FormForm from '../../Form/Form.js';
import FormInput from '../../Form/Input.js';
import ApiGruppe from "../../../api/lehrveranstaltung/gruppe.js";
import drop from '../../../directives/drop.js';
export default{
name: "LVGruppen",
components: {
@@ -9,6 +10,9 @@ export default{
FormForm,
FormInput
},
directives: {
drop
},
props: {
lehreinheit_id: Number
},
@@ -187,28 +191,48 @@ export default{
this.reload();
})
},
onDropVerband(node, gruppe) {
this.$api.call(ApiGruppe.getGruppe(gruppe))
.then(result => result.data)
.then(result => {
let newData = {
'gid': result,
'lehreinheit_id': this.lehreinheit_id,
'lehrverband': gruppe.lehrverband,
}
this.$api.call(ApiGruppe.add(newData))
.then(result => {
this.reload()
})
});
},
},
template: `
<core-filter-cmpt
ref="table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
:side-menu="false"
:reload=true
>
<template #search> <!--TODO (david) Slot prüfen -->
<form-input
type="autocomplete"
:suggestions="filteredGroups"
:placeholder="$p.t('lehre', 'addGroup')"
v-model="selectedGroup"
field="label"
@item-select="addGroup"
@complete="searchGroup"
></form-input>
</template>
</core-filter-cmpt>
<div v-drop:link-strict.verband="(evt, gruppe) => onDropVerband(node, gruppe)">
<core-filter-cmpt
ref="table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
:side-menu="false"
:reload=true
>
<template #search> <!--TODO (david) Slot prüfen -->
<form-input
type="autocomplete"
:suggestions="filteredGroups"
:placeholder="$p.t('lehre', 'addGroup')"
v-model="selectedGroup"
field="label"
@item-select="addGroup"
@complete="searchGroup"
></form-input>
</template>
</core-filter-cmpt>
</div>
`
};
@@ -8,9 +8,10 @@ import LvTabs from "./Setup/Tabs.js";
import ApiDetails from "../../api/lehrveranstaltung/details.js";
import ApiLektor from "../../api/lehrveranstaltung/lektor.js";
import ApiGruppe from "../../api/lehrveranstaltung/gruppe.js";
import ApiStudiengangTree from "../../api/lehrveranstaltung/studiengangtree.js";
import ApiSearchbar from "../../api/factory/searchbar.js";
import AppConfig from "../AppConfig.js";
import ApiLvConfig from "../../api/lehrveranstaltung/config.js";
export default {
@@ -23,6 +24,7 @@ export default {
StvStudiensemester,
LvTable,
LvTabs,
AppConfig
},
props: {
defaultSemester: String,
@@ -32,7 +34,9 @@ export default {
stg: { type: String, required: false },
semester: { type: [Number, String], required: false, default: null },
studiensemester_kurzbz: { type: String, required: false, default: null },
emp: { type: String, required: false, default: null }
emp: { type: String, required: false, default: null },
avatarUrl: String,
logoutUrl: String,
},
provide() {
@@ -44,7 +48,6 @@ export default {
lehreinheitAnmerkungDefault: (this.config.lehreinheitAnmerkungDefault || '').replace(/\\n/g, '\n'),
lehreinheitRaumtypDefault: this.config.lehreinheitRaumtypDefault,
lehreinheitRaumtypAlternativeDefault: this.config.lehreinheitRaumtypAlternativeDefault,
permissionLehrveranstaltung: this.permissions['lehre/lehrveranstaltung'],
permissionGruppenEntfernen: this.permissions['lv-plan/gruppenentfernen'],
permissionLektorEntfernen: this.permissions['lv-plan/lektorentfernen'],
@@ -72,6 +75,8 @@ export default {
},
data() {
return {
appconfig:{},
configEndpoints: ApiLvConfig,
selected: [],
studiengang: "",
filter: {},
@@ -269,6 +274,51 @@ export default {
<span class="fa-solid fa-table-list"></span>
</button>
<core-searchbar :searchoptions="searchbaroptions" :searchfunction=searchfunction class="searchbar w-100"></core-searchbar>
<div id="nav-user" class="dropdown">
<button
id="nav-user-btn"
class="btn btn-link rounded-0 py-0"
type="button"
data-bs-toggle="dropdown"
data-bs-target="#nav-user-menu"
aria-expanded="false"
aria-controls="nav-user-menu"
>
<img
:src="avatarUrl"
:alt="$p.t('profilUpdate/profilBild')"
class="bg-light avatar rounded-circle border border-light"
/>
</button>
<ul
ref="navUserDropdown"
class="dropdown-menu dropdown-menu-dark dropdown-menu-end rounded-0 text-center m-0"
aria-labelledby="nav-user-btn"
>
<li>
<button
type="button"
class="dropdown-item"
data-bs-toggle="modal"
data-bs-target="#configModal"
>
{{ $p.t('ui/settings') }}
</button>
</li>
<li><hr class="dropdown-divider m-0"/></li>
<li>
<nav-language
item-class="dropdown-item border-left-dark"
/>
</li>
<li><hr class="dropdown-divider m-0"/></li>
<li>
<a class="dropdown-item" :href="logoutUrl">
{{ $p.t('ui/logout') }}
</a>
</li>
</ul>
</div>
</header>
<div class="container-fluid overflow-hidden">
<div class="row h-100">
@@ -285,7 +335,7 @@ export default {
<div class="offcanvas-header justify-content-end px-1 d-md-none">
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" :aria-label="$p.t('ui/schliessen')"></button>
</div>
<stv-verband :preselectedKey="selectedStudiengang" :endpoint="endpoint" @select-verband="onSelectVerband" class="col" style="height:0%"></stv-verband>
<stv-verband :preselectedKey="selectedStudiengang" :dragEnabled=true :endpoint="endpoint" @select-verband="onSelectVerband" class="col" style="height:0%"></stv-verband>
<stv-studiensemester v-model:studiensemester-kurzbz="selectedStudiensemester" @update:studiensemester-kurzbz="studiensemesterChanged"></stv-studiensemester>
</nav>
@@ -327,5 +377,6 @@ export default {
</main>
</div>
</div>
<app-config ref="config" v-model="appconfig" :endpoints="configEndpoints"></app-config>
</div>`
};
@@ -140,7 +140,7 @@ export default{
{
this.$emit('changedLektor', this.changed.mitarbeiter_uid);
}
if (this.changed.semesterstunden || this.changed.stundensatz)
else if (this.changed.semesterstunden || this.changed.stundensatz)
{
this.$emit('changedCosts');
}
@@ -183,9 +183,6 @@ export default{
},
},
created() {
this.getLektorData()
},
template: `
<core-form ref="form" @submit.prevent="updateDaten">
<div class="position-sticky top-0 z-1">

Some files were not shown because too many files have changed in this diff Show More