From 68292b60afe4b56ce211da9bae55157b0d3baa5b Mon Sep 17 00:00:00 2001 From: ma0068 Date: Mon, 20 Jul 2026 17:36:54 +0200 Subject: [PATCH 1/7] add Tags Incoming and Preabbrecher --- application/config/stv.php | 2 + .../libraries/tags/CoreIncomingTagLib.php | 63 +++++++++++++++ .../libraries/tags/CorePreabbrecherTagLib.php | 80 +++++++++++++++++++ application/models/codex/Bisio_model.php | 53 ++++++++++++ public/css/tags.css | 9 +++ .../75959_StudVw_Automatische_Tags.php | 22 ++++- 6 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 application/libraries/tags/CoreIncomingTagLib.php create mode 100644 application/libraries/tags/CorePreabbrecherTagLib.php diff --git a/application/config/stv.php b/application/config/stv.php index 3090e3f2e..4893560e0 100644 --- a/application/config/stv.php +++ b/application/config/stv.php @@ -150,4 +150,6 @@ $config['stv_prestudent_tags'] = [ 'unterbrecher_auto' => ['readonly' => true], 'stbtr_erh_auto' => ['readonly' => true], 'jgv_auto' => ['readonly' => true], + 'in_auto' => ['readonly' => true], + 'preabbr_auto' => ['readonly' => true], ]; diff --git a/application/libraries/tags/CoreIncomingTagLib.php b/application/libraries/tags/CoreIncomingTagLib.php new file mode 100644 index 000000000..26d7dad0b --- /dev/null +++ b/application/libraries/tags/CoreIncomingTagLib.php @@ -0,0 +1,63 @@ +ci = get_instance(); + $this->ci->load->model('codex/Bisio_model', 'BisioModel'); + } + + public function getZuordnungIds(array $params) + { + if(!isset($params['studiensemester_kurzbz'])) + { + return (object) array( + 'idArray' => [] + ); + } + + $semester = $params['studiensemester_kurzbz']; + + $result = $this->ci->BisioModel->getIncomingsOfSemester($semester); + + $data = $result->retval; + + $outgoing_data = array_map(function($item) { + return [ + 'id' => $item->prestudent_id, + 'von' => $item->von, + 'bis' => $item->bis + ]; + }, $data); + + return (object) array( + 'data' => $outgoing_data, + 'typeId' => 'prestudent_id', + ); + } + + public function isCriteriaSetFor(array $params) + { + if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id') + return false; + + $semester = $params['studiensemester_kurzbz']; + $prestudent_id = $params['id']; + + $result = $this->ci->BisioModel->isPrestudentIncoming($semester, $prestudent_id); + + if (hasData($result)) { + return $result; + } + else + return null; + } + +} diff --git a/application/libraries/tags/CorePreabbrecherTagLib.php b/application/libraries/tags/CorePreabbrecherTagLib.php new file mode 100644 index 000000000..41712ac3b --- /dev/null +++ b/application/libraries/tags/CorePreabbrecherTagLib.php @@ -0,0 +1,80 @@ +ci = get_instance(); + $this->ci->load->model('education/Studierendenantrag_model', 'StudierendenantragModel'); + } + + public function getZuordnungIds(array $params) + { + if(!isset($params['studiensemester_kurzbz'])) + { + return (object) array( + 'idArray' => [] + ); + } + + $semester = $params['studiensemester_kurzbz']; + + $this->ci->StudierendenantragModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + $this->ci->StudierendenantragModel->db->where_in('typ', self::TYP_ABMELDUNG); + $this->ci->StudierendenantragModel->db->where('studiensemester_kurzbz', $semester); + $result = $this->ci->StudierendenantragModel->getWithLastStatusWhere([ + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_CREATED + ]); + + $data = $result->retval; + + $prewiederholer_data = array_map(function($item) { + return [ + 'id' => $item->prestudent_id, + 'von' => $item->start, + 'bis' => $item->ende + ]; + }, $data); + + return (object) array( + 'data' => $prewiederholer_data, + 'typeId' => 'prestudent_id', + ); + } + + public function isCriteriaSetFor(array $params) + { + if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id') + return false; + + $semester = $params['studiensemester_kurzbz']; + $prestudent_id = $params['id']; + + $this->ci->StudierendenantragModel->addSelect('prestudent_id'); + $this->ci->StudierendenantragModel->addSelect('start as von'); + $this->ci->StudierendenantragModel->addSelect('ende as bis'); + + $this->ci->StudierendenantragModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + $this->ci->StudierendenantragModel->db->where_in('typ', self::TYP_ABMELDUNG); + $this->ci->StudierendenantragModel->db->where('studiensemester_kurzbz', $semester); + $this->ci->StudierendenantragModel->db->where('prestudent_id', $prestudent_id); + $result = $this->ci->StudierendenantragModel->getWithLastStatusWhere([ + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_CREATED + ]); + + if(hasData($result)) + { + return $result; + } + else + return null; + } + +} diff --git a/application/models/codex/Bisio_model.php b/application/models/codex/Bisio_model.php index 9a9fd37fb..972661c8b 100644 --- a/application/models/codex/Bisio_model.php +++ b/application/models/codex/Bisio_model.php @@ -73,6 +73,34 @@ class Bisio_model extends DB_Model return $this->execQuery($query, array($studiensemester_kurzbz)); } + /** + * Gets incoming students of certain Semester + * @param String studiensemester_kurzbz + * @return array of prestudent_ids + */ + public function getIncomingsOfSemester($studiensemester_kurzbz) + { + $query = " + SELECT DISTINCT ps.prestudent_id, tbl_bisio.von, tbl_bisio.bis + FROM bis.tbl_bisio + JOIN public.tbl_student USING (student_uid) + JOIN public.tbl_prestudent ps USING (prestudent_id) + JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id) + JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz) + WHERE ss.studiensemester_kurzbz = ? + AND nation_code = 'A' + AND ( + tbl_bisio.von <= ss.ende + AND ( + tbl_bisio.bis >= ss.start + OR tbl_bisio.bis IS NULL + ) + ) + "; + + return $this->execQuery($query, array($studiensemester_kurzbz)); + } + public function isPrestudentOutgoing($studiensemester_kurzbz, $prestudent_id) { $query = " @@ -97,4 +125,29 @@ class Bisio_model extends DB_Model return $this->execQuery($query, array($studiensemester_kurzbz, $prestudent_id)); } + + public function isPrestudentIncoming($studiensemester_kurzbz, $prestudent_id) + { + $query = " + SELECT + ps.prestudent_id, tbl_bisio.von, tbl_bisio.bis + FROM bis.tbl_bisio + JOIN public.tbl_student USING (student_uid) + JOIN public.tbl_prestudent ps USING (prestudent_id) + JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id) + JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz) + WHERE ss.studiensemester_kurzbz = ? + AND nation_code = 'A' + AND ( + tbl_bisio.von <= ss.ende + AND ( + tbl_bisio.bis >= ss.start + OR tbl_bisio.bis IS NULL + ) + ) + AND ps.prestudent_id = ? + "; + + return $this->execQuery($query, array($studiensemester_kurzbz, $prestudent_id)); + } } diff --git a/public/css/tags.css b/public/css/tags.css index 3b3377d5d..0634c5ce3 100644 --- a/public/css/tags.css +++ b/public/css/tags.css @@ -91,6 +91,15 @@ color: black; } +.tag_skyblue { + background-color: #90D5FF; + color: black; +} +.tag_blush { + background-color: #A83E3E; + color: white; +} + .tag_done { text-decoration: line-through; } diff --git a/system/dbupdate_3.4/75959_StudVw_Automatische_Tags.php b/system/dbupdate_3.4/75959_StudVw_Automatische_Tags.php index 1b6d6b1dd..1954ae48d 100644 --- a/system/dbupdate_3.4/75959_StudVw_Automatische_Tags.php +++ b/system/dbupdate_3.4/75959_StudVw_Automatische_Tags.php @@ -16,7 +16,7 @@ if(!@$db->db_query("SELECT taglib FROM public.tbl_notiz_typ LIMIT 1")) $tags = [ [ 'typ_kurzbz' => 'wh_auto', - 'bezeichnung' => '{WH, R}', + 'bezeichnung' => '{WH, WH}', 'style' => 'tag_dark_grey', 'prioritaet' => 11, 'taglib' => 'tags/CoreWiederholerTagLib' @@ -30,14 +30,14 @@ $tags = [ ], [ 'typ_kurzbz' => 'out_auto', - 'bezeichnung' => '{Out, Out}', + 'bezeichnung' => '{Outgoing, Outgoing}', 'style' => 'tag_limette', 'prioritaet' => 12, 'taglib' => 'tags/CoreOutgoingTagLib' ], [ 'typ_kurzbz' => 'prewh_auto', - 'bezeichnung' => '{Pre-WH, pre-R}', + 'bezeichnung' => '{Pre-WH, pre-WH}', 'style' => 'tag_light_grey', 'prioritaet' => 13, 'taglib' => 'tags/CorePrewiederholerTagLib' @@ -51,7 +51,7 @@ $tags = [ ], [ 'typ_kurzbz' => 'unterbrecher_auto', - 'bezeichnung' => '{UB, I}', + 'bezeichnung' => '{UB, UB}', 'style' => 'tag_turquoise', 'prioritaet' => 15, 'taglib' => 'tags/CoreUnterbrecherTagLib' @@ -70,6 +70,20 @@ $tags = [ 'prioritaet' => 17, 'taglib' => 'tags/CoreJgvTagLib' ], + [ + 'typ_kurzbz' => 'preabbr_auto', + 'bezeichnung' => '{Pre-Abbr, pre-Abbr}', + 'style' => 'tag_blush', + 'prioritaet' => 18, + 'taglib' => 'tags/CorePreabbrecherTagLib' + ], + [ + 'typ_kurzbz' => 'in_auto', + 'bezeichnung' => '{Incoming, Incoming}', + 'style' => 'tag_skyblue', + 'prioritaet' => 19, + 'taglib' => 'tags/CoreIncomingTagLib' + ], ]; From 7cda22f8079f4a9ba425b13d9e5fc583a72377fa Mon Sep 17 00:00:00 2001 From: ma0068 Date: Tue, 21 Jul 2026 10:41:27 +0200 Subject: [PATCH 2/7] use language index for tags --- .../api/frontend/v1/stv/Students.php | 48 +++++++++++++------ application/core/Tag_Controller.php | 12 +++-- application/libraries/stv/StudentListLib.php | 8 ++-- 3 files changed, 46 insertions(+), 22 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Students.php b/application/controllers/api/frontend/v1/stv/Students.php index e77e20889..f822c7205 100644 --- a/application/controllers/api/frontend/v1/stv/Students.php +++ b/application/controllers/api/frontend/v1/stv/Students.php @@ -103,6 +103,8 @@ class Students extends FHCAPI_Controller */ public function getIncoming($studiensemester_kurzbz) { + $index_bezeichnung_mehrsprachig = $this->_getLanguageIndex(); + $this->addMeta('ci_method', __FUNCTION__); $this->addMeta('ci_params', [ 'studiensemester_kurzbz' => $studiensemester_kurzbz @@ -135,7 +137,7 @@ class Students extends FHCAPI_Controller $this->addFilter($studiensemester_kurzbz); - $result = $this->studentlistlib->execute($studiensemester_kurzbz); + $result = $this->studentlistlib->execute($studiensemester_kurzbz, $index_bezeichnung_mehrsprachig); $data = $this->getDataOrTerminateWithError($result); $this->decodeTagsJsonInResult($data); @@ -149,6 +151,8 @@ class Students extends FHCAPI_Controller */ public function getOutgoing($studiensemester_kurzbz) { + $index_bezeichnung_mehrsprachig = $this->_getLanguageIndex(); + $this->addMeta('ci_method', __FUNCTION__); $this->addMeta('ci_params', [ 'studiensemester_kurzbz' => $studiensemester_kurzbz @@ -194,7 +198,7 @@ class Students extends FHCAPI_Controller $this->addFilter($studiensemester_kurzbz); - $result = $this->studentlistlib->execute($studiensemester_kurzbz); + $result = $this->studentlistlib->execute($studiensemester_kurzbz, $index_bezeichnung_mehrsprachig); $data = $this->getDataOrTerminateWithError($result); $this->decodeTagsJsonInResult($data); @@ -208,6 +212,7 @@ class Students extends FHCAPI_Controller */ public function getGemeinsamestudien($studiensemester_kurzbz) { + $index_bezeichnung_mehrsprachig = $this->_getLanguageIndex(); $this->addMeta('ci_method', __FUNCTION__); $this->addMeta('ci_params', [ 'studiensemester_kurzbz' => $studiensemester_kurzbz @@ -239,7 +244,7 @@ class Students extends FHCAPI_Controller $this->addFilter($studiensemester_kurzbz); - $result = $this->studentlistlib->execute($studiensemester_kurzbz); + $result = $this->studentlistlib->execute($studiensemester_kurzbz, $index_bezeichnung_mehrsprachig); $data = $this->getDataOrTerminateWithError($result); $this->decodeTagsJsonInResult($data); @@ -290,6 +295,8 @@ class Students extends FHCAPI_Controller { $stdsemEsc = $studiensemester_kurzbz ? $this->PrestudentModel->escape($studiensemester_kurzbz) : 'NULL'; + $index_bezeichnung_mehrsprachig = $this->_getLanguageIndex(); + $selectRT = " SELECT 1 FROM public.tbl_rt_person @@ -444,7 +451,7 @@ class Students extends FHCAPI_Controller $this->addFilter($studiensemester_kurzbz); - $result = $this->studentlistlib->execute($studiensemester_kurzbz); + $result = $this->studentlistlib->execute($studiensemester_kurzbz, $index_bezeichnung_mehrsprachig); $data = $this->getDataOrTerminateWithError($result); $this->decodeTagsJsonInResult($data); @@ -547,6 +554,7 @@ class Students extends FHCAPI_Controller $gruppe_kurzbz = null, $orgform_kurzbz = null ) { + $index_bezeichnung_mehrsprachig = $this->_getLanguageIndex(); $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); if (!$this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz)) @@ -591,10 +599,8 @@ class Students extends FHCAPI_Controller } } - $this->addFilter($studiensemester_kurzbz); - - $result = $this->studentlistlib->execute($studiensemester_kurzbz); + $result = $this->studentlistlib->execute($studiensemester_kurzbz, $index_bezeichnung_mehrsprachig); $data = $this->getDataOrTerminateWithError($result); $this->decodeTagsJsonInResult($data); @@ -620,6 +626,7 @@ class Students extends FHCAPI_Controller */ public function getPrestudent($studiensemester_kurzbz, $prestudent_id) { + $index_bezeichnung_mehrsprachig = $this->_getLanguageIndex(); $this->addMeta('ci_method', __FUNCTION__); $this->addMeta('ci_params', array( 'studiensemester_kurzbz' => $studiensemester_kurzbz, @@ -647,10 +654,8 @@ class Students extends FHCAPI_Controller $this->studentlistlib->addWhere('tbl_prestudent.prestudent_id', $prestudent_id); - $this->addFilter($studiensemester_kurzbz); - - $result = $this->studentlistlib->execute($studiensemester_kurzbz); + $result = $this->studentlistlib->execute($studiensemester_kurzbz, $index_bezeichnung_mehrsprachig); $data = $this->getDataOrTerminateWithError($result); $this->decodeTagsJsonInResult($data); @@ -664,6 +669,7 @@ class Students extends FHCAPI_Controller */ public function getStudent($studiensemester_kurzbz, $student_uid) { + $index_bezeichnung_mehrsprachig = $this->_getLanguageIndex(); $this->addMeta('ci_method', __FUNCTION__); $this->addMeta('ci_params', array( 'studiensemester_kurzbz' => $studiensemester_kurzbz, @@ -679,11 +685,9 @@ class Students extends FHCAPI_Controller $this->studentlistlib->addWhere('s.student_uid', $student_uid); - $this->addFilter($studiensemester_kurzbz); - - $result = $this->studentlistlib->execute($studiensemester_kurzbz); + $result = $this->studentlistlib->execute($studiensemester_kurzbz, $index_bezeichnung_mehrsprachig); $data = $this->getDataOrTerminateWithError($result); $this->decodeTagsJsonInResult($data); @@ -698,6 +702,8 @@ class Students extends FHCAPI_Controller */ public function getPerson($studiensemester_kurzbz, $person_id) { + $index_bezeichnung_mehrsprachig = $this->_getLanguageIndex(); + $this->addMeta('ci_method', __FUNCTION__); $this->addMeta('ci_params', array( 'studiensemester_kurzbz' => $studiensemester_kurzbz, @@ -717,7 +723,7 @@ class Students extends FHCAPI_Controller $this->addFilter($studiensemester_kurzbz); - $result = $this->studentlistlib->execute($studiensemester_kurzbz); + $result = $this->studentlistlib->execute($studiensemester_kurzbz, $index_bezeichnung_mehrsprachig); $data = $this->getDataOrTerminateWithError($result); $this->decodeTagsJsonInResult($data); @@ -731,6 +737,7 @@ class Students extends FHCAPI_Controller */ public function search($studiensemester_kurzbz) { + $index_bezeichnung_mehrsprachig = $this->_getLanguageIndex(); $this->addMeta('ci_method', __FUNCTION__); $this->addMeta('ci_params', array( 'studiensemester_kurzbz' => $studiensemester_kurzbz @@ -786,7 +793,7 @@ class Students extends FHCAPI_Controller $this->addFilter($studiensemester_kurzbz); - $result = $this->studentlistlib->execute($studiensemester_kurzbz); + $result = $this->studentlistlib->execute($studiensemester_kurzbz, $index_bezeichnung_mehrsprachig); $data = $this->getDataOrTerminateWithError($result); $this->decodeTagsJsonInResult($data); @@ -818,4 +825,15 @@ class Students extends FHCAPI_Controller } } } + + private function _getLanguageIndex() + { + $this->load->model('system/Sprache_model', 'SpracheModel'); + $this->SpracheModel->addSelect('index'); + $result = $this->SpracheModel->loadWhere(array('sprache' => getUserLanguage())); + $language = hasData($result) ? getData($result)[0]->index : 1; + $index_bezeichnung_mehrsprachig = $language - 1; + + return $index_bezeichnung_mehrsprachig; + } } diff --git a/application/core/Tag_Controller.php b/application/core/Tag_Controller.php index c4c5afa88..0600200c8 100644 --- a/application/core/Tag_Controller.php +++ b/application/core/Tag_Controller.php @@ -42,6 +42,9 @@ class Tag_Controller extends FHCAPI_Controller public function getTag($readonly_tags = null) { + $language = $this->_getLanguageIndex(); + $index_bezeichnung_mehrsprachig = $language - 1; + $id = $this->input->get('id'); if (is_array($readonly_tags) && !isEmptyArray($readonly_tags)) @@ -66,7 +69,7 @@ class Tag_Controller extends FHCAPI_Controller $this->NotizModel->addSelect( "tbl_notiz.titel, tbl_notiz.text, - array_to_json(bezeichnung_mehrsprachig::varchar[])->>0 as bezeichnung, + array_to_json(bezeichnung_mehrsprachig::varchar[])->>". $index_bezeichnung_mehrsprachig . " as bezeichnung, tbl_notiz.notiz_id, tbl_notiz_typ.style, tbl_notiz_typ.automatisiert, @@ -96,10 +99,11 @@ class Tag_Controller extends FHCAPI_Controller public function getTags($tags = null) { $language = $this->_getLanguageIndex(); + $index_bezeichnung_mehrsprachig = $language - 1; $this->NotiztypModel->addSelect( "typ_kurzbz as tag_typ_kurzbz, - array_to_json(bezeichnung_mehrsprachig::varchar[])->>0 as bezeichnung, + array_to_json(bezeichnung_mehrsprachig::varchar[])->>". $index_bezeichnung_mehrsprachig . " as bezeichnung, style, beschreibung, tag, @@ -283,6 +287,8 @@ class Tag_Controller extends FHCAPI_Controller } public function getAllTags($readonly_tags = false){ + $language = $this->_getLanguageIndex(); + $index_bezeichnung_mehrsprachig = $language - 1; $prestudent_id = $this->input->get('prestudent_id'); //TODO check for readonly: necessary? @@ -307,7 +313,7 @@ class Tag_Controller extends FHCAPI_Controller $this->NotizModel->addSelect( "tbl_notiz.titel, tbl_notiz.text, - array_to_json(bezeichnung_mehrsprachig::varchar[])->>0 as bezeichnung, + array_to_json(bezeichnung_mehrsprachig::varchar[])->>". $index_bezeichnung_mehrsprachig . " as bezeichnung, tbl_notiz.notiz_id, tbl_notiz_typ.style, tbl_notiz_typ.automatisiert, diff --git a/application/libraries/stv/StudentListLib.php b/application/libraries/stv/StudentListLib.php index 432b75bd4..de84cda0b 100644 --- a/application/libraries/stv/StudentListLib.php +++ b/application/libraries/stv/StudentListLib.php @@ -251,9 +251,9 @@ class StudentListLib * * @return stdClass result of the query */ - public function execute($studiensemester_kurzbz) + public function execute($studiensemester_kurzbz, $index_bezeichnung_mehrsprachig=0) { - $this->addSelectAndJoinForTagsIfConfigured($studiensemester_kurzbz); + $this->addSelectAndJoinForTagsIfConfigured($studiensemester_kurzbz, $index_bezeichnung_mehrsprachig); $stdsemEsc = $studiensemester_kurzbz ? $this->_ci->PrestudentModel->escape($studiensemester_kurzbz) : 'NULL'; @@ -302,7 +302,7 @@ class StudentListLib * @param string studiensemester_kurzbz * */ - protected function addSelectAndJoinForTagsIfConfigured($studiensemester_kurzbz) + protected function addSelectAndJoinForTagsIfConfigured($studiensemester_kurzbz, $index_bezeichnung_mehrsprachig) { if (defined('STV_TAGS_ENABLED') && STV_TAGS_ENABLED) { $this->_ci->load->config('stv'); @@ -326,7 +326,7 @@ class StudentListLib SELECT DISTINCT ON (n.notiz_id) n.notiz_id AS id, nt.typ_kurzbz, - array_to_json(nt.bezeichnung_mehrsprachig)->>0 AS beschreibung, + array_to_json(nt.bezeichnung_mehrsprachig)->>". $index_bezeichnung_mehrsprachig . " AS beschreibung, n.text AS notiz, nt.style, n.erledigt AS done, From 1ae02d8ed8bcc6f9afe71713f468e21b31f0014f Mon Sep 17 00:00:00 2001 From: ma0068 Date: Tue, 21 Jul 2026 13:35:31 +0200 Subject: [PATCH 3/7] refactor addTaginTable and deleteTagInTable, add missing logic for automated tags in case of manual adding --- public/js/helpers/TagHelper.js | 67 +++++++++++++++++++++++---- public/js/tabulator/formatter/tags.js | 4 ++ 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/public/js/helpers/TagHelper.js b/public/js/helpers/TagHelper.js index 9282aa167..0573858d9 100644 --- a/public/js/helpers/TagHelper.js +++ b/public/js/helpers/TagHelper.js @@ -6,9 +6,52 @@ export function addTagInTable(addedTag, rows, matchKey, tagsKey = "tags") rows.forEach(row => { const rowData = row.getData(); - let updated = false; - addedTag.response.forEach(tag => + //add save check if String or Array and avoid same reference later for mutations + let raw = rowData[tagsKey]; + let tags = typeof raw === "string" + ? JSON.parse(raw || "[]") + : Array.isArray(raw) + ? [...raw] + : []; + + let updated = false; + for (const tag of addedTag.response) + { + if (rowData[matchKey] !== tag[matchKey]) + continue; + + //avoid double inserts + if (tags.some(x => x.id === tag.id)) + continue; + + const newTag = { + id: tag.id, + prestudent_id: tag.prestudent_id, + //add also information of addedTag + beschreibung: addedTag.beschreibung, + notiz: addedTag.notiz ?? "", + style: addedTag.style, + done: addedTag.done ?? false, + typ_kurzbz: addedTag.tag_typ_kurzbz ?? addedTag.typ_kurzbz, //here seem to be 2 variations + automatisiert: addedTag.automatisiert + }; + + tags.unshift(newTag); + updated = true; + } + + if (updated) + { + row.update({ + [tagsKey]: JSON.stringify(tags) + }); + + row.reformat(); + } + }); + +/* addedTag.response.forEach(tag => { if (rowData[matchKey] !== tag[matchKey]) return; @@ -36,7 +79,7 @@ export function addTagInTable(addedTag, rows, matchKey, tagsKey = "tags") if (updated) row.update(rowData); - }); + });*/ } export function deleteTagInTable(deletedTag, rows, tagsKeys = ['tags']) @@ -49,26 +92,30 @@ export function deleteTagInTable(deletedTag, rows, tagsKeys = ['tags']) let updates = {}; let changed = false; - tagsKeys.forEach(key => { - let tags; + for (const key of tagsKeys) { + let raw = rowData[key]; + let tags = []; try { - tags = JSON.parse(rowData[key] || "[]"); + if (typeof raw === "string") { + tags = JSON.parse(raw || "[]"); + } else if (Array.isArray(raw)) { + tags = [...raw]; + } } catch (e) { tags = []; } if (!Array.isArray(tags)) - return; + continue; let filtered = tags.filter(tag => tag?.id !== deletedTag); - if (filtered.length !== tags.length) - { + if (filtered.length !== tags.length) { updates[key] = JSON.stringify(filtered); changed = true; } - }); + } if (changed) { row.update(updates); diff --git a/public/js/tabulator/formatter/tags.js b/public/js/tabulator/formatter/tags.js index 367a8a5fb..f334bb8df 100644 --- a/public/js/tabulator/formatter/tags.js +++ b/public/js/tabulator/formatter/tags.js @@ -49,6 +49,10 @@ export function tagFormatter(cell, tagComponent) tagElement.title = tag.notiz; tagElement.className = "tag " + tag.style; if (tag.done) tagElement.className += " tag_done"; + if (tag.automatisiert){ + tagElement.className += " tag_auto"; + tagElement.innerHTML = " " + tag.beschreibung; + } const tagDef = mappedData.find(t => t.typ_kurzbz === tag.typ_kurzbz); From a8dd99519515f91a1f7d769872d0e31cae0182ce Mon Sep 17 00:00:00 2001 From: ma0068 Date: Wed, 22 Jul 2026 14:11:07 +0200 Subject: [PATCH 4/7] rebuild tags for whole studiengang / selection refactor backend for multiaction rebuildTags add phrases refactor haederFilter --- application/core/Tag_Controller.php | 21 ++- .../components/DetailHeader/DetailHeader.js | 4 +- .../Stv/Studentenverwaltung/List.js | 69 +++++++++- .../Studentenverwaltung/List/ModalLoading.js | 69 ++++++++++ system/phrasesupdate.php | 123 ++++++++++++++++++ 5 files changed, 278 insertions(+), 8 deletions(-) create mode 100644 public/js/components/Stv/Studentenverwaltung/List/ModalLoading.js diff --git a/application/core/Tag_Controller.php b/application/core/Tag_Controller.php index 0600200c8..cb7deda82 100644 --- a/application/core/Tag_Controller.php +++ b/application/core/Tag_Controller.php @@ -344,16 +344,27 @@ class Tag_Controller extends FHCAPI_Controller public function rebuildTagsForTypeId() { - $id = $this->input->post('id'); + $ids = $this->input->post('ids'); $typeId = $this->input->post('typeId'); $semester = $this->input->post('sem'); - $result = $this->taglib->rebuildTagsForTypeId($typeId, $id, $semester); + $idsSuccess = []; + $idsError = []; + $outputResult = []; - if (isError($result)) - return error ('Error occurred during updateAutomatedTags'); + foreach ($ids as $id) + { + $result = $this->taglib->rebuildTagsForTypeId($typeId, $id, $semester); - $this->terminateWithSuccess($result); + if (isError($result)) + $idsError[] = $id; + else + $idsSuccess[] = $id; + + $outputResult[] = $result; + } + + $this->terminateWithSuccess([$outputResult, $idsSuccess, $idsError]); } private function _setAuthUID() diff --git a/public/js/components/DetailHeader/DetailHeader.js b/public/js/components/DetailHeader/DetailHeader.js index 84e462df3..8bf1fe2be 100644 --- a/public/js/components/DetailHeader/DetailHeader.js +++ b/public/js/components/DetailHeader/DetailHeader.js @@ -315,8 +315,10 @@ export default { this.reload(); }, rebuildPrestudentTags(){ + const id = [this.headerData[0].prestudent_id]; + const params = { - id : this.headerData[0].prestudent_id, + ids: id, typeId: 'prestudent_id', sem: this.currentSemester }; diff --git a/public/js/components/Stv/Studentenverwaltung/List.js b/public/js/components/Stv/Studentenverwaltung/List.js index 56a6a5334..f9abb3558 100644 --- a/public/js/components/Stv/Studentenverwaltung/List.js +++ b/public/js/components/Stv/Studentenverwaltung/List.js @@ -4,6 +4,7 @@ import CoreTag from '../../Tag/Tag.js'; import { tagHeaderFilter } from "../../../tabulator/filters/extendedHeaderFilter.js"; import { addTagInTable, deleteTagInTable, updateTagInTable } from "../../../../js/helpers/TagHelper.js"; import { tagFormatter } from "../../../../js/tabulator/formatter/tags.js"; +import ModalLoading from "./List/ModalLoading.js"; import ApiTag from "../../../api/factory/stv/tag.js"; import ListFilter from './List/Filter.js'; @@ -18,7 +19,8 @@ export default { CoreFilterCmpt, ListNew, CoreTag, - ListFilter + ListFilter, + ModalLoading }, directives: { draggable @@ -246,7 +248,9 @@ export default { oldScrollUrl: '', oldScrollLeft: 0, oldScrollTop: 0, - allPrestudents: [] + allPrestudents: [], + rebuildData: [], + isLoading: false } }, computed: { @@ -646,11 +650,55 @@ export default { this.oldScrollUrl = this.$refs.table.tabulator.getAjaxUrl(); } }, + rebuild(selected){ + this.isLoading = true; + let prestudentIds = []; + this.showModal(); + if(!selected.length) + prestudentIds = this.$refs.table.tabulator + .getData() + .map(row => row.prestudent_id); + else + prestudentIds = selected.map(item => item.prestudent_id); + + const params = { + ids: prestudentIds, + typeId: 'prestudent_id', + sem: this.studiensemesterKurzbz + }; + + return this.$api + .call(ApiTag.rebuildTagsforTypeId(params)) + .then(result => { + this.rebuildData = result.data; + this.isLoading = false; + console.log("Rebuild manually triggered"); + if(this.rebuildData[1].length > 0) + { + this.$fhcAlert.alertSuccess(this.$p.t('tag', 'alertSuccessRebuild', { count: this.rebuildData[1].length })); + } + if(this.rebuildData[2].length > 0) + { + this.$fhcAlert.alertError(this.$p.t('tag', 'alertErrorRebuild') + this.rebuildData[2].toString()); + } + }) + .catch(this.$fhcAlert.handleSystemError); + }, + showModal(){ + this.$refs.modalLocked.open(); + }, }, // TODO(chris): focusin, focusout, keydown and tabindex should be in the filter component // TODO(chris): filter component column chooser has no accessibilty features template: `
+ + +
+ + +