diff --git a/application/config/abgabe.php b/application/config/abgabe.php
index 90aedbd8b..3427046cd 100644
--- a/application/config/abgabe.php
+++ b/application/config/abgabe.php
@@ -43,3 +43,11 @@ $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;
+
+
diff --git a/application/controllers/api/frontend/v1/Abgabe.php b/application/controllers/api/frontend/v1/Abgabe.php
index d6f3c29a8..5ba0ef172 100644
--- a/application/controllers/api/frontend/v1/Abgabe.php
+++ b/application/controllers/api/frontend/v1/Abgabe.php
@@ -93,6 +93,7 @@ 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,
@@ -100,7 +101,7 @@ class Abgabe extends FHCAPI_Controller
'abgabetypenBetreuer' => $abgabetypenBetreuer,
'ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT' => $ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT,
'ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER' => $ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER,
- 'BETREUER_SAMMELMAIL_BUTTON_STUDENT' => $BETREUER_SAMMELMAIL_BUTTON_STUDENT,
+ 'MULTIEDIT_TABLE' => $MULTIEDIT_TABLE,
);
$this->terminateWithSuccess($ret);
@@ -110,10 +111,14 @@ 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');
+ $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');
$ret = array(
'moodle_link' => $moodle_link,
+ 'title_edit_allowed' => $title_edit_allowed,
+ 'confetti_on_endupload' => $confetti_on_endupload
);
$this->terminateWithSuccess($ret);
@@ -190,8 +195,8 @@ class Abgabe extends FHCAPI_Controller
} else {
$result = $this->ProjektarbeitModel->getStudentProjektarbeitenWithBetreuer(getAuthUID());
}
-
- $projektarbeiten = getData($result);
+
+ $projektarbeiten = hasData($result) ? getData($result) : array();
if(count($projektarbeiten)) {
foreach($projektarbeiten as $pa) {
@@ -459,6 +464,10 @@ class Abgabe extends FHCAPI_Controller
*/
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');
@@ -467,6 +476,13 @@ class Abgabe extends FHCAPI_Controller
$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');
+ }
+
+
$this->checkProjektarbeitForFinishedStatus($projektarbeit_id);
$this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel');
@@ -484,6 +500,8 @@ class Abgabe extends FHCAPI_Controller
$this->terminateWithError($this->p->t('abgabetool', 'c4noZuordnungBetreuerStudent'), 'general');
}
+
+
$result = $this->ProjektarbeitModel->load($projektarbeit_id);
$data = getData($result);
@@ -492,7 +510,7 @@ class Abgabe extends FHCAPI_Controller
$result = $this->ProjektarbeitModel->update(
$projektarbeit_id,
array(
- 'titel' => trim($titel),
+ 'titel' => $titel,
'updatevon' => getAuthUID(),
'updateamum' => date('Y-m-d H:i:s')
)
@@ -504,7 +522,7 @@ class Abgabe extends FHCAPI_Controller
'titelUpdate',
array(
'projektarbeit_id' => $projektarbeit_id,
- 'titel' => trim($titel),
+ 'titel' => $titel,
'updatevon' => getAuthUID(),
'updateamum' => date('Y-m-d H:i:s')
),
@@ -514,7 +532,7 @@ class Abgabe extends FHCAPI_Controller
$this->sendTitelChangedEmail(
$projektarbeit_id,
- trim($titel),
+ $titel,
$oldTitle,
$assignedStudentUid
);
@@ -1533,7 +1551,7 @@ class Abgabe extends FHCAPI_Controller
};
Events::trigger('projektarbeit_is_current', $projektarbeit_id, $returnFunc);
if(!$projektarbeitIsCurrent) {
- $this->terminateWithError($this->p->t('abgabetool','c4fehlerAktualitaetProjektarbeit'), 'general');
+ $this->terminateWithError($this->p->t('abgabetool','c4fehlerAktualitaetProjektarbeitv2'), 'general');
}
// Link to Abgabetool
@@ -1739,7 +1757,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','c4fehlerAktualitaetProjektarbeit');
+ $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 {
diff --git a/application/views/Cis/Abgabetool.php b/application/views/Cis/Abgabetool.php
index a0621b1f9..469bc8110 100644
--- a/application/views/Cis/Abgabetool.php
+++ b/application/views/Cis/Abgabetool.php
@@ -38,7 +38,7 @@ $includesArray = array(
$this->load->view('templates/FHC-Header', $includesArray);
?>
-
+
uid=
student_uid_prop=""
stg_kz_prop=""
diff --git a/public/css/components/abgabetool/abgabe.css b/public/css/components/abgabetool/abgabe.css
index 12e0e82ee..78a04e114 100644
--- a/public/css/components/abgabetool/abgabe.css
+++ b/public/css/components/abgabetool/abgabe.css
@@ -347,3 +347,52 @@
}
+/*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;
+ }
+}
diff --git a/public/js/components/Cis/Abgabetool/AbgabeStudentDetail.js b/public/js/components/Cis/Abgabetool/AbgabeStudentDetail.js
index c9d5280cb..01673539f 100644
--- a/public/js/components/Cis/Abgabetool/AbgabeStudentDetail.js
+++ b/public/js/components/Cis/Abgabetool/AbgabeStudentDetail.js
@@ -21,7 +21,7 @@ export const AbgabeStudentDetail = {
VueDatePicker,
FhcOverlay
},
- inject: ['notenOptions', 'isMobile', 'isViewMode', 'moodle_link'],
+ inject: ['notenOptions', 'isMobile', 'isViewMode', 'moodle_link', 'confetti_on_endupload', 'title_edit_allowed'],
props: {
projektarbeit: {
type: Object,
@@ -52,6 +52,67 @@ 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();
@@ -155,6 +216,9 @@ 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
})
@@ -274,8 +338,7 @@ export const AbgabeStudentDetail = {
return qgatefound
},
isTitelEditAllowed() {
- // blocked once the projektarbeit has a note (finished) - mirrors backend guard
- return !this.isViewMode && !this.projektarbeit?.note;
+ return this.title_edit_allowed && !this.isViewMode && !this.projektarbeit?.note;
},
getTooltipVerspaetet() {
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipVerspaetet')), class: "custom-tooltip" }
@@ -433,7 +496,7 @@ export const AbgabeStudentDetail = {
-
+
{{$capitalize( $p.t('abgabetool/c4abgabetyp') )}}
@@ -544,6 +607,8 @@ export const AbgabeStudentDetail = {
+
+
{{ editingTitel.length }} / 1024
@@ -674,6 +739,7 @@ export const AbgabeStudentDetail = {
{{ $p.t('abgabetool/c4zusatzdatenausfuellen') }}
{{$capitalize( $p.t('ui/hochladen') )}}
+
`,
};
diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js
index fee321299..b962fc5ad 100644
--- a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js
+++ b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js
@@ -95,6 +95,7 @@ export const AbgabetoolAssistenz = {
old_abgabe_beurteilung_link: null,
ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT: null,
ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER: null,
+ MULTIEDIT_TABLE: false,
saving: false,
loading: false,
abgabeTypeOptions: null,
@@ -2275,6 +2276,7 @@ export const AbgabetoolAssistenz = {
this.old_abgabe_beurteilung_link = res.data?.old_abgabe_beurteilung_link;
this.ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT = res.data?.ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT;
this.ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER = res.data?.ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER;
+ this.MULTIEDIT_TABLE = res.data?.MULTIEDIT_TABLE;
}
// 2. Studiengänge
@@ -2659,7 +2661,7 @@ export const AbgabetoolAssistenz = {
{{$p.t('abgabetool/abgabetoolTitleAdmin')}}
-
+
{{$capitalize($p.t('lehre/studiengang'))}}:
@@ -2676,10 +2678,10 @@ export const AbgabetoolAssistenz = {
-
+
{{$capitalize($p.t('lehre/note'))}}:
-
+
-
+
{{ $p.t('abgabetool/c4terminansicht') }}
diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolStudent.js b/public/js/components/Cis/Abgabetool/AbgabetoolStudent.js
index c855b6283..9ffcc1f94 100644
--- a/public/js/components/Cis/Abgabetool/AbgabetoolStudent.js
+++ b/public/js/components/Cis/Abgabetool/AbgabetoolStudent.js
@@ -18,7 +18,9 @@ export const AbgabetoolStudent = {
return {
notenOptions: Vue.computed(() => this.notenOptions),
isViewMode: Vue.computed(() => this.isViewMode),
- moodle_link: Vue.computed(() => this.moodle_link)
+ 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)
}
},
props: {
@@ -46,6 +48,8 @@ export const AbgabetoolStudent = {
projektarbeiten: null,
selectedProjektarbeit: null,
moodle_link: null,
+ title_edit_allowed: null,
+ confetti_on_endupload: null,
editingTitel: '',
editingProjektarbeit: null,
};
@@ -335,6 +339,8 @@ export const AbgabetoolStudent = {
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
}).catch(e => {
this.loading = false
})
@@ -381,7 +387,7 @@ export const AbgabetoolStudent = {
rows="10"
maxlength="1024"
class="form-control w-100"
- @keyup.enter="saveTitel"
+ @keydown.enter.prevent="saveTitel"
/>
{{ editingTitel.length }} / 1024
@@ -407,7 +413,7 @@ export const AbgabetoolStudent = {
{{$capitalize( $p.t('abgabetool/abgabetoolTitle') )}}
-
+
{{$capitalize( $p.t('abgabetool/c4abgabeStudentNoProjectsFound') )}}
@@ -497,7 +503,7 @@ export const AbgabetoolStudent = {
style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"
>{{ projektarbeit.titel }}
12 chars, "mailto:" -> 7 chars
const baseOverhead = useBcc ? 12 : 7;
- let queryString = urlParams.toString();
+ let queryString = urlParams.toString().replace(/\+/g, '%20');;
let overhead = baseOverhead + (queryString ? 1 + queryString.length : 0); // +1 accounts for '?' or '&'
// calculate overhead with body to exceed the limit
if (overhead > 2024) {
await alertPluginRef.alertWarning(phrasenPluginRef.t('ui', 'bodyZuLang'));
- urlParams.delete('body');
+ urlParams.delete('body').replace(/\+/g, '%20');;
queryString = urlParams.toString();
overhead = baseOverhead + (queryString ? 1 + queryString.length : 0);
}
diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php
index 8f2cdc9ac..5c6a633c8 100644
--- a/system/phrasesupdate.php
+++ b/system/phrasesupdate.php
@@ -34342,7 +34342,7 @@ array(
'sprache' => 'German',
'text' => 'Sie können eine Entschuldigung für einen Zeitraum von bis zu {0} Tagen in die Vergangenheit und bis zum Ende des aktuellen Semesters hochladen. Die erlaubten Dateitypen für das Dokument sind .pdf, .png und .jpg.
- Sobald Sie eine Entschuldigung hochladen wird die zugehörige Studiengangsassistenz informiert und Ihr Anliegen überprüft. Solange Ihre Entschuldigung noch keinen akzeptierten oder abgelehnten Status erhalten hat, steht es Ihnen frei diese inklusive Datei zu löschen. Sobald sie entweder akzeptiert oder abgelehnt wurde können Sie den Eintrag nichtmehr löschen.
+ Sobald Sie eine Entschuldigung hochladen wird die zugehörige Studiengangsassistenz informiert und Ihr Anliegen überprüft. Solange Ihre Entschuldigung noch keinen akzeptierten oder abgelehnten Status erhalten hat, steht es Ihnen frei diese inklusive Datei zu löschen. Sobald sie entweder akzeptiert oder abgelehnt wurde können Sie den Eintrag nicht mehr löschen.
Bei einer akzeptierten Entschuldigung werden sämtliche digitalen Anwesenheiten in diesem Zeitraum als positiv gewertet. Eine abgelehnte Entschuldigung hat keine Auswirkungen auf ihre Anwesenheitsquote.',
'description' => '',
@@ -41761,12 +41761,12 @@ array(
array(
'app' => 'core',
'category' => 'abgabetool',
- 'phrase' => 'c4fehlerAktualitaetProjektarbeit',
+ 'phrase' => 'c4fehlerAktualitaetProjektarbeitv2',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
- 'text' => "Projektarbeit ist nichtmehr aktuell",
+ 'text' => "Projektarbeit ist nicht mehr aktuell",
'description' => '',
'insertvon' => 'system'
),
@@ -47221,6 +47221,46 @@ array(
)
)
),
+ array(
+ 'app' => 'core',
+ 'category' => 'abgabetool',
+ 'phrase' => 'c4PATitleChanged',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Titel einer Projektarbeit wurde geändert.',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Title of a project work has been changed',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'abgabetool',
+ 'phrase' => 'c4studentEditNotAllowed',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Editieren des Titels ist für Studierende nicht erlaubt.',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Editing of thesis title is not allowed for students.',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
array(
'app' => 'core',
'category' => 'ui',