From 3465e299f7ec81bd373d9806d14c31e1a437d19f Mon Sep 17 00:00:00 2001 From: ma0048 Date: Thu, 12 Feb 2026 08:16:31 +0100 Subject: [PATCH 01/11] tag - helper and formatter --- public/js/helpers/TagHelper.js | 124 ++++++++++++++++++ .../tabulator/filters/extendedHeaderFilter.js | 2 +- public/js/tabulator/formatter/tags.js | 67 ++++++++++ 3 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 public/js/helpers/TagHelper.js create mode 100644 public/js/tabulator/formatter/tags.js diff --git a/public/js/helpers/TagHelper.js b/public/js/helpers/TagHelper.js new file mode 100644 index 000000000..9282aa167 --- /dev/null +++ b/public/js/helpers/TagHelper.js @@ -0,0 +1,124 @@ +export function addTagInTable(addedTag, rows, matchKey, tagsKey = "tags") +{ + if (!addedTag || !Array.isArray(addedTag.response)) + return; + + rows.forEach(row => + { + const rowData = row.getData(); + let updated = false; + + addedTag.response.forEach(tag => + { + if (rowData[matchKey] !== tag[matchKey]) + return; + + let tags; + try { + tags = JSON.parse(rowData[tagsKey] || "[]"); + } catch (e) { + tags = []; + } + + if (!Array.isArray(tags)) + tags = []; + + if (tags.some(t => t?.id === tag.id)) + return; + + let newTag = { ...addedTag, id: tag.id }; + + tags.unshift(newTag); + + rowData[tagsKey] = JSON.stringify(tags); + updated = true; + }); + + if (updated) + row.update(rowData); + }); +} + +export function deleteTagInTable(deletedTag, rows, tagsKeys = ['tags']) +{ + if (!Array.isArray(tagsKeys)) + tagsKeys = [tagsKeys]; + + rows.forEach(row => { + let rowData = row.getData(); + let updates = {}; + let changed = false; + + tagsKeys.forEach(key => { + let tags; + + try { + tags = JSON.parse(rowData[key] || "[]"); + } catch (e) { + tags = []; + } + + if (!Array.isArray(tags)) + return; + + let filtered = tags.filter(tag => tag?.id !== deletedTag); + + if (filtered.length !== tags.length) + { + updates[key] = JSON.stringify(filtered); + changed = true; + } + }); + + if (changed) { + row.update(updates); + row.reformat(); + } + }); +} + + +export function updateTagInTable(updatedTag, rows, fields = ['tags']) +{ + if (!Array.isArray(fields)) + fields = [fields]; + + rows.forEach(row => + { + const rowData = row.getData(); + let updated = false; + + fields.forEach(field => + { + if (!rowData[field]) + return; + + let fieldData; + try { + fieldData = JSON.parse(rowData[field] || "[]"); + } catch (e) { + return; + } + + if (!Array.isArray(fieldData)) + return; + + let index = fieldData.findIndex(tag => tag?.id === updatedTag.id); + + if (index !== -1) + { + fieldData[index] = { ...updatedTag }; + let updatedFieldData = JSON.stringify(fieldData); + + if (updatedFieldData !== rowData[field]) + { + rowData[field] = updatedFieldData; + updated = true; + } + } + }); + + if (updated) + row.update(rowData); + }); +} diff --git a/public/js/tabulator/filters/extendedHeaderFilter.js b/public/js/tabulator/filters/extendedHeaderFilter.js index 7bf86c119..35b66dc1c 100644 --- a/public/js/tabulator/filters/extendedHeaderFilter.js +++ b/public/js/tabulator/filters/extendedHeaderFilter.js @@ -146,7 +146,7 @@ export function tagHeaderFilter(headerValue, rowValue, rowData, filterParams) if (Array.isArray(data)) { combinedText = data - .filter(item => item?.done === false) + .filter(item => item?.done !== true) .map(item => `${item?.beschreibung} ${item?.notiz}`) .join(' '); } diff --git a/public/js/tabulator/formatter/tags.js b/public/js/tabulator/formatter/tags.js new file mode 100644 index 000000000..0d2f5004c --- /dev/null +++ b/public/js/tabulator/formatter/tags.js @@ -0,0 +1,67 @@ +export function tagFormatter(cell, tagComponent) +{ + let tags = cell.getValue(); + if (!tags) return; + + let container = document.createElement('div'); + container.className = "d-flex gap-1"; + + let parsedTags = JSON.parse(tags); + let maxVisibleTags = 2; + + const rowData = cell.getRow().getData(); + if (rowData._tagExpanded === undefined) { + rowData._tagExpanded = false; + } + + const renderTags = () => { + container.innerHTML = ''; + parsedTags = parsedTags.filter(item => item !== null); + + parsedTags.sort((a, b) => { + let adone = a.done ? 1 : 0; + let bbone = b.done ? 1 : 0; + + if (adone !== bbone) + { + return adone - bbone; + } + return b.id - a.id; + }); + 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; + tagElement.className = "tag " + tag.style; + if (tag.done) tagElement.className += " tag_done"; + + tagElement.addEventListener('click', (event) => { + event.stopPropagation(); + event.preventDefault(); + tagComponent.editTag(tag.id); + }); + + container.appendChild(tagElement); + }); + + 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"; + + toggle.addEventListener('click', () => { + rowData._tagExpanded = !rowData._tagExpanded; + renderTags(); + }); + + container.appendChild(toggle); + } + }; + + renderTags(); + return container; +} \ No newline at end of file From 5c1e967d5e680a43fd17343031d9d5c761b4fc13 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Thu, 12 Feb 2026 16:05:02 +0100 Subject: [PATCH 02/11] dont reset z-index on opening tiered menu (email button) by setting its autoZIndex property to false --- public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js index 7fa78f7d1..c0c8d7ba8 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js @@ -1392,7 +1392,7 @@ export const AbgabetoolAssistenz = { > - + From 043b1bcf114047eb39ece39ad801a3fbe330b4d7 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Thu, 12 Feb 2026 17:38:00 +0100 Subject: [PATCH 03/11] extracted email split method from stv/kontakt component to helperfile; adjusted that method to take subject param & make phrasen/alert call via parameter reference; --- .../Cis/Abgabetool/AbgabetoolAssistenz.js | 19 +++---- .../Details/Kontaktieren.js | 49 ++----------------- public/js/helpers/EmailHelpers.js | 45 +++++++++++++++++ 3 files changed, 56 insertions(+), 57 deletions(-) create mode 100644 public/js/helpers/EmailHelpers.js diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js index c0c8d7ba8..a7da9da5a 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js @@ -7,6 +7,7 @@ import ApiAbgabe from '../../../api/factory/abgabe.js' import ApiStudiensemester from '../../../api/factory/studiensemester.js'; import AbgabeterminStatusLegende from "./StatusLegende.js"; import FhcOverlay from "../../Overlay/FhcOverlay.js"; +import { splitMailsHelper } from "../../../helpers/EmailHelpers.js" // spoofed date testing // const todayISO = '2025-08-08' @@ -226,18 +227,17 @@ export const AbgabetoolAssistenz = { ]}; }, methods: { - sammelMailStudent() { + sammelMailStudent(param) { + const emails = this.selectedData .map(row => `${row.student_uid}@${this.domain}`) .join(','); - + const uniqueRecipients = [...new Set(emails)]; const subject = this.$p.t('abgabetool/c4sammelmailStudentBetreff', [this.selectedStudiengangOption?.bezeichnung]); - - const href = `mailto:${emails}?subject=${subject}`; - - window.location.href = href + splitMailsHelper(uniqueRecipients, param.originalEvent, subject, this.$fhcAlert, this.$p) }, - sammelMailBetreuer() { + sammelMailBetreuer(param) { + const recipientList = []; this.selectedData.forEach(row => { if (row.betreuer_mail) recipientList.push(row.betreuer_mail); @@ -246,11 +246,8 @@ export const AbgabetoolAssistenz = { // actually not necessary for email clients but looks better for assistenz if we avoid duplicates here const uniqueRecipients = [...new Set(recipientList)]; - const subject = this.$p.t('abgabetool/c4sammelmailBetreuerBetreff', [this.selectedStudiengangOption?.bezeichnung]); - const href = `mailto:${uniqueRecipients.join(',')}?subject=${encodeURIComponent(subject)}`; - - window.location.href = href; + splitMailsHelper(uniqueRecipients, param.originalEvent, subject, this.$fhcAlert, this.$p) }, selectHandler(e, cell) { const row = cell.getRow(); diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Kontaktieren.js b/public/js/components/Stv/Studentenverwaltung/Details/Kontaktieren.js index bd7554a47..43995b918 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Kontaktieren.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Kontaktieren.js @@ -1,3 +1,4 @@ +import { splitMailsHelper } from "../../../../helpers/EmailHelpers.js" export default { name: "Kontaktieren", computed: { @@ -22,60 +23,16 @@ export default { }, methods: { - async splitMails(mails, event) { - let splititem = ","; - let maillist = mails.join(splititem); - let mailto = ""; - - if (maillist.length > 2024) - { - if (await this.$fhcAlert.confirm({message: this.$p.t('stv', 'zuvieleEMails') }) === false) - return; - } - - let firstrun = true; - let useBcc = event?.ctrlKey || event?.metaKey; - while (maillist.length > 0) - { - if (maillist.length > 2024) - { - let splitposition = maillist.lastIndexOf(splititem, 1900); - mailto = maillist.substring(0, splitposition); - maillist = maillist.substring(splitposition + 1); - } - else - { - mailto = maillist; - maillist = ""; - } - - let mailLink = useBcc ? `mailto:?bcc=${mailto}` : `mailto:${mailto}`; - - if (firstrun) - { - window.location.href = mailLink; - firstrun = false; - } - else - { - if (await this.$fhcAlert.confirm({message: this.$p.t('stv', 'weitereEMail')}) === true) - { - window.location.href = mailLink; - } - } - - } - }, internMail(event) { if (this.internMails.length) { - this.splitMails(this.internMails, event); + splitMailsHelper(this.privateMails, event, null, this.$fhcAlert, this.$p) } }, privateMail(event) { if (this.privateMails.length) { - this.splitMails(this.privateMails, event); + splitMailsHelper(this.privateMails, event, null, this.$fhcAlert, this.$p) } } }, diff --git a/public/js/helpers/EmailHelpers.js b/public/js/helpers/EmailHelpers.js new file mode 100644 index 000000000..87daa828a --- /dev/null +++ b/public/js/helpers/EmailHelpers.js @@ -0,0 +1,45 @@ +export async function splitMailsHelper(mails, event, subject, alertPluginRef, phrasenPluginRef) { + let splititem = ","; + let maillist = mails.join(splititem); + let mailto = ""; + // take subject line length + '?subject=' length into account + const subjectlength = subject && typeof subject === 'string' ? subject.length + 9 : 0 + if (maillist.length > 2024) + { + if (await alertPluginRef.confirm({message: phrasenPluginRef.t('stv', 'zuvieleEMails') }) === false) + return; + } + + let firstrun = true; + let useBcc = event?.ctrlKey || event?.metaKey; + while (maillist.length > 0) + { + if (maillist.length + subjectlength > 2024) + { + let splitposition = maillist.lastIndexOf(splititem, 1900); + mailto = maillist.substring(0, splitposition); + maillist = maillist.substring(splitposition + 1); + } + else + { + mailto = maillist; + maillist = ""; + } + + let mailLink = useBcc ? `mailto:?bcc=${mailto}` : `mailto:${mailto}`; + if(subject && typeof subject === 'string') mailLink += `?subject=${subject}` + if (firstrun) + { + window.location.href = mailLink; + firstrun = false; + } + else + { + if (await alertPluginRef.confirm({message: phrasenPluginRef.t('stv', 'weitereEMail')}) === true) + { + window.location.href = mailLink; + } + } + + } +} \ No newline at end of file From d9d15c1ed3d5b61bd06e296485885f4d97c24b32 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Fri, 13 Feb 2026 11:10:16 +0100 Subject: [PATCH 04/11] neue tag farben --- public/css/tags.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/public/css/tags.css b/public/css/tags.css index 9e0d7ee4b..e92f415b2 100644 --- a/public/css/tags.css +++ b/public/css/tags.css @@ -51,6 +51,14 @@ background-color: #6d4c41; } +.tag_dark_grey { + background-color: #595959; +} + +.tag_light_grey { + background-color: #9a9a9a; +} + .tag_blau { background-color: #508498; } From 632866c8c420b9adab3305e73fe6f64410b32932 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Fri, 13 Feb 2026 13:45:12 +0100 Subject: [PATCH 05/11] reset newTermin object when switching projektarbeit so they are assigned to the correct student --- .../Cis/Abgabetool/AbgabeMitarbeiterDetail.js | 18 ++++++++++++++++++ .../Cis/Abgabetool/AbgabetoolMitarbeiter.js | 1 - 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js b/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js index 42952d0df..b303a831e 100644 --- a/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js +++ b/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js @@ -643,6 +643,24 @@ export const AbgabeMitarbeiterDetail = { 'projektarbeit'(newVal) { // set invertedFixtermin field for UI/UX purposes -> avoid double negation in text + // reset newTermin object + const typ = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === 'zwischen') + this.newTermin = { + 'paabgabe_id': -1, + 'projektarbeit_id': newVal.projektarbeit_id, + 'fixtermin': false, + 'invertedFixtermin': true, + 'kurzbz': '', + 'datum': new Date().toISOString().split('T')[0], + 'note': this.allowedNotenOptions.find(opt => opt.note == 9), + 'beurteilungsnotiz': '', + 'upload_allowed': typ.upload_allowed_default, + 'paabgabetyp_kurzbz': '', + 'bezeichnung': typ, + 'abgabedatum': null, + 'insertvon': this.viewData?.uid ?? '' + } + newVal?.abgabetermine?.forEach(termin => termin.invertedFixtermin = !termin.fixtermin) // default select german if projektarbeit sprache was null diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js b/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js index 67e1d09af..ff414ee10 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js @@ -333,7 +333,6 @@ export const AbgabetoolMitarbeiter = { pa.student = `${pa.vorname} ${pa.nachname}` this.selectedProjektarbeit = pa - this.$refs.modalContainerAbgabeDetail.show() }).finally(()=>{this.loading = false}) From 60294dd8f2591031fe5b90b67a9e43a89d977ab0 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Mon, 16 Feb 2026 03:22:39 +0100 Subject: [PATCH 06/11] paBenotet evaluation fix --- public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js b/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js index ff414ee10..f33333ea3 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js @@ -304,7 +304,7 @@ export const AbgabetoolMitarbeiter = { pa.isCurrent = res.data[1] let paIsBenotet = false - if(pa.note !== undefined && pa !== null) { + if(pa.note !== undefined && pa.note !== null) { // check if the note is not defined as a non final projektarbeit note const opt = this.notenOptionsNonFinal.find(opt => opt.note) // if thats the case allow further work From 3831f3c1d781ae1d9477e215d52a0006bba7b014 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Mon, 16 Feb 2026 03:41:30 +0100 Subject: [PATCH 07/11] consistent use of :optionDisabled="getOptionDisabled" for paabgabetyp dropdowns in assistenz view --- .../components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js | 1 + .../js/components/Cis/Abgabetool/AbgabetoolAssistenz.js | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js b/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js index b303a831e..971783746 100644 --- a/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js +++ b/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js @@ -729,6 +729,7 @@ export const AbgabeMitarbeiterDetail = { v-model="newTermin.bezeichnung" :options="getAllowedAbgabeTypeOptions" :optionLabel="getOptionLabelAbgabetyp" + :optionDisabled="getOptionDisabled" scrollHeight="300px"> diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js index a7da9da5a..34ddd3fc2 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js @@ -966,7 +966,10 @@ export const AbgabetoolAssistenz = { // this.loadProjektarbeiten() this.calcMaxTableHeight() - } + }, + getOptionDisabled(option) { + return !option.aktiv + }, }, computed: { emailItems() { @@ -1176,7 +1179,8 @@ export const AbgabetoolAssistenz = { :style="{'width': '100%'}" v-model="serienTermin.bezeichnung" :options="abgabeTypeOptions" - :optionLabel="getOptionLabelAbgabetyp"> + :optionLabel="getOptionLabelAbgabetyp" + :optionDisabled="getOptionDisabled"> From 5415180b2ce2b77d15918cefe436c1aa08f0c77d Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Mon, 16 Feb 2026 14:18:59 +0100 Subject: [PATCH 08/11] fetch count and paginated data in one query --- application/models/system/Message_model.php | 105 ++++++++++---------- 1 file changed, 51 insertions(+), 54 deletions(-) diff --git a/application/models/system/Message_model.php b/application/models/system/Message_model.php index e0a185f9b..741c96ade 100644 --- a/application/models/system/Message_model.php +++ b/application/models/system/Message_model.php @@ -242,74 +242,71 @@ class Message_model extends DB_Model */ public function getMessagesForTable($person_id, $offset, $limit) { - $sql_base = " - SELECT + $sql = <<execQuery($sql, $parametersArray); - - if (isError($count)) - return $count; - - $count = ceil(current(getData($count))->count/$limit); - $sql = " - SELECT * FROM ( - " . $sql_base . " - ) a - ORDER BY insertamum DESC - LIMIT ? - OFFSET ? - "; + (SELECT COALESCE(titelpre,'') || ' ' || COALESCE(vorname,'') || ' ' || COALESCE(nachname,'') || ' ' || COALESCE(titelpost,'') FROM public.tbl_person WHERE person_id = fm.sender_id) as sender, + (SELECT COALESCE(titelpre,'') || ' ' || COALESCE(vorname,'') || ' ' || COALESCE(nachname,'') || ' ' || COALESCE(titelpost,'') FROM public.tbl_person WHERE person_id = fm.recipient_id) as recipient, + fm.sender_id, + fm.recipient_id, + ms.status, + ms.insertamum as statusdatum + from + filtered_messages fm + join + public.tbl_msg_message m on fm.message_id = m.message_id + join + lastmsgstatus ms on fm.message_id = ms.message_id and fm.recipient_id = ms.person_id + order by + m.insertamum DESC + limit ? + offset ?; +EOSQL; $parametersArray = array($person_id, $person_id, $limit, $offset); + $count = 0; $data = $this->execQuery($sql, $parametersArray); if (isError($data)) return $data; $data = getData($data); + if($data) + { + $count = ceil($data[0]->total_msgs / $limit); + } return success(['data' => $data, 'count' => $count]); } From 962cbf4e783958bf75822dbe988aba3097166f5f Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Mon, 16 Feb 2026 15:16:49 +0100 Subject: [PATCH 09/11] join person table for sender and recipient instead of using subselect --- application/models/system/Message_model.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/application/models/system/Message_model.php b/application/models/system/Message_model.php index 741c96ade..33e3d9649 100644 --- a/application/models/system/Message_model.php +++ b/application/models/system/Message_model.php @@ -276,8 +276,8 @@ class Message_model extends DB_Model m.body AS body, m.insertamum AS insertamum, m.relationmessage_id AS relationmessage_id, - (SELECT COALESCE(titelpre,'') || ' ' || COALESCE(vorname,'') || ' ' || COALESCE(nachname,'') || ' ' || COALESCE(titelpost,'') FROM public.tbl_person WHERE person_id = fm.sender_id) as sender, - (SELECT COALESCE(titelpre,'') || ' ' || COALESCE(vorname,'') || ' ' || COALESCE(nachname,'') || ' ' || COALESCE(titelpost,'') FROM public.tbl_person WHERE person_id = fm.recipient_id) as recipient, + (COALESCE(ps.titelpre,'') || ' ' || COALESCE(ps.vorname,'') || ' ' || COALESCE(ps.nachname,'') || ' ' || COALESCE(ps.titelpost,'')) as sender, + (COALESCE(pr.titelpre,'') || ' ' || COALESCE(pr.vorname,'') || ' ' || COALESCE(pr.nachname,'') || ' ' || COALESCE(pr.titelpost,'')) as recipient, fm.sender_id, fm.recipient_id, ms.status, @@ -288,6 +288,10 @@ class Message_model extends DB_Model public.tbl_msg_message m on fm.message_id = m.message_id join lastmsgstatus ms on fm.message_id = ms.message_id and fm.recipient_id = ms.person_id + left join + public.tbl_person ps on ps.person_id = fm.sender_id + left join + public.tbl_person pr on pr.person_id = fm.recipient_id order by m.insertamum DESC limit ? From 0496eb7cc947c874281dad9b28df1db758734b70 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Mon, 16 Feb 2026 15:56:40 +0100 Subject: [PATCH 10/11] use union instead of or to avoid parallel seq scan --- application/models/system/Message_model.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/application/models/system/Message_model.php b/application/models/system/Message_model.php index 33e3d9649..3a5579cc7 100644 --- a/application/models/system/Message_model.php +++ b/application/models/system/Message_model.php @@ -251,9 +251,23 @@ class Message_model extends DB_Model join public.tbl_msg_recipient mr on mr.message_id = m.message_id where - m.person_id = ? or mr.person_id = ? + m.person_id = ? group by m.message_id, m.person_id, mr.person_id + + union + + select + m.message_id, m.person_id as sender_id, mr.person_id as recipient_id + from + public.tbl_msg_message m + join + public.tbl_msg_recipient mr on mr.message_id = m.message_id + where + mr.person_id = ? + group by + m.message_id, m.person_id, mr.person_id + ), lastmsgstatus as ( select ms.* From e12b7e1ed55baf978d9911301e2561dc2769f1e1 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Tue, 17 Feb 2026 08:06:30 +0100 Subject: [PATCH 11/11] add indexes for person_id to table msg_message and msg_recipient, ensure tabulator data request is made before requests of create msg components --- application/models/system/Message_model.php | 2 +- .../Messages/Details/TableMessages.js | 1 + public/js/components/Messages/Messages.js | 10 +++++-- system/dbupdate_3.4.php | 1 + .../71645_studvw_messagetab_ladezeit.php | 28 +++++++++++++++++++ 5 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 system/dbupdate_3.4/71645_studvw_messagetab_ladezeit.php diff --git a/application/models/system/Message_model.php b/application/models/system/Message_model.php index 3a5579cc7..19129b606 100644 --- a/application/models/system/Message_model.php +++ b/application/models/system/Message_model.php @@ -255,7 +255,7 @@ class Message_model extends DB_Model group by m.message_id, m.person_id, mr.person_id - union + union all select m.message_id, m.person_id as sender_id, mr.person_id as recipient_id diff --git a/public/js/components/Messages/Details/TableMessages.js b/public/js/components/Messages/Details/TableMessages.js index a55ddec63..6a4cf5ca0 100644 --- a/public/js/components/Messages/Details/TableMessages.js +++ b/public/js/components/Messages/Details/TableMessages.js @@ -243,6 +243,7 @@ export default { title: this.$p.t('global', 'aktionen') }); */ + this.$emit('tabulator_tablebuilt'); } }, { diff --git a/public/js/components/Messages/Messages.js b/public/js/components/Messages/Messages.js index 1f9afcb9e..5e247ddb5 100644 --- a/public/js/components/Messages/Messages.js +++ b/public/js/components/Messages/Messages.js @@ -56,6 +56,7 @@ export default { }, data() { return { + tablebuilt: false, isVisibleDiv: false, messageId: null } @@ -139,8 +140,10 @@ export default { }, resetMessageId(){ this.messageId = null; + }, + tableBuilt: function() { + this.tablebuilt = true; } - }, template: `
@@ -155,6 +158,7 @@ export default { -
+
diff --git a/system/dbupdate_3.4.php b/system/dbupdate_3.4.php index 793930243..4ddb38203 100644 --- a/system/dbupdate_3.4.php +++ b/system/dbupdate_3.4.php @@ -91,6 +91,7 @@ require_once('dbupdate_3.4/69065_Projektarbeiten_Firmen_verwalten.php'); require_once('dbupdate_3.4/68744_StV_settings.php'); require_once('dbupdate_3.4/62889_reihungstest_ueberwachung_mit_constructor.php'); require_once('dbupdate_3.4/71399_dashboard_update_widget_paths.php'); +require_once('dbupdate_3.4/71645_studvw_messagetab_ladezeit.php'); // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen echo '

Pruefe Tabellen und Attribute!

'; diff --git a/system/dbupdate_3.4/71645_studvw_messagetab_ladezeit.php b/system/dbupdate_3.4/71645_studvw_messagetab_ladezeit.php new file mode 100644 index 000000000..4ad88fba9 --- /dev/null +++ b/system/dbupdate_3.4/71645_studvw_messagetab_ladezeit.php @@ -0,0 +1,28 @@ +db_query("SELECT * FROM pg_class WHERE relname='idx_tbl_msg_message_person_id'")) +{ + if ($db->db_num_rows($result) == 0) + { + $qry = "CREATE INDEX idx_tbl_msg_message_person_id ON public.tbl_msg_message USING btree (person_id)"; + + if (! $db->db_query($qry)) + echo 'idx_tbl_msg_message_person_id: ' . $db->db_last_error() . '
'; + else + echo 'Index idx_tbl_msg_message_person_id angelegt
'; + } +} + +if ($result = $db->db_query("SELECT * FROM pg_class WHERE relname='idx_tbl_msg_recipient_person_id'")) +{ + if ($db->db_num_rows($result) == 0) + { + $qry = "CREATE INDEX idx_tbl_msg_recipient_person_id ON public.tbl_msg_recipient USING btree (person_id)"; + + if (! $db->db_query($qry)) + echo 'idx_tbl_msg_recipient_person_id: ' . $db->db_last_error() . '
'; + else + echo 'Index idx_tbl_msg_recipient_person_id angelegt
'; + } +}