diff --git a/application/config/studierendenantrag.php b/application/config/studierendenantrag.php index ef2098aca..9d1e7a1c8 100644 --- a/application/config/studierendenantrag.php +++ b/application/config/studierendenantrag.php @@ -197,3 +197,11 @@ $config['unterbrechung_enabled'] = true; */ $config['wiederholung_enabled'] = true; +/** + * Modal with Infotext of TLC + * if true, there will be shown an info modal in the studentsView after the first button ABMELDUNG + * + * @var boolean + */ +$config['showInfoModalAbmeldung'] = true; + diff --git a/application/controllers/components/Cis/Mylv.php b/application/controllers/components/Cis/Mylv.php index 1fdb7e2a1..30544b647 100644 --- a/application/controllers/components/Cis/Mylv.php +++ b/application/controllers/components/Cis/Mylv.php @@ -14,11 +14,11 @@ class Mylv extends Auth_Controller public function __construct() { parent::__construct([ - 'Student' => ['student/anrechnung_beantragen:r','user:r'], // TODO(chris): permissions? - 'Studiensemester' => ['student/anrechnung_beantragen:r','user:r'], // TODO(chris): permissions? - 'Lvs' => ['student/anrechnung_beantragen:r','user:r'], // TODO(chris): permissions? - 'Info' => ['student/anrechnung_beantragen:r','user:r'], // TODO(chris): permissions? - 'Pruefungen' => ['student/anrechnung_beantragen:r','user:r'] // TODO(chris): permissions? + 'Student' => self::PERM_LOGGED, + 'Studiensemester' => self::PERM_LOGGED, + 'Lvs' => self::PERM_LOGGED, + 'Info' => self::PERM_LOGGED, + 'Pruefungen' => self::PERM_LOGGED ]); } diff --git a/application/controllers/jobs/AntragJob.php b/application/controllers/jobs/AntragJob.php index 5b72518d2..8bcb898a9 100644 --- a/application/controllers/jobs/AntragJob.php +++ b/application/controllers/jobs/AntragJob.php @@ -125,7 +125,7 @@ class AntragJob extends JOB_Controller $stgLeitungen[$leitung->uid]['stgs'][] = $antrag->studiengang_kz; } - $result = $this->StudierendenantragModel->getStgAndSem($antrag->studierendenantrag_id); + $result = $this->StudierendenantragModel->getStgAndDetails($antrag->studierendenantrag_id); if (isError($result)) { $this->logError(getError($result)); diff --git a/application/controllers/lehre/Studierendenantrag.php b/application/controllers/lehre/Studierendenantrag.php index ebc1d2eaa..ad333679d 100644 --- a/application/controllers/lehre/Studierendenantrag.php +++ b/application/controllers/lehre/Studierendenantrag.php @@ -25,7 +25,8 @@ class Studierendenantrag extends FHC_Controller // Load language phrases $this->loadPhrases([ - 'studierendenantrag' + 'studierendenantrag', + 'ui' ]); $this->load->config('studierendenantrag'); diff --git a/application/libraries/stv/StudentListLib.php b/application/libraries/stv/StudentListLib.php index 293d74932..8826b50b8 100644 --- a/application/libraries/stv/StudentListLib.php +++ b/application/libraries/stv/StudentListLib.php @@ -320,16 +320,9 @@ class StudentListLib * @param string studiensemester_kurzbz * */ - protected function addSelectAndJoinForTagsIfConfigured($studiensemester_kurzbz) - { + protected function addSelectAndJoinForTagsIfConfigured($studiensemester_kurzbz) + { if (defined('STV_TAGS_ENABLED') && STV_TAGS_ENABLED) { - - $this->_ci->load->model('system/Sprache_model', 'SpracheModel'); - $this->_ci->SpracheModel->addSelect('index'); - $result = $this->_ci->SpracheModel->loadWhere(array('sprache' => getUserLanguage())); - $language = hasData($result) ? getData($result)[0]->index : 1; - $index_bezeichnung_mehrsprachig = $language - 1; - $this->_ci->load->config('stv'); $tags = $this->_ci->config->item('stv_prestudent_tags'); @@ -351,7 +344,11 @@ class StudentListLib SELECT DISTINCT ON (n.notiz_id) n.notiz_id AS id, nt.typ_kurzbz, - array_to_json(nt.bezeichnung_mehrsprachig)->>". $index_bezeichnung_mehrsprachig . " AS beschreibung, + nt.bezeichnung_mehrsprachig[( + SELECT index + FROM tbl_sprache + WHERE sprache=" . $this->_ci->PrestudentModel->escape(getUserLanguage()) . " + )] AS beschreibung, n.text AS notiz, nt.style, n.erledigt AS done, @@ -387,7 +384,7 @@ class StudentListLib $this->addSelect('tag_data_agg.tags'); $this->addJoin($subQueryTag, 'tag_data_agg.prestudent_id = tbl_prestudent.prestudent_id', 'LEFT'); } - } + } /** * Get alias of a table or select statement diff --git a/application/models/education/Studierendenantrag_model.php b/application/models/education/Studierendenantrag_model.php index 40224ffc9..c18d18053 100644 --- a/application/models/education/Studierendenantrag_model.php +++ b/application/models/education/Studierendenantrag_model.php @@ -188,6 +188,7 @@ class Studierendenantrag_model extends DB_Model * Get the studiengang and ausbildungssemester the student was in * for the studiensemester the antrag was committed for * + * * @param integer $antrag_id * * @return stdClass @@ -242,6 +243,28 @@ class Studierendenantrag_model extends DB_Model ); } + /** + * Get the studiengang and the studienplan the student is in + * + * @param integer $antrag_id + * + * @return stdClass + */ + public function getStgAndDetails($antrag_id) + { + $this->addSelect('p.studiengang_kz'); + $this->addSelect('stg.orgform_kurzbz'); + $this->addSelect('stg.bezeichnung'); + $this->addJoin('public.tbl_prestudent p', 'prestudent_id'); + $this->addJoin('public.tbl_studiengang stg', 'studiengang_kz'); + + $this->addLimit(1); + + return $this->load( + $antrag_id + ); + } + public function getStgEmail($antrag_id) { $this->addJoin('public.tbl_prestudent p', 'prestudent_id'); diff --git a/application/views/lehre/Antrag/Student/List.php b/application/views/lehre/Antrag/Student/List.php index 6d769dafe..884d1f391 100644 --- a/application/views/lehre/Antrag/Student/List.php +++ b/application/views/lehre/Antrag/Student/List.php @@ -30,6 +30,12 @@ if(defined('CIS4')){ } ?> +load->config('studierendenantrag'); + $showInfoModalAbmeldung = $this->config->item('showInfoModalAbmeldung'); +?> +
@@ -43,17 +49,75 @@ if(defined('CIS4')){

()

+
-

p->t('studierendenantrag', 'calltoaction_' . $type); ?>

-
- - p->t('studierendenantrag', 'antrag_typ_' . $type); ?> - +

p->t('studierendenantrag', 'calltoaction_' . $type); ?>

+
+ + + + data-bs-toggle="modal" + + > + p->t('studierendenantrag', 'antrag_typ_' . $type); ?> + + + +
- + + + + @@ -205,7 +269,7 @@ if(defined('CIS4')){ p->t('studierendenantrag', 'my_lvs'); ?> diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Details.js b/public/js/components/Stv/Studentenverwaltung/Details/Details.js index e457c2773..0b5eec44d 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Details.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Details.js @@ -402,7 +402,7 @@ export default { name="foto" @actionbutton-clicked="sendInfomail" > - No Image + No Image -
- +
+
+ +
+
diff --git a/public/js/components/filter/Filter.js b/public/js/components/filter/Filter.js index 2df32d041..31b1efe12 100644 --- a/public/js/components/filter/Filter.js +++ b/public/js/components/filter/Filter.js @@ -107,7 +107,6 @@ export const CoreFilterCmpt = { default: [], }, }, - inject: ["language"], data: function () { return { uuid: 0, @@ -215,23 +214,9 @@ export const CoreFilterCmpt = { fieldIdsForVisibilty() { if (!this.tableBuilt) return []; - const localizedColumnTitles = this.tabulator.getLang().columns; - const isTabulatorLocalized = !!this.$props.tabulatorOptions.locale; - return this.tabulator - .getColumns() - .filter((col) => { - let def = col.getDefinition(); - let title = - isTabulatorLocalized && localizedColumnTitles[def.field] - ? localizedColumnTitles[def.field] - : def.title; - return ( - !def.frozen && - title && - def.formatter != "responsiveCollapse" - ); - }) - .map((col) => col.getField()); + return this.$props.tabulatorOptions?.columns + ?.filter((column) => column.formatter !== "responsiveCollapse") + .map((column) => column.field) ?? []; }, idExtra() { if (!this.uuid) return ""; @@ -261,6 +246,9 @@ export const CoreFilterCmpt = { ...{ title: filterTitles[el.name] }, })); }, + language() { + return this.$p.user_language; + }, }, watch: { "language.value": { @@ -931,7 +919,23 @@ export const CoreFilterCmpt = { if (!this.tableBuilt) { return {}; } else if (this.tabulator.options.locale) { - return this.tabulator.getLang().columns; + let result = this.tabulator.getLang().columns; + const columnFieldTitlePairs = this.tabulator + .getColumns() + .map((column) => { + const definition = column.getDefinition(); + return [definition.field, definition.title]; + }); + columnFieldTitlePairs.forEach((fieldTitlePair) => { + if ( + fieldTitlePair[0]?.length && + fieldTitlePair[1]?.length && + !(fieldTitlePair[0] in result) + ) { + result[fieldTitlePair[0]] = fieldTitlePair[1]; + } + }); + return result; } return this.tabulator.getColumns().reduce((res, col) => { res[col.getField()] = col.getDefinition().title; diff --git a/public/js/components/filter/Filter/TablePresets.js b/public/js/components/filter/Filter/TablePresets.js index 79f1b3e91..a8a0c8e4c 100644 --- a/public/js/components/filter/Filter/TablePresets.js +++ b/public/js/components/filter/Filter/TablePresets.js @@ -34,6 +34,7 @@ export default { presetInfo: null, newPreset: null, newPresetName: "", + presetIndexBeingApplied: null, }; }, computed: { @@ -161,6 +162,9 @@ export default { }, }, methods: { + getPresetIndex() { + return this.presetIndexBeingApplied; + }, async showPresetInfo(preset) { let shouldUpdatePresetInfo; if (!this.presetInfo) { @@ -393,14 +397,22 @@ export default { this.fetchCustomUserTabulatorPresets(); } }, - async applyPreset(preset) { - preset = this.syncPresetWithConfig(preset); - window.localStorage["tabulatorPreset-" + this.$props.presetsId] = - preset.id ? "custom:" + preset.id : "general:" + preset.name; + async applyPreset(preset, index) { + this.presetIndexBeingApplied = index; - this.$props.tabulator.modules.tablePresets.applyPreset(preset); - - this.$emit("tablePresetApplied", { preset }); + // workaround for updating DOM and displaying preset-loading spinner + // conventional use of $nextTick ineffective + setTimeout(() => { + preset = this.syncPresetWithConfig(preset); + window.localStorage["tabulatorPreset-" + this.$props.presetsId] = + preset.id ? "custom:" + preset.id : "general:" + preset.name; + + this.$props.tabulator.modules.tablePresets.applyPreset(preset); + + this.$emit("tablePresetApplied", { preset }); + + this.presetIndexBeingApplied = null; + }, 50); }, async fetchCustomUserTabulatorPresets() { let tabulatorPresetsResponse = await this.$api.call( @@ -453,7 +465,7 @@ export default { if (!preset) return; - this.applyPreset(preset); + this.applyPreset(preset, null); }, }, async created() { @@ -467,8 +479,19 @@ export default {
{{ $p.t("tabulator_presets/table_presets") }}
-
-
{{ preset.name }}
+
+
+ {{ preset.name }} +
+
+ Loading... +
+
+
diff --git a/public/js/tabulator/customModules/TablePresetsModule.js b/public/js/tabulator/customModules/TablePresetsModule.js index cb67e91fd..644514dc5 100644 --- a/public/js/tabulator/customModules/TablePresetsModule.js +++ b/public/js/tabulator/customModules/TablePresetsModule.js @@ -34,10 +34,21 @@ export default class TablePresetsModule extends Module { if (preset.headerFilters) { Object.entries(preset.headerFilters).forEach( ([columnField, filterValue]) => { - this.table.setHeaderFilterValue( - columnField, - filterValue, - ); + this.table.setHeaderFilterValue(columnField, filterValue); + + // double checking if filter inputs are filled in due to issues with custom header filters + const headerFilterInputElement = + this.table.columnManager.columns + .find((column) => column.field === columnField) + ?.element.querySelector( + ".tabulator-header-filter input", + ); + if ( + headerFilterInputElement && + headerFilterInputElement.value !== filterValue + ) { + headerFilterInputElement.value = filterValue; + } }, ); } diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 8630fe31a..448059932 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -41556,6 +41556,26 @@ array( ) ), // grades + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'grades_gradelist', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Studienverlauf', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Grade list', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'stv', @@ -62265,6 +62285,72 @@ I have been informed that I am under no obligation to consent to the transmissio ) ), // StudVw legacy Bis Links ende + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'link_Abmeldung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Weiter zur Abmeldung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Continue to De-registration', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'modalHinweistext_Abmeldung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Bevor du deine Abmeldung abschließt, möchten wir dir gerne ein Gespräch anbieten. Vielleicht gibt es Fragen, Unsicherheiten oder Herausforderungen, bei denen wir dich unterstützen können.
+
Du kannst dich jederzeit unverbindlich an deine Studiengangsassistenz, Studiengangsleitung oder das Student Support Team wenden. Ein Gespräch ist natürlich freiwillig – manchmal hilft es aber, gemeinsam Optionen zu besprechen, zum Beispiel auch eine vorübergehende +Studienunterbrechung. +

Wenn du deine Abmeldung fortsetzen möchtest, kannst du das selbstverständlich tun.", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Before you finalize your deregistration, we would like to offer you a consultation. Perhaps you have questions, uncertainties, or challenges that we can help you with.
+
You can contact your program assistant, program director, or the Student Support Team at any time without obligation. A consultation is, of course, voluntary; however, sometimes it helps to discuss options together, such as a +temporary break from your studies. +

If you wish to proceed with your deregistration, you can, of course, do so.", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'info_consultation', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Hinweis Beratungsgespräch', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Note Consultation', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), );