added paabgabetyp columns "aktiv", "upload_allowed", "aktiv"; setting sensible default values for existing typen that are just the developers best guess really; accordion header with icons & tooltips; logLib in Abgabe API controller logging all successful delete/insert/update requests; show arbitrary '23:59' string after target date so it is clear until when the upload should be fulfilled, even though we still dont do anything technically different; new Termine can only be made with aktiv paabgabe typen; note & benotungsnotiz now tied to paabgabetyp benotbar flag instead of hardcoded for qgate1 & 2; added "noch nicht abgegeben" text in case the abgabedatum is null; modal now spawns in xl with fullscreen optionally enabled;

This commit is contained in:
Johann Hoffmann
2025-10-14 16:45:28 +02:00
parent 14aad56d5e
commit 0d2e41cf2f
8 changed files with 478 additions and 75 deletions
@@ -43,6 +43,20 @@ class Abgabe extends FHCAPI_Controller
$this->load->library('PhrasesLib'); $this->load->library('PhrasesLib');
// Loads LogLib with different debug trace levels to get data of the job that extends this class
// It also specify parameters to set database fields
$this->load->library('LogLib', array(
'classIndex' => 5,
'functionIndex' => 5,
'lineIndex' => 4,
'dbLogType' => 'API', // required
'dbExecuteUser' => 'RESTful API',
'requestId' => 'API',
'requestDataFormatter' => function ($data) {
return json_encode($data);
}
), 'logLib');
$this->loadPhrases( $this->loadPhrases(
array( array(
'global', 'global',
@@ -150,7 +164,6 @@ class Abgabe extends FHCAPI_Controller
} }
} }
$this->terminateWithSuccess(array($projektarbeiten, DOMAIN, $uid)); $this->terminateWithSuccess(array($projektarbeiten, DOMAIN, $uid));
} }
@@ -161,7 +174,6 @@ class Abgabe extends FHCAPI_Controller
*/ */
public function postStudentProjektarbeitZwischenabgabe() public function postStudentProjektarbeitZwischenabgabe()
{ {
$projektarbeit_id = $_POST['projektarbeit_id']; $projektarbeit_id = $_POST['projektarbeit_id'];
$paabgabe_id = $_POST['paabgabe_id']; $paabgabe_id = $_POST['paabgabe_id'];
$student_uid = $_POST['student_uid']; $student_uid = $_POST['student_uid'];
@@ -189,6 +201,13 @@ class Abgabe extends FHCAPI_Controller
)); ));
$this->sendUploadEmail($bperson_id, $projektarbeit_id, $paabgabetyp_kurzbz, $student_uid); $this->sendUploadEmail($bperson_id, $projektarbeit_id, $paabgabetyp_kurzbz, $student_uid);
$this->logLib->logInfoDB(array('zwischenupload',$res, array(
'abgabedatum' => date('Y-m-d'),
'updatevon' => getAuthUID(),
'updateamum' => date('Y-m-d H:i:s')
), getAuthUID(), getAuthPersonId(), $student_uid));
$this->terminateWithSuccess($res); $this->terminateWithSuccess($res);
} else { } else {
$this->terminateWithError('Error moving File'); $this->terminateWithError('Error moving File');
@@ -197,7 +216,6 @@ class Abgabe extends FHCAPI_Controller
} else { } else {
$this->terminateWithError('File missing'); $this->terminateWithError('File missing');
} }
} }
/** /**
@@ -275,6 +293,13 @@ class Abgabe extends FHCAPI_Controller
$this->sendUploadEmail($bperson_id, $projektarbeit_id, $paabgabetyp_kurzbz, $student_uid); $this->sendUploadEmail($bperson_id, $projektarbeit_id, $paabgabetyp_kurzbz, $student_uid);
$this->logLib->logInfoDB(array('endupload',$res, array(
'abgabedatum' => date('Y-m-d'),
'updatevon' => getAuthUID(),
'updateamum' => date('Y-m-d H:i:s')
), getAuthUID(), getAuthPersonId(), array($projektarbeit_id,$sprache,$abstract,$abstract_en
,$schlagwoerter, $schlagwoerter_en, $seitenanzahl)));
$this->terminateWithSuccess($res); $this->terminateWithSuccess($res);
} else { } else {
$this->terminateWithError('Error moving File'); $this->terminateWithError('Error moving File');
@@ -531,6 +556,8 @@ class Abgabe extends FHCAPI_Controller
'insertamum' => date('Y-m-d H:i:s') 'insertamum' => date('Y-m-d H:i:s')
) )
); );
$this->logLib->logInfoDB(array('paabgabe created',$result, getAuthUID(), getAuthPersonId()));
} else { } else {
// load existing entry of paabgabe and check if note has changed to negativ, to avoid sending when // load existing entry of paabgabe and check if note has changed to negativ, to avoid sending when
// only notiz has changed. // only notiz has changed.
@@ -554,6 +581,17 @@ class Abgabe extends FHCAPI_Controller
'updateamum' => date('Y-m-d H:i:s') 'updateamum' => date('Y-m-d H:i:s')
) )
); );
$this->logLib->logInfoDB(array('paabgabe updated',$result, array(
'paabgabetyp_kurzbz' => $paabgabetyp_kurzbz,
'datum' => $datum,
'kurzbz' => $kurzbz,
'note' => $note,
'beurteilungsnotiz' => $beurteilungsnotiz,
'upload_allowed' => $upload_allowed,
'updatevon' => getAuthUID(),
'updateamum' => date('Y-m-d H:i:s')
), getAuthUID(), getAuthPersonId()));
} }
// check if $paaabgabe is a qual gate and its note is deemed negative // check if $paaabgabe is a qual gate and its note is deemed negative
@@ -583,12 +621,10 @@ class Abgabe extends FHCAPI_Controller
} else { // benotung legitimately changed -> email } else { // benotung legitimately changed -> email
$this->sendQualGateNegativEmail($projektarbeit_id, $betreuer_person_id, $paabgabe); $this->sendQualGateNegativEmail($projektarbeit_id, $betreuer_person_id, $paabgabe);
} }
} else { // nothing existing previously -> send that mail } else { // nothing existing previously -> send that mail
$this->sendQualGateNegativEmail($projektarbeit_id, $betreuer_person_id, $paabgabe); $this->sendQualGateNegativEmail($projektarbeit_id, $betreuer_person_id, $paabgabe);
} }
} }
} }
@@ -603,15 +639,17 @@ class Abgabe extends FHCAPI_Controller
$this->load->model('education/Paabgabe_model', 'PaabgabeModel'); $this->load->model('education/Paabgabe_model', 'PaabgabeModel');
$result = $this->PaabgabeModel->load($paabgabe_id); $paabgabeResult = $this->PaabgabeModel->load($paabgabe_id);
$result = $this->getDataOrTerminateWithError($result); $paabgabeArr = $this->getDataOrTerminateWithError($paabgabeResult);
if(count($result) == 0) if(count($paabgabeArr) == 0)
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general'); $this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
if($result[0]->insertvon === getAuthUID()) { if($paabgabeArr[0]->insertvon === getAuthUID()) {
$result = $this->PaabgabeModel->delete($paabgabe_id); $result = $this->PaabgabeModel->delete($paabgabe_id);
$result = $this->getDataOrTerminateWithError($result); $result = $this->getDataOrTerminateWithError($result);
$this->logLib->logInfoDB(array($paabgabeArr[0], getAuthUID(), getAuthPersonId()));
$this->terminateWithSuccess($result); $this->terminateWithSuccess($result);
} }
@@ -705,6 +743,8 @@ class Abgabe extends FHCAPI_Controller
); );
} }
$this->logLib->logInfoDB(array('serientermin angelegt',$res, getAuthUID(), getAuthPersonId()));
$this->terminateWithSuccess($res); $this->terminateWithSuccess($res);
} }
@@ -725,7 +765,7 @@ class Abgabe extends FHCAPI_Controller
$this->load->model('education/Paabgabe_model', 'PaabgabeModel'); $this->load->model('education/Paabgabe_model', 'PaabgabeModel');
$result = $this->PaabgabeModel->getDeadlines($person_id); $result = $this->PaabgabeModel->getDeadlines($person_id);
$data = $this->getDataOrTerminateWithError($result); $data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data); $this->terminateWithSuccess($data);
} }
+9 -9
View File
@@ -3,7 +3,7 @@
background-color: var(--fhc-red-70); background-color: var(--fhc-red-70);
font-weight: 600; font-weight: 600;
border-radius: 6px; border-radius: 6px;
padding: 0px 0px 0px 32px; padding: 0px 0px 0px 34px;
transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease; transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease;
box-shadow: 0 1px 2px rgba(0,0,0,0.08); box-shadow: 0 1px 2px rgba(0,0,0,0.08);
} }
@@ -31,7 +31,7 @@
background-color: var(--fhc-yellow-70); background-color: var(--fhc-yellow-70);
font-weight: 600; font-weight: 600;
border-radius: 6px; border-radius: 6px;
padding: 0px 0px 0px 32px; padding: 0px 0px 0px 34px;
transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease; transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease;
box-shadow: 0 1px 2px rgba(0,0,0,0.08); box-shadow: 0 1px 2px rgba(0,0,0,0.08);
} }
@@ -56,29 +56,29 @@
/* Base Header */ /* Base Header */
.verspaetet-header { .verspaetet-header {
background-color: var(--fhc-pink-70); background-color: var(--fhc-pink-40);
font-weight: 600; font-weight: 600;
border-radius: 6px; border-radius: 6px;
padding: 0px 0px 0px 32px; padding: 0px 0px 0px 34px;
transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease; transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease;
box-shadow: 0 1px 2px rgba(0,0,0,0.08); box-shadow: 0 1px 2px rgba(0,0,0,0.08);
} }
/* Hover State */ /* Hover State */
.verspaetet-header:hover { .verspaetet-header:hover {
background-color: var(--fhc-pink-60); background-color: var(--fhc-pink-20);
box-shadow: 0 2px 6px rgba(0,0,0,0.12); box-shadow: 0 2px 6px rgba(0,0,0,0.12);
} }
/* Active / Expanded State */ /* Active / Expanded State */
.p-accordion-tab-active > .verspaetet-header { .p-accordion-tab-active > .verspaetet-header {
background-color: var(--fhc-pink-50); background-color: var(--fhc-pink-30);
box-shadow: 0 2px 8px rgba(0,0,0,0.2); box-shadow: 0 2px 8px rgba(0,0,0,0.2);
} }
/* Hover State Active*/ /* Hover State Active*/
.p-accordion-tab-active > .verspaetet-header:hover { .p-accordion-tab-active > .verspaetet-header:hover {
background-color: var(--fhc-pink-60); background-color: var(--fhc-pink-20);
box-shadow: 0 2px 6px rgba(0,0,0,0.12); box-shadow: 0 2px 6px rgba(0,0,0,0.12);
} }
@@ -87,7 +87,7 @@
background-color: var(--fhc-green-70); background-color: var(--fhc-green-70);
font-weight: 600; font-weight: 600;
border-radius: 6px; border-radius: 6px;
padding: 0px 0px 0px 32px; padding: 0px 0px 0px 34px;
transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease; transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease;
box-shadow: 0 1px 2px rgba(0,0,0,0.08); box-shadow: 0 1px 2px rgba(0,0,0,0.08);
} }
@@ -115,7 +115,7 @@
background-color: var(--fhc-white-70); background-color: var(--fhc-white-70);
font-weight: 600; font-weight: 600;
border-radius: 6px; border-radius: 6px;
padding: 0px 0px 0px 32px; padding: 0px 0px 0px 34px;
transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease; transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease;
box-shadow: 0 1px 2px rgba(0,0,0,0.08); box-shadow: 0 1px 2px rgba(0,0,0,0.08);
} }
@@ -16,7 +16,13 @@ export const AbgabeMitarbeiterDetail = {
AccordionTab: primevue.accordiontab, AccordionTab: primevue.accordiontab,
VueDatePicker VueDatePicker
}, },
inject: ['abgabeTypeOptions', 'allowedNotenOptions', 'turnitin_link', 'old_abgabe_beurteilung_link', 'isMobile'], inject: [
'abgabeTypeOptions',
'allowedNotenOptions',
'turnitin_link',
'old_abgabe_beurteilung_link',
'isMobile'
],
props: { props: {
projektarbeit: { projektarbeit: {
type: Object, type: Object,
@@ -164,23 +170,23 @@ export const AbgabeMitarbeiterDetail = {
const oneDayMs = 1000 * 60 * 60 * 24 const oneDayMs = 1000 * 60 * 60 * 24
return Math.round((new Date(datum) - new Date(today)) / oneDayMs) return Math.round((new Date(datum) - new Date(today)) / oneDayMs)
}, },
getDateStyle(termin, mode) { getDateStyleClass(termin, mode) {
const datum = new Date(termin.datum) const datum = new Date(termin.datum)
const abgabedatum = new Date(termin.abgabedatum) const abgabedatum = new Date(termin.abgabedatum)
// https://wiki.fhcomplete.info/doku.php?id=cis:abgabetool_fuer_studierende // https://wiki.fhcomplete.info/doku.php?id=cis:abgabetool_fuer_studierende
if (termin.abgabedatum === null) { if (termin.abgabedatum === null) {
if(datum < today) { if(datum < today) {
return 'verpasst-header' return 'verpasst'
} else if (datum > today && this.dateDiffInDays(datum, today) <= 12) { } else if (datum > today && this.dateDiffInDays(datum, today) <= 12) {
return 'abzugeben-header' return 'abzugeben'
} else { } else {
return 'standard-header' return 'standard'
} }
} else if(abgabedatum > datum) { } else if(abgabedatum > datum) {
return 'verspaetet-header' return 'verspaetet'
} else { } else {
return 'abgegeben-header' return 'abgegeben'
} }
}, },
openBeurteilungLink(link) { openBeurteilungLink(link) {
@@ -209,7 +215,7 @@ export const AbgabeMitarbeiterDetail = {
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root + 'index.ci.php/extensions/FHC-Core-Projektarbeitsbeurteilung/' + path const link = FHC_JS_DATA_STORAGE_OBJECT.app_root + 'index.ci.php/extensions/FHC-Core-Projektarbeitsbeurteilung/' + path
window.open(link, '_blank') window.open(link, '_blank')
}, },
formatDate(dateParam) { formatDate(dateParam, showTime = true) {
const date = new Date(dateParam) const date = new Date(dateParam)
// handle missing leading 0 // handle missing leading 0
const padZero = (num) => String(num).padStart(2, '0'); const padZero = (num) => String(num).padStart(2, '0');
@@ -217,19 +223,21 @@ export const AbgabeMitarbeiterDetail = {
const month = padZero(date.getMonth() + 1); // Months are zero-based const month = padZero(date.getMonth() + 1); // Months are zero-based
const day = padZero(date.getDate()); const day = padZero(date.getDate());
const year = date.getFullYear(); const year = date.getFullYear();
return `${day}.${month}.${year}`; // abgabedatum should SHOW abgabezeit which should always be last minute of the day
return `${day}.${month}.${year}` + (showTime ? ' 23:59' : '');
}, },
getAccTabHeaderForTermin(termin) { getAccTabHeaderForTermin(termin) {
let tabTitle = '' let tabTitle = ''
const datumFormatted = this.formatDate(termin.datum) const datumFormatted = this.formatDate(termin.datum, false)
tabTitle += termin.bezeichnung?.bezeichnung + ' ' + datumFormatted tabTitle += termin.bezeichnung?.bezeichnung + ' ' + datumFormatted
return tabTitle return tabTitle
}, },
openCreateNewAbgabeModal() { openCreateNewAbgabeModal() {
if(!this.newTermin) { if(!this.newTermin) {
const typ = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === 'zwischen')
this.newTermin = { this.newTermin = {
'paabgabe_id': -1, 'paabgabe_id': -1,
'projektarbeit_id': this.projektarbeit.projektarbeit_id, 'projektarbeit_id': this.projektarbeit.projektarbeit_id,
@@ -238,9 +246,9 @@ export const AbgabeMitarbeiterDetail = {
'datum': new Date().toISOString().split('T')[0], 'datum': new Date().toISOString().split('T')[0],
'note': this.allowedNotenOptions.find(opt => opt.note == 9), 'note': this.allowedNotenOptions.find(opt => opt.note == 9),
'beurteilungsnotiz': '', 'beurteilungsnotiz': '',
'upload_allowed': false, 'upload_allowed': typ.upload_allowed_default,
'paabgabetyp_kurzbz': '', 'paabgabetyp_kurzbz': '',
'bezeichnung': this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === 'zwischen'), 'bezeichnung': typ,
'abgabedatum': null, 'abgabedatum': null,
'insertvon': this.viewData?.uid ?? '' 'insertvon': this.viewData?.uid ?? ''
} }
@@ -290,6 +298,9 @@ export const AbgabeMitarbeiterDetail = {
}, },
computed: { computed: {
activeAbgabeTypeOptions() {
return this.abgabeTypeOptions?.filter(opt => opt.aktiv === true)
},
getEid() { getEid() {
return this.$p.t('abgabetool/c4eidesstattlicheErklaerung') return this.$p.t('abgabetool/c4eidesstattlicheErklaerung')
}, },
@@ -317,6 +328,36 @@ export const AbgabeMitarbeiterDetail = {
}, },
getSpeedDialStyle() { getSpeedDialStyle() {
return 'position: static !important;' return 'position: static !important;'
},
getTooltipVerspaetet() {
return {
value: this.$p.t('abgabetool/c4tooltipVerspaetet'),
class: "custom-tooltip"
}
},
getTooltipVerpasst() {
return {
value: this.$p.t('abgabetool/c4tooltipVerpasst'),
class: "custom-tooltip"
}
},
getTooltipAbzugeben() {
return {
value: this.$p.t('abgabetool/c4tooltipAbzugeben'),
class: "custom-tooltip"
}
},
getTooltipStandard() {
return {
value: this.$p.t('abgabetool/c4tooltipStandard'),
class: "custom-tooltip"
}
},
getTooltipAbgegeben() {
return {
value: this.$p.t('abgabetool/c4tooltipAbgegeben'),
class: "custom-tooltip"
}
} }
}, },
watch: { watch: {
@@ -326,6 +367,8 @@ export const AbgabeMitarbeiterDetail = {
if(newVal?.paabgabetyp_kurzbz === 'qualgate1' || newVal?.paabgabetyp_kurzbz === 'qualgate2') { if(newVal?.paabgabetyp_kurzbz === 'qualgate1' || newVal?.paabgabetyp_kurzbz === 'qualgate2') {
this.newTermin.kurzbz = newVal.bezeichnung this.newTermin.kurzbz = newVal.bezeichnung
} }
this.newTermin.upload_allowed = newVal.upload_allowed_default
} }
}, },
created() { created() {
@@ -339,7 +382,7 @@ export const AbgabeMitarbeiterDetail = {
id="innerModalNewAbgabe" id="innerModalNewAbgabe"
ref="modalContainerCreateNewAbgabe" ref="modalContainerCreateNewAbgabe"
class="bootstrap-prompt" class="bootstrap-prompt"
dialogClass="bordered-modal modal-xl" dialogClass="bordered-modal modal-lg"
:backdrop="true" :backdrop="true"
@hideBsModal="console.log('hideBsModal'); showAutomagicModalPhrase=false;" @hideBsModal="console.log('hideBsModal'); showAutomagicModalPhrase=false;"
> >
@@ -383,13 +426,13 @@ export const AbgabeMitarbeiterDetail = {
<Dropdown <Dropdown
:style="{'width': '100%'}" :style="{'width': '100%'}"
v-model="newTermin.bezeichnung" v-model="newTermin.bezeichnung"
:options="abgabeTypeOptions" :options="activeAbgabeTypeOptions"
:optionLabel="getOptionLabelAbgabetyp" :optionLabel="getOptionLabelAbgabetyp"
scrollHeight="300px"> scrollHeight="300px">
</Dropdown> </Dropdown>
</div> </div>
</div> </div>
<div class="row mt-2" v-if="newTermin.bezeichnung?.paabgabetyp_kurzbz === 'qualgate1' || newTermin.bezeichnung?.paabgabetyp_kurzbz === 'qualgate2'"> <div class="row mt-2">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4upload_allowed')}}</div> <div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4upload_allowed')}}</div>
<div class="col-8 col-md-9"> <div class="col-8 col-md-9">
<Checkbox <Checkbox
@@ -403,7 +446,7 @@ export const AbgabeMitarbeiterDetail = {
<div class="row mt-2"> <div class="row mt-2">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4abgabekurzbz')}}</div> <div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4abgabekurzbz')}}</div>
<div class="col-8 col-md-9"> <div class="col-8 col-md-9">
<Textarea style="margin-bottom: 4px;" v-model="newTermin.kurzbz" rows="1" :cols=" isMobile ? 30 : 90"></Textarea> <Textarea style="margin-bottom: 4px;" v-model="newTermin.kurzbz" rows="1" :cols=" isMobile ? 30 : 60"></Textarea>
</div> </div>
</div> </div>
</div> </div>
@@ -439,8 +482,21 @@ export const AbgabeMitarbeiterDetail = {
<Accordion :multiple="true" :activeIndex="[0]"> <Accordion :multiple="true" :activeIndex="[0]">
<template v-for="termin in this.projektarbeit?.abgabetermine"> <template v-for="termin in this.projektarbeit?.abgabetermine">
<AccordionTab :header="getAccTabHeaderForTermin(termin)" :headerClass="getDateStyle(termin)"> <AccordionTab :headerClass="getDateStyleClass(termin) + '-header'">
<template #header>
<div class="d-flex row w-100">
<div class="col-auto" style="transform: translateX(-62px)">
<i v-if="getDateStyleClass(termin) == 'verspaetet'" v-tooltip.right="getTooltipVerspaetet" class="fa-solid fa-triangle-exclamation"></i>
<i v-else-if="getDateStyleClass(termin) == 'verpasst'" v-tooltip.right="getTooltipVerpasst" class="fa-solid fa-calendar-xmark"></i>
<i v-else-if="getDateStyleClass(termin) == 'abzugeben'" v-tooltip.right="getTooltipAbzugeben" class="fa-solid fa-hourglass-half"></i>
<i v-else-if="getDateStyleClass(termin) == 'standard'" v-tooltip.right="getTooltipStandard" class="fa-solid fa-clock"></i>
<i v-else-if="getDateStyleClass(termin) == 'abgegeben'" v-tooltip.right="getTooltipAbgegeben" class="fa-solid fa-check"></i>
</div>
<div class="col-6 text-start">
<span>{{getAccTabHeaderForTermin(termin)}}</span>
</div>
</div>
</template>
<div class="row"> <div class="row">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4fixterminv2')}}</div> <div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4fixterminv2')}}</div>
<div class="col-8 col-md-9"> <div class="col-8 col-md-9">
@@ -476,7 +532,7 @@ export const AbgabeMitarbeiterDetail = {
:disabled="!termin.allowedToSave" :disabled="!termin.allowedToSave"
v-model="termin.bezeichnung" v-model="termin.bezeichnung"
@change="handleChangeAbgabetyp(termin)" @change="handleChangeAbgabetyp(termin)"
:options="abgabeTypeOptions" :options="activeAbgabeTypeOptions"
:optionLabel="getOptionLabelAbgabetyp"> :optionLabel="getOptionLabelAbgabetyp">
</Dropdown> </Dropdown>
</div> </div>
@@ -492,7 +548,7 @@ export const AbgabeMitarbeiterDetail = {
</Checkbox> </Checkbox>
</div> </div>
</div> </div>
<div class="row mt-2" v-if="termin.bezeichnung?.paabgabetyp_kurzbz === 'qualgate1' || termin.bezeichnung?.paabgabetyp_kurzbz === 'qualgate2'"> <div class="row mt-2" v-if="termin.bezeichnung?.benotbar">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4note')}}</div> <div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4note')}}</div>
<div class="col-8 col-md-9"> <div class="col-8 col-md-9">
<Dropdown <Dropdown
@@ -503,7 +559,7 @@ export const AbgabeMitarbeiterDetail = {
</Dropdown> </Dropdown>
</div> </div>
</div> </div>
<div class="row mt-2" v-if="termin.bezeichnung?.paabgabetyp_kurzbz === 'qualgate1' || termin.bezeichnung?.paabgabetyp_kurzbz === 'qualgate2'"> <div class="row mt-2" v-if="termin.bezeichnung?.benotbar">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4notizQualGatev2')}}</div> <div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4notizQualGatev2')}}</div>
<div class="col-8 col-md-9"> <div class="col-8 col-md-9">
<Textarea style="margin-bottom: 4px;" v-model="termin.beurteilungsnotiz" rows="1" :cols=" isMobile ? 30 : 90" :disabled="!termin.allowedToSave"></Textarea> <Textarea style="margin-bottom: 4px;" v-model="termin.beurteilungsnotiz" rows="1" :cols=" isMobile ? 30 : 90" :disabled="!termin.allowedToSave"></Textarea>
@@ -519,10 +575,15 @@ export const AbgabeMitarbeiterDetail = {
<div class="row mt-2"> <div class="row mt-2">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4abgabedatum')}}</div> <div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4abgabedatum')}}</div>
<div class="col-8 col-md-9"> <div class="col-8 col-md-9">
{{ termin.abgabedatum?.split("-").reverse().join(".") }} <template v-if="termin?.abgabedatum">
<button v-if="termin?.abgabedatum" @click="downloadAbgabe(termin)" class="btn btn-primary"> {{ termin.abgabedatum?.split("-").reverse().join(".") }}
<a> {{$p.t('abgabetool/c4downloadAbgabe')}} <i class="fa fa-file-pdf" style="margin-left:4px; cursor: pointer;"></i></a> <button v-if="termin?.abgabedatum" @click="downloadAbgabe(termin)" class="btn btn-primary">
</button> <a> {{$p.t('abgabetool/c4downloadAbgabe')}} <i class="fa fa-file-pdf" style="margin-left:4px; cursor: pointer;"></i></a>
</button>
</template>
<template v-else>
{{ $p.t('abgabetool/c4nochNichtsAbgegeben') }}
</template>
</div> </div>
</div> </div>
<div class="row mt-2"> <div class="row mt-2">
@@ -531,13 +592,13 @@ export const AbgabeMitarbeiterDetail = {
</div> </div>
<div class="col-8 col-md-9"> <div class="col-8 col-md-9">
<div class="row"> <div class="row">
<div class="col-4"> <div class="col-auto">
<button v-if="termin.allowedToSave" style="max-height: 40px;" class="btn btn-primary border-0" @click="saveTermin(termin)"> <button v-if="termin.allowedToSave" style="max-height: 40px;" class="btn btn-primary border-0" @click="saveTermin(termin)">
{{ $p.t('abgabetool/c4save') }} {{ $p.t('abgabetool/c4save') }}
<i class="fa-solid fa-floppy-disk"></i> <i class="fa-solid fa-floppy-disk"></i>
</button> </button>
</div> </div>
<div class="col-4"> <div class="col-auto">
<button v-if="termin.allowedToDelete && termin.paabgabe_id > 0" style="max-height: 40px;" class="btn btn-danger border-0" @click="handleDeleteTermin(termin)"> <button v-if="termin.allowedToDelete && termin.paabgabe_id > 0" style="max-height: 40px;" class="btn btn-danger border-0" @click="handleDeleteTermin(termin)">
{{ $p.t('abgabetool/c4delete') }} {{ $p.t('abgabetool/c4delete') }}
<i class="fa-solid fa-trash"></i> <i class="fa-solid fa-trash"></i>
@@ -146,7 +146,7 @@ export const AbgabeStudentDetail = {
downloadAbgabe(termin) { downloadAbgabe(termin) {
this.$api.call(ApiAbgabe.getStudentProjektarbeitAbgabeFile(termin.paabgabe_id, this.projektarbeit.student_uid)) this.$api.call(ApiAbgabe.getStudentProjektarbeitAbgabeFile(termin.paabgabe_id, this.projektarbeit.student_uid))
}, },
formatDate(dateParam) { formatDate(dateParam, showTime = true) {
const date = new Date(dateParam) const date = new Date(dateParam)
// handle missing leading 0 // handle missing leading 0
const padZero = (num) => String(num).padStart(2, '0'); const padZero = (num) => String(num).padStart(2, '0');
@@ -155,7 +155,7 @@ export const AbgabeStudentDetail = {
const day = padZero(date.getDate()); const day = padZero(date.getDate());
const year = date.getFullYear(); const year = date.getFullYear();
return `${day}.${month}.${year}`; return `${day}.${month}.${year}` + (showTime ? ' 23:59' : '');
}, },
async upload(termin) { async upload(termin) {
@@ -211,23 +211,23 @@ export const AbgabeStudentDetail = {
const oneDayMs = 1000 * 60 * 60 * 24 const oneDayMs = 1000 * 60 * 60 * 24
return Math.round((new Date(datum) - new Date(today)) / oneDayMs) return Math.round((new Date(datum) - new Date(today)) / oneDayMs)
}, },
getDateStyle(termin, mode) { getDateStyleClass(termin, mode) {
const datum = new Date(termin.datum) const datum = new Date(termin.datum)
const abgabedatum = new Date(termin.abgabedatum) const abgabedatum = new Date(termin.abgabedatum)
// https://wiki.fhcomplete.info/doku.php?id=cis:abgabetool_fuer_studierende // https://wiki.fhcomplete.info/doku.php?id=cis:abgabetool_fuer_studierende
if (termin.abgabedatum === null) { if (termin.abgabedatum === null) {
if(datum < today) { if(datum < today) {
return 'verpasst-header' return 'verpasst'
} else if (datum > today && this.dateDiffInDays(datum, today) <= 12) { } else if (datum > today && this.dateDiffInDays(datum, today) <= 12) {
return 'abzugeben-header' return 'abzugeben'
} else { } else {
return 'standard-header' return 'standard'
} }
} else if(abgabedatum > datum) { } else if(abgabedatum > datum) {
return 'verspaetet-header' return 'verspaetet'
} else { } else {
return 'abgegeben-header' return 'abgegeben'
} }
}, },
openBeurteilungLink(link) { openBeurteilungLink(link) {
@@ -243,7 +243,7 @@ export const AbgabeStudentDetail = {
getAccTabHeaderForTermin(termin) { getAccTabHeaderForTermin(termin) {
let tabTitle = '' let tabTitle = ''
const datumFormatted = this.formatDate(termin.datum) const datumFormatted = this.formatDate(termin.datum, false)
tabTitle += termin.bezeichnung + ' ' + datumFormatted tabTitle += termin.bezeichnung + ' ' + datumFormatted
return tabTitle return tabTitle
@@ -277,6 +277,36 @@ export const AbgabeStudentDetail = {
} }
}) })
return qgatefound return qgatefound
},
getTooltipVerspaetet() {
return {
value: this.$p.t('abgabetool/c4tooltipVerspaetet'),
class: "custom-tooltip"
}
},
getTooltipVerpasst() {
return {
value: this.$p.t('abgabetool/c4tooltipVerpasst'),
class: "custom-tooltip"
}
},
getTooltipAbzugeben() {
return {
value: this.$p.t('abgabetool/c4tooltipAbzugeben'),
class: "custom-tooltip"
}
},
getTooltipStandard() {
return {
value: this.$p.t('abgabetool/c4tooltipStandard'),
class: "custom-tooltip"
}
},
getTooltipAbgegeben() {
return {
value: this.$p.t('abgabetool/c4tooltipAbgegeben'),
class: "custom-tooltip"
}
} }
}, },
created() { created() {
@@ -301,7 +331,21 @@ export const AbgabeStudentDetail = {
<Accordion :multiple="true" :activeIndex="[0]"> <Accordion :multiple="true" :activeIndex="[0]">
<template v-for="termin in this.projektarbeit?.abgabetermine"> <template v-for="termin in this.projektarbeit?.abgabetermine">
<AccordionTab :header="getAccTabHeaderForTermin(termin)" :headerClass="getDateStyle(termin)"> <AccordionTab :headerClass="getDateStyleClass(termin) + '-header'">
<template #header>
<div class="d-flex row w-100">
<div class="col-auto" style="transform: translateX(-62px)">
<i v-if="getDateStyleClass(termin) == 'verspaetet'" v-tooltip.right="getTooltipVerspaetet" class="fa-solid fa-triangle-exclamation"></i>
<i v-else-if="getDateStyleClass(termin) == 'verpasst'" v-tooltip.right="getTooltipVerpasst" class="fa-solid fa-calendar-xmark"></i>
<i v-else-if="getDateStyleClass(termin) == 'abzugeben'" v-tooltip.right="getTooltipAbzugeben" class="fa-solid fa-hourglass-half"></i>
<i v-else-if="getDateStyleClass(termin) == 'standard'" v-tooltip.right="getTooltipStandard" class="fa-solid fa-clock"></i>
<i v-else-if="getDateStyleClass(termin) == 'abgegeben'" v-tooltip.right="getTooltipAbgegeben" class="fa-solid fa-check"></i>
</div>
<div class="col-6 text-start">
<span>{{getAccTabHeaderForTermin(termin)}}</span>
</div>
</div>
</template>
<div class="row"> <div class="row">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4fixterminv2')}}</div> <div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4fixterminv2')}}</div>
<div class="col-8 col-md-9"> <div class="col-8 col-md-9">
@@ -312,8 +356,6 @@ export const AbgabeStudentDetail = {
:pt="{ root: { class: 'ml-auto' }}" :pt="{ root: { class: 'ml-auto' }}"
> >
</Checkbox> </Checkbox>
<!-- <i v-if="termin.fixtermin" class="fa-solid fa-2x fa-circle-check fhc-bullet-red"></i>-->
<!-- <i v-else="" class="fa-solid fa-2x fa-circle-xmark fhc-bullet-green"></i>-->
</div> </div>
</div> </div>
@@ -329,7 +371,6 @@ export const AbgabeStudentDetail = {
:text-input="true" :text-input="true"
auto-apply> auto-apply>
</VueDatePicker> </VueDatePicker>
<!-- <i class="position-absolute abgabe-zieldatum-overlay fa-solid fa-2x" :class="getDateStyle(termin, 'icon')"></i>-->
</div> </div>
</div> </div>
@@ -366,10 +407,15 @@ export const AbgabeStudentDetail = {
<div class="row mt-2"> <div class="row mt-2">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4abgabedatum')}}</div> <div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4abgabedatum')}}</div>
<div class="col-8 col-md-9"> <div class="col-8 col-md-9">
{{ termin.abgabedatum?.split("-").reverse().join(".") }} <template v-if="termin?.abgabedatum">
<button v-if="termin?.abgabedatum" @click="downloadAbgabe(termin)" class="btn btn-primary"> {{ termin.abgabedatum?.split("-").reverse().join(".") }}
<a> {{$p.t('abgabetool/c4downloadAbgabe')}} <i class="fa fa-file-pdf" style="margin-left:4px; cursor: pointer;"></i></a> <button v-if="termin?.abgabedatum" @click="downloadAbgabe(termin)" class="btn btn-primary">
</button> <a> {{$p.t('abgabetool/c4downloadAbgabe')}} <i class="fa fa-file-pdf" style="margin-left:4px; cursor: pointer;"></i></a>
</button>
</template>
<template v-else>
{{ $p.t('abgabetool/c4nochNichtsAbgegeben') }}
</template>
</div> </div>
</div> </div>
@@ -229,10 +229,8 @@ export const AbgabetoolMitarbeiter = {
termin.allowedToSave = termin.insertvon == this.viewData?.uid && pa.betreuerart_kurzbz != 'Zweitbegutachter' termin.allowedToSave = termin.insertvon == this.viewData?.uid && pa.betreuerart_kurzbz != 'Zweitbegutachter'
termin.allowedToDelete = termin.allowedToSave && !termin.abgabedatum termin.allowedToDelete = termin.allowedToSave && !termin.abgabedatum
termin.bezeichnung = { termin.bezeichnung = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === termin.paabgabetyp_kurzbz)
bezeichnung: termin.bezeichnung,
paabgabetyp_kurzbz: termin.paabgabetyp_kurzbz
}
}) })
pa.student_uid = details.student_uid pa.student_uid = details.student_uid
pa.student = `${pa.vorname} ${pa.nachname}` pa.student = `${pa.vorname} ${pa.nachname}`
@@ -355,7 +353,7 @@ export const AbgabetoolMitarbeiter = {
}, },
computed: { computed: {
}, },
created() { created() {
// fetch config to avoid hard coded links // fetch config to avoid hard coded links
@@ -448,7 +446,7 @@ export const AbgabetoolMitarbeiter = {
</bs-modal> </bs-modal>
<bs-modal ref="modalContainerAbgabeDetail" class="bootstrap-prompt" <bs-modal ref="modalContainerAbgabeDetail" class="bootstrap-prompt"
dialogClass="modal-fullscreen"> dialogClass="modal-xl" :allowFullscreenExpand="true">
<template v-slot:title> <template v-slot:title>
<div> <div>
{{$p.t('abgabetool/c4abgabeMitarbeiterDetailTitle')}} {{$p.t('abgabetool/c4abgabeMitarbeiterDetailTitle')}}
@@ -233,7 +233,7 @@ export const AbgabetoolStudent = {
template: ` template: `
<bs-modal ref="modalContainerAbgabeDetail" class="bootstrap-prompt" <bs-modal ref="modalContainerAbgabeDetail" class="bootstrap-prompt"
dialogClass="modal-fullscreen"> dialogClass="modal-xl" :allowFullscreenExpand="true">
<template v-slot:title> <template v-slot:title>
<div> <div>
{{$p.t('abgabetool/c4abgabeStudentDetailTitle')}} {{$p.t('abgabetool/c4abgabeStudentDetailTitle')}}
@@ -1,12 +1,59 @@
<?php <?php
if (! defined('DB_NAME')) exit('No direct script access allowed'); if (! defined('DB_NAME')) exit('No direct script access allowed');
// add campus.tbl_paabgabetyp options for Quality Gates
if($result = $db->db_query("SELECT 1 FROM information_schema.columns WHERE table_schema = 'campus' AND table_name = 'tbl_paabgabetyp' AND column_name = 'aktiv'"))
{
if($db->db_num_rows($result) === 0)
{
$qry = "ALTER TABLE campus.tbl_paabgabetyp
ADD COLUMN IF NOT EXISTS aktiv BOOLEAN DEFAULT true;";
if(!$db->db_query($qry))
echo '<strong>campus.tbl_paabgabetyp: '.$db->db_last_error().'</strong><br>';
else
echo '<br>paabgabetyp column aktiv default true hinzugefuegt';
}
}
if($result = $db->db_query("SELECT 1 FROM information_schema.columns WHERE table_schema = 'campus' AND table_name = 'tbl_paabgabetyp' AND column_name = 'upload_allowed_default'"))
{
if($db->db_num_rows($result) === 0)
{
$qry = "ALTER TABLE campus.tbl_paabgabetyp
ADD COLUMN IF NOT EXISTS upload_allowed_default BOOLEAN DEFAULT true;";
if(!$db->db_query($qry))
echo '<strong>campus.tbl_paabgabetyp: '.$db->db_last_error().'</strong><br>';
else
echo '<br>paabgabetyp column upload_allowed_default default true hinzugefuegt';
}
}
if($result = $db->db_query("SELECT 1 FROM information_schema.columns WHERE table_schema = 'campus' AND table_name = 'tbl_paabgabetyp' AND column_name = 'benotbar'"))
{
if($db->db_num_rows($result) === 0)
{
$qry = "ALTER TABLE campus.tbl_paabgabetyp
ADD COLUMN IF NOT EXISTS benotbar BOOLEAN DEFAULT true;";
if(!$db->db_query($qry))
echo '<strong>campus.tbl_paabgabetyp: '.$db->db_last_error().'</strong><br>';
else
echo '<br>paabgabetyp column benotbar default true hinzugefuegt';
}
}
// TODO DEFINE ACTUAL VALUES BENOTBAR / UPLOAD_ALLOWED_DEFAULT / AKTIV FOR EACH PAABGABETYPE - DEVLOPER DEFAULTS BELOW
if($result = $db->db_query("SELECT 1 FROM campus.tbl_paabgabetyp WHERE paabgabetyp_kurzbz='qualgate1'")) if($result = $db->db_query("SELECT 1 FROM campus.tbl_paabgabetyp WHERE paabgabetyp_kurzbz='qualgate1'"))
{ {
if($db->db_num_rows($result) === 0) if($db->db_num_rows($result) === 0)
{ {
$qry = "INSERT INTO campus.tbl_paabgabetyp (paabgabetyp_kurzbz, bezeichnung) VALUES('qualgate1', 'Quality Gate 1');"; $qry = "INSERT INTO campus.tbl_paabgabetyp (paabgabetyp_kurzbz, bezeichnung, benotbar, upload_allowed_default, aktiv)
VALUES('qualgate1', 'Quality Gate 1', true, true, true);";
if(!$db->db_query($qry)) if(!$db->db_query($qry))
echo '<strong>campus.tbl_paabgabetyp: '.$db->db_last_error().'</strong><br>'; echo '<strong>campus.tbl_paabgabetyp: '.$db->db_last_error().'</strong><br>';
@@ -15,11 +62,102 @@ if($result = $db->db_query("SELECT 1 FROM campus.tbl_paabgabetyp WHERE paabgabet
} }
} }
// set new cols for zwischenabgabe
if($result = $db->db_query("SELECT 1 FROM campus.tbl_paabgabetyp WHERE paabgabetyp_kurzbz='zwischen'"))
{
if($db->db_num_rows($result) === 1)
{
$qry = "UPDATE campus.tbl_paabgabetyp
SET benotbar = false,
upload_allowed_default = true,
aktiv = true
WHERE paabgabetyp_kurzbz='zwischen';";
if(!$db->db_query($qry))
echo '<strong>campus.tbl_paabgabetyp: '.$db->db_last_error().'</strong><br>';
else
echo '<br>paabgabetyp zwischen updated benotbar = false, upload_allowed_default = true, aktiv = true';
}
}
// set new cols for note
if($result = $db->db_query("SELECT 1 FROM campus.tbl_paabgabetyp WHERE paabgabetyp_kurzbz='note'"))
{
if($db->db_num_rows($result) === 1)
{
$qry = "UPDATE campus.tbl_paabgabetyp
SET benotbar = false,
upload_allowed_default = false,
aktiv = false
WHERE paabgabetyp_kurzbz='note';";
if(!$db->db_query($qry))
echo '<strong>campus.tbl_paabgabetyp: '.$db->db_last_error().'</strong><br>';
else
echo '<br>paabgabetyp note updated benotbar = false, upload_allowed_default = false, aktiv = false';
}
}
// set new cols for abstract / entwurf
if($result = $db->db_query("SELECT 1 FROM campus.tbl_paabgabetyp WHERE paabgabetyp_kurzbz='abstract'"))
{
if($db->db_num_rows($result) === 1)
{
$qry = "UPDATE campus.tbl_paabgabetyp
SET benotbar = false,
upload_allowed_default = true,
aktiv = true
WHERE paabgabetyp_kurzbz='abstract';";
if(!$db->db_query($qry))
echo '<strong>campus.tbl_paabgabetyp: '.$db->db_last_error().'</strong><br>';
else
echo '<br>paabgabetyp abstract updated benotbar = false, upload_allowed_default = true, aktiv = true';
}
}
// set new cols for endabgabe / end
if($result = $db->db_query("SELECT 1 FROM campus.tbl_paabgabetyp WHERE paabgabetyp_kurzbz='end'"))
{
if($db->db_num_rows($result) === 1)
{
$qry = "UPDATE campus.tbl_paabgabetyp
SET benotbar = false,
upload_allowed_default = true,
aktiv = true
WHERE paabgabetyp_kurzbz='end';";
if(!$db->db_query($qry))
echo '<strong>campus.tbl_paabgabetyp: '.$db->db_last_error().'</strong><br>';
else
echo '<br>paabgabetyp end updated benotbar = false, upload_allowed_default = true, aktiv = true';
}
}
// set new cols for endabgabe im sekretariat / enda
if($result = $db->db_query("SELECT 1 FROM campus.tbl_paabgabetyp WHERE paabgabetyp_kurzbz='enda'"))
{
if($db->db_num_rows($result) === 1)
{
$qry = "UPDATE campus.tbl_paabgabetyp
SET benotbar = false,
upload_allowed_default = false,
aktiv = false
WHERE paabgabetyp_kurzbz='enda';";
if(!$db->db_query($qry))
echo '<strong>campus.tbl_paabgabetyp: '.$db->db_last_error().'</strong><br>';
else
echo '<br>paabgabetyp enda updated benotbar = false, upload_allowed_default = false, aktiv = false';
}
}
if($result = $db->db_query("SELECT 1 FROM campus.tbl_paabgabetyp WHERE paabgabetyp_kurzbz='qualgate2'")) if($result = $db->db_query("SELECT 1 FROM campus.tbl_paabgabetyp WHERE paabgabetyp_kurzbz='qualgate2'"))
{ {
if($db->db_num_rows($result) === 0) if($db->db_num_rows($result) === 0)
{ {
$qry = "INSERT INTO campus.tbl_paabgabetyp (paabgabetyp_kurzbz, bezeichnung) VALUES('qualgate2', 'Quality Gate 2');"; $qry = "INSERT INTO campus.tbl_paabgabetyp (paabgabetyp_kurzbz, bezeichnung, benotbar, upload_allowed_default, aktiv)
VALUES('qualgate2', 'Quality Gate 2', true, true, true);";
if(!$db->db_query($qry)) if(!$db->db_query($qry))
echo '<strong>campus.tbl_paabgabetyp: '.$db->db_last_error().'</strong><br>'; echo '<strong>campus.tbl_paabgabetyp: '.$db->db_last_error().'</strong><br>';
@@ -34,7 +172,7 @@ if($result = $db->db_query("SELECT 1 FROM information_schema.columns WHERE table
{ {
$qry = "ALTER TABLE campus.tbl_paabgabe $qry = "ALTER TABLE campus.tbl_paabgabe
ADD COLUMN note SMALLINT DEFAULT NULL, ADD COLUMN IF NOT EXISTS note SMALLINT DEFAULT NULL,
ADD CONSTRAINT tbl_paabgabe_note_fkey ADD CONSTRAINT tbl_paabgabe_note_fkey
FOREIGN KEY (note) FOREIGN KEY (note)
REFERENCES lehre.tbl_note(note) REFERENCES lehre.tbl_note(note)
+120
View File
@@ -42816,6 +42816,106 @@ array(
) )
) )
), ),
array(
'app' => 'core',
'category' => 'abgabetool',
'phrase' => 'c4tooltipVerspaetet',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Verspätet abgegeben",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Submitted late',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'abgabetool',
'phrase' => 'c4tooltipVerpasst',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Termin überschritten",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Deadline exceeded',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'abgabetool',
'phrase' => 'c4tooltipAbzugeben',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Termin innerhalb von 12 Tagen",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Deadline within 12 days',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'abgabetool',
'phrase' => 'c4tooltipStandard',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Termin mehr als 12 Tag entfernt",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Deadline more than 12 days away',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'abgabetool',
'phrase' => 'c4tooltipAbgegeben',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Rechtzeitig abgegeben",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Delivered on time',
'description' => '',
'insertvon' => 'system'
)
)
),
array( array(
'app' => 'core', 'app' => 'core',
'category' => 'abgabetool', 'category' => 'abgabetool',
@@ -42856,6 +42956,26 @@ array(
) )
) )
), ),
array(
'app' => 'core',
'category' => 'abgabetool',
'phrase' => 'c4nochNichtsAbgegeben',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Keine Abgabe vorhanden",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'No submission yet',
'description' => '',
'insertvon' => 'system'
)
)
),
array( array(
'app' => 'core', 'app' => 'core',
'category' => 'abgabetool', 'category' => 'abgabetool',