diff --git a/application/core/Tag_Controller.php b/application/core/Tag_Controller.php
index 082cb9ac4..b321bac92 100644
--- a/application/core/Tag_Controller.php
+++ b/application/core/Tag_Controller.php
@@ -15,11 +15,12 @@ class Tag_Controller extends FHCAPI_Controller
'getTag' => self::BERECHTIGUNG_KURZBZ,
'getTags' => self::BERECHTIGUNG_KURZBZ,
'addTag' => self::BERECHTIGUNG_KURZBZ,
-
'updateTag' => self::BERECHTIGUNG_KURZBZ,
'doneTag' => self::BERECHTIGUNG_KURZBZ,
'deleteTag' => self::BERECHTIGUNG_KURZBZ,
'getAllTags' => self::BERECHTIGUNG_KURZBZ,
+ 'getSemDates' => self::BERECHTIGUNG_KURZBZ,
+ 'getAllStartAndEndAutomatedTags' => self::BERECHTIGUNG_KURZBZ,
'rebuildTagsForTypeId' => self::BERECHTIGUNG_KURZBZ,
];
@@ -341,16 +342,46 @@ class Tag_Controller extends FHCAPI_Controller
{
$id = $this->input->post('id');
$typeId = $this->input->post('typeId');
+ $semester = $this->input->post('sem');
+
+ $result = $this->taglib->rebuildTagsForTypeId($typeId, $id, $semester);
- $result = $this->taglib->rebuildTagsForTypeId($typeId, $id);
- //TODO (refactor; um semester, studiengang_kz)
- //$result = $this->taglib->rebuildTagsForTypeId($params);
if (isError($result))
return error ('Error occurred during updateAutomatedTags');
$this->terminateWithSuccess($result);
}
+ public function getSemDates()
+ {
+ $studiensemester_kurzbz = $this->input->get('semester');
+ $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
+ $result = $this->StudiensemesterModel->loadWhere(array('studiensemester_kurzbz' => $studiensemester_kurzbz));
+ if (isError($result))
+ return error('Error occurred during retrieving studiensemester');
+ $data = getData($result);
+ $this->terminateWithSuccess(current($data));
+
+ }
+
+ public function getAllStartAndEndAutomatedTags()
+ {
+ $this->NotizModel->addSelect('notiz_id as id');
+ $this->NotizModel->addSelect('start');
+ $this->NotizModel->addSelect('ende');
+ $this->NotizModel->addSelect('typ');
+
+ $result = $this->NotizModel->loadWhere(array(
+ 'titel' => 'TAG',
+ 'verfasser_uid' => 'sftest'
+ ));
+
+ if (isError($result))
+ return error('Error occurred during retrieving intervalls automated tags');
+ $data = getData($result);
+ $this->terminateWithSuccess($data);
+ }
+
private function _setAuthUID()
{
$this->_uid = getAuthUID();
diff --git a/application/libraries/TagLib.php b/application/libraries/TagLib.php
index 2442b2fab..4eb8dedef 100644
--- a/application/libraries/TagLib.php
+++ b/application/libraries/TagLib.php
@@ -241,21 +241,18 @@ class TagLib
/*
* main function for rebuild Tags for typeId
* */
- public function rebuildTagsForTypeId($typeId, $id) //TODO aktSem of frontend
+ public function rebuildTagsForTypeId($typeId, $id, $studiensemester_kurzbz)
{
$automatedTagsRes = $this->_ci->NotiztypModel->loadWhere(array('automatisiert' => true, 'taglib IS NOT NULL' => null));
$automatedTags = hasData($automatedTagsRes) ? getData($automatedTagsRes) : [];
- //TODO change to chosen Studiensemester in frontend
- $result = $this->_ci->StudiensemesterModel->getAktOrNextSemester();
+ $result = $this->_ci->StudiensemesterModel->load($studiensemester_kurzbz);
if (isError($result))
return error('Error occurred during retrieving studiensemester');
if (empty($result->retval) || !isset($result->retval[0])) {
return error('No studiensemester found');
}
- $studiensemester_kurzbz = $result->retval[0]->studiensemester_kurzbz ?? null;
- //for checkDelete
$startSem = $result->retval[0]->start ?? null;
$endeSem = $result->retval[0]->ende ?? null;
$return = [];
diff --git a/public/js/api/factory/stv/tag.js b/public/js/api/factory/stv/tag.js
index 26aacca69..3a3f78167 100644
--- a/public/js/api/factory/stv/tag.js
+++ b/public/js/api/factory/stv/tag.js
@@ -61,6 +61,21 @@ export default {
};
},
+ getSemDates(studiensemester_kurzbz){
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/stv/Tags/getSemDates',
+ params: studiensemester_kurzbz
+ };
+ },
+
+ getAllStartAndEndAutomatedTags(){
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/stv/Tags/getAllStartAndEndAutomatedTags',
+ };
+ },
+
rebuildTagsforTypeId(data){
return {
method: 'post',
diff --git a/public/js/components/DetailHeader/DetailHeader.js b/public/js/components/DetailHeader/DetailHeader.js
index 516ecdd87..076b7db56 100644
--- a/public/js/components/DetailHeader/DetailHeader.js
+++ b/public/js/components/DetailHeader/DetailHeader.js
@@ -49,6 +49,10 @@ export default {
from: 'configStvTagsEnabled',
default: false
},
+ currentSemester: {
+ from: 'currentSemester',
+ required: true
+ },
},
computed: {
appRoot() {
@@ -85,6 +89,7 @@ export default {
}
if(this.tagsEnabled) {
+ this.getSemesterDates(this.currentSemester);
this.loadTagsAndRender(this.headerData[0].prestudent_id);
}
@@ -113,6 +118,14 @@ export default {
}
},
deep: true,
+ },
+ currentSemester: {
+ handler(newVal) {
+ if (newVal) {
+ this.getSemesterDates(newVal);
+ }
+ },
+ deep: true,
}
},
data(){
@@ -123,7 +136,8 @@ export default {
isFetchingIssues: false,
tagEndpoint: ApiTag,
tagData: null,
- rebuildData: null
+ rebuildData: null,
+ semDates: {}
};
},
methods: {
@@ -219,7 +233,9 @@ export default {
prestudent_id,
this.tagData,
this.$refs.tagComponent,
- 'prestudent_id'
+ 'prestudent_id',
+ this.semDates.start,
+ this.semDates.ende
);
this.$refs.tagWrapper.innerHTML = '';
@@ -248,7 +264,8 @@ export default {
rebuildPrestudentTags(){
const params = {
id : this.headerData[0].prestudent_id,
- typeId: 'prestudent_id'
+ typeId: 'prestudent_id',
+ sem: this.currentSemester
};
return this.$api
@@ -259,6 +276,17 @@ export default {
this.reload();
})
.catch(this.$fhcAlert.handleSystemError);
+ },
+ getSemesterDates(semester){
+ const params = {
+ studiensemester_kurzbz: semester
+ };
+ return this.$api
+ .call(ApiTag.getSemDates({semester}))
+ .then(result => {
+ this.semDates = result.data;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
}
},
template: `
@@ -339,8 +367,10 @@ export default {
v-if="tagsEnabled"
@click="rebuildPrestudentTags"
class="btn btn-outline btn-light mb-1"
- title="Automatische Tags neu laden">
+ :title="'Automatische Tags fuer ' + currentSemester + ' neu laden'"
+ >
+ {{currentSemester}}
unruly
diff --git a/public/js/components/Stv/Studentenverwaltung/List.js b/public/js/components/Stv/Studentenverwaltung/List.js
index 4fe8b5eb6..d98883165 100644
--- a/public/js/components/Stv/Studentenverwaltung/List.js
+++ b/public/js/components/Stv/Studentenverwaltung/List.js
@@ -237,7 +237,9 @@ export default {
dragSource: [],
oldScrollUrl: '',
oldScrollLeft: 0,
- oldScrollTop: 0
+ oldScrollTop: 0,
+ semDates: {}, //TODO(Manu) check injections
+ intervalMap: {}
}
},
computed: {
@@ -293,7 +295,12 @@ export default {
},
},
created: function() {
+
if(this.tagsEnabled) {
+ this.getSemesterDates(this.currentSemester);
+
+ this.loadIntervals(); //preload
+
const coltags = {
title: 'Tags',
field: 'tags',
@@ -301,7 +308,36 @@ export default {
headerFilter: "input",
headerFilterFunc: tagHeaderFilter,
headerFilterFuncParams: {field: 'tags'},
- formatter: (cell) => tagFormatter(cell, this.$refs.tagComponent),
+ //formatter: (cell) => tagFormatter(cell, this.$refs.tagComponent), //prev. Version without filter
+ formatter: (cell) => {
+ const raw = cell.getValue();
+
+ const tags =
+ Array.isArray(raw)
+ ? raw
+ : (typeof raw === 'string'
+ ? JSON.parse(raw)
+ : []);
+
+ const id = tags?.[0]?.id;
+
+ const interval = id
+ ? this.intervalMap[id]
+ : null;
+
+ const enrichedTags = {
+ tags: tags,
+ start: interval?.start || null,
+ ende: interval?.end || null,
+ };
+
+ return tagFormatter(
+ enrichedTags,
+ this.$refs.tagComponent,
+ this.semDates.start,
+ this.semDates.ende
+ );
+ },
width: 150,
};
this.tabulatorOptions.columns.splice(2, 0, coltags);
@@ -312,9 +348,40 @@ export default {
if (n !== o && o !== undefined && this.$refs.table.tableBuilt) {
this.translateTabulator();
}
+ },
+ currentSemester: {
+ handler(newVal) {
+ if (newVal) {
+ this.getSemesterDates(newVal);
+ }
+ },
+ deep: true,
}
},
methods: {
+ loadIntervals() {
+ return this.$api
+ .call(ApiTag.getAllStartAndEndAutomatedTags())
+ .then(result => {
+ const data = result.data || [];
+ this.intervalMap = {};
+ data.forEach(item => {
+ this.intervalMap[item.id] = item;
+ });
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ },
+ getSemesterDates(semester){ //TODO(check for injections)
+ const params = {
+ studiensemester_kurzbz: semester
+ };
+ return this.$api
+ .call(ApiTag.getSemDates({semester}))
+ .then(result => {
+ this.semDates = result.data;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ },
translateTabulator() {
this.$p
.loadCategory(['global', 'person', 'lehre', 'ui', 'profilUpdate', 'admission', 'stv'])
@@ -398,7 +465,6 @@ export default {
//for tags
this.selectedRows = this.$refs.table.tabulator.getSelectedRows();
this.selectedColumnValues = this.selectedRows.filter(row => row.getData().prestudent_id !== undefined && row.getData().prestudent_id).map(row => row.getData().prestudent_id);
-
this.$emit('update:selected', data);
},
autoSelectRows(data) {
@@ -601,6 +667,7 @@ export default {
// TODO(chris): filter component column chooser has no accessibilty features
template: `
+ test manu: currentSEM: {{semDates.start}} - {{semDates.ende}}
({
id: tag.notiz_id,
typ_kurzbz: tag.titel?.toLowerCase(),
@@ -12,9 +15,33 @@ export function idTagFormatter (id, tagData, tagComponent, typeId)
style: tag.style,
done: tag.done,
automatisiert: tag.automatisiert,
- typeId: id
+ typeId: id,
+ validFrom: tag.start ? new Date(tag.start) : null,
+ validTo: tag.ende ? new Date(tag.ende) : null
}));
+ const isInSemester = (tag) => {
+ if (!hasSemesterFilter) return true;
+
+ if (!tag.validFrom && !tag.validTo) return true;
+
+ if (!tag.validFrom && !tag.validTo) return true;
+
+ if (tag.validFrom && tag.validTo) {
+ return tag.validFrom <= semEnd && tag.validTo >= semStart;
+ }
+
+ if (tag.validFrom && !tag.validTo) {
+ return tag.validFrom <= semEnd;
+ }
+
+ if (!tag.validFrom && tag.validTo) {
+ return tag.validTo >= semStart;
+ }
+
+ return false;
+ };
+
let container = document.createElement('div');
container.className = "d-flex gap-1";
@@ -23,7 +50,9 @@ export function idTagFormatter (id, tagData, tagComponent, typeId)
const renderTags = () => {
container.innerHTML = '';
- let filtered = parsedTags.filter(t => t != null);
+ let filtered = parsedTags
+ .filter(t => t != null)
+ .filter(isInSemester);
filtered.sort((a, b) => {
let adone = a.done ? 1 : 0;
diff --git a/public/js/tabulator/formatter/tags.js b/public/js/tabulator/formatter/tags.js
index b9578dbd4..d9a5244fd 100644
--- a/public/js/tabulator/formatter/tags.js
+++ b/public/js/tabulator/formatter/tags.js
@@ -1,28 +1,115 @@
-export function tagFormatter(cell, tagComponent)
-{
+export function tagFormatter(
+ cell,
+ tagComponent,
+ semesterStart = null,
+ semesterEnd = null
+) {
+
+ // support both call versions
+ // 1. previous Tabulator cell: old version
+ // 2. custom enriched object: with start and end for tags plus semesterDates
+ // for check if valid tag
+
+ const isTabulatorCell =
+ cell && typeof cell.getValue === 'function';
+
+ const normalized = isTabulatorCell
+ ? {
+ tags: cell.getValue() || [],
+ start: null,
+ ende: null,
+ rowData: cell.getRow().getData(),
+ }
+ : {
+ tags: cell.tags || [],
+ start: cell.start || null,
+ ende: cell.ende || null,
+ rowData: cell.rowData || {},
+ };
+
+ const tags = normalized.tags || [];
+
+ if (!tags.length) {
+ return "";
+ }
+
const mappedData = tagComponent.tags.map(tag => ({
typ_kurzbz: tag.tag_typ_kurzbz,
- automatisiert: tag.automatisiert
+ automatisiert: tag.automatisiert,
+ validFrom: normalized.start
+ ? new Date(normalized.start)
+ : null,
+ validTo: normalized.ende
+ ? new Date(normalized.ende)
+ : null
}));
+ const hasSemesterFilter =
+ !!(semesterStart && semesterEnd);
- let tags = cell.getValue();
- if (!tags) return;
+ const semStart = hasSemesterFilter ? new Date(semesterStart) : null;
+
+ const semEnd = hasSemesterFilter ? new Date(semesterEnd) : null;
+
+ const isInSemester = (tag) => {
+
+ if (!hasSemesterFilter) {
+ return true;
+ }
+
+ if (!tag.validFrom && !tag.validTo) {
+ return true;
+ }
+
+ if (tag.validFrom && tag.validTo) {
+ return (
+ tag.validFrom <= semEnd &&
+ tag.validTo >= semStart
+ );
+ }
+
+ if (tag.validFrom && !tag.validTo) {
+ return tag.validFrom <= semEnd;
+ }
+
+ if (!tag.validFrom && tag.validTo) {
+ return tag.validTo >= semStart;
+ }
+
+ return false;
+ };
+
+ // parse tags if needed
+ let parsedTags =
+ typeof tags === 'string'
+ ? JSON.parse(tags)
+ : tags;
let container = document.createElement('div');
container.className = "d-flex gap-1";
- let parsedTags = JSON.parse(tags);
let maxVisibleTags = 2;
- const rowData = cell.getRow().getData();
+ const rowData = normalized.rowData;
+
if (rowData._tagExpanded === undefined) {
rowData._tagExpanded = false;
}
const renderTags = () => {
container.innerHTML = '';
- parsedTags = parsedTags.filter(item => item !== null);
+
+ parsedTags = parsedTags.filter(tag => {
+ const mapped = mappedData.find(
+ m => m.typ_kurzbz === tag.typ_kurzbz
+ );
+
+ if (!mapped) {
+ return true;
+ }
+
+ return isInSemester(mapped);
+ });
parsedTags.sort((a, b) => {
let adone = a.done ? 1 : 0;
@@ -34,10 +121,14 @@ export function tagFormatter(cell, tagComponent)
}
return b.id - a.id;
});
- const tagsToShow = rowData._tagExpanded ? parsedTags : parsedTags.slice(0, maxVisibleTags);
+
+ const tagsToShow = rowData._tagExpanded
+ ? parsedTags
+ : parsedTags.slice(0, maxVisibleTags);
tagsToShow.forEach(tag => {
if (!tag) return;
+
let tagElement = document.createElement('span');
tagElement.innerText = tag.beschreibung;
tagElement.title = tag.notiz;
@@ -60,8 +151,10 @@ export function tagFormatter(cell, tagComponent)
container.appendChild(tagElement);
});
- if (parsedTags.length > maxVisibleTags) {
+ // show expand button
+ if ( parsedTags.length > maxVisibleTags) {
let toggle = document.createElement('button');
+
toggle.innerText = (rowData._tagExpanded ? '- ' : '+ ') + (parsedTags.length - maxVisibleTags);
toggle.className = "display_all";
toggle.title = rowData._tagExpanded ? "Tags ausblenden" : "Tags einblenden";