Merge branch 'feature-25999/C4_cleanup' into feature-53416/stundenplan_eine_lva

# Conflicts:
#	public/js/apps/Cis/Stundenplan.js
#	public/js/components/DashboardWidget/Stundenplan.js
This commit is contained in:
Johann Hoffmann
2024-11-13 11:17:43 +01:00
23 changed files with 599 additions and 136 deletions
@@ -52,7 +52,7 @@ class LvMenu extends FHCAPI_Controller
$this->load->library("PermissionLib", null, 'PermissionLib');
$this->load->library("PhrasesLib");
$this->loadPhrases(array('global'));
$this->loadPhrases(array('global', 'lehre'));
}
//------------------------------------------------------------------------------------------------------------------
@@ -68,18 +68,12 @@ class LvMenu extends FHCAPI_Controller
if(!isset($lvid) || !isset($studiensemester_kurzbz))
$this->terminateWithError('Missing parameters', self::ERROR_TYPE_GENERAL);
// FH-Core Variables
// ##########################################################################################
require_once(FHCPATH.'include/phrasen.class.php');
// get the sprache
$sprache = getUserLanguage();
$p = new phrasen($sprache);
//$p = $this->$p;
// get the user
if (!$user=getAuthUID())
$this->terminateWithError($p->t('global/nichtAngemeldet'));
$this->terminateWithError($this->p->t('global', 'nichtAngemeldet'));
// check if is_lector
$is_lector = false;
@@ -209,39 +203,29 @@ class LvMenu extends FHCAPI_Controller
$menu = array();
//todo: digitale anwesenheiten sollte ein Events.php im config/ folder beinhalten
//$this->fhc_menu_digitale_anwesenheiten($menu, $angemeldet, $studiengang_kz, $semester, $lvid, $angezeigtes_stsem, $p);
$this->fhc_menu_lvinfo($menu, $lvid, $studiengang_kz, $lektor_der_lv, $is_lector, $lehrfach_oe_kurzbz_arr, $p);
$this->fhc_menu_lvinfo($menu, $lvid, $studiengang_kz, $lektor_der_lv, $is_lector, $lehrfach_oe_kurzbz_arr);
$this->fhc_menu_feedback($menu, $angemeldet, $lvid, $p);
$this->fhc_menu_feedback($menu, $angemeldet, $lvid);
$this->fhc_menu_gesamtnote($menu, $angemeldet, $lvid, $lv, $is_lector, $angezeigtes_stsem, $p);
$this->fhc_menu_gesamtnote($menu, $angemeldet, $lvid, $lv, $is_lector, $angezeigtes_stsem);
$this->fhc_menu_emailStudierende($menu, $user, $angemeldet, $lvid, $angezeigtes_stsem, $p);
$this->fhc_menu_emailStudierende($menu, $user, $angemeldet, $lvid, $angezeigtes_stsem);
$this->fhc_menu_abmeldung($menu, $user, $is_lector, $lvid, $angezeigtes_stsem, $p);
$this->fhc_menu_abmeldung($menu, $user, $is_lector, $lvid, $angezeigtes_stsem);
$this->fhc_menu_lehretools($menu, $lvid, $angezeigtes_stsem, $sprache);
$this->fhc_menu_anrechnungStudent($menu, $lvid, $angezeigtes_stsem, $p);
$this->fhc_menu_anrechnungStudent($menu, $lvid, $angezeigtes_stsem);
$this->fhc_menu_anrechnungLector($menu, $angezeigtes_stsem, $p);
$this->fhc_menu_anrechnungLector($menu, $angezeigtes_stsem);
// LEGACY FH-CORE MENU POINTS
//$this->fhc_menu_notenliste($menu, $angemeldet, $is_lector, $lvid, $studiengang_kz, $angezeigtes_stsem, $kurzbz, $semester, $short_short_name, $p);
//$this->fhc_menu_download($menu, $angemeldet, $user_is_allowed_to_upload, $rechte, $semester, $kurzbz, $studiengang_kz, $short_short_name, $short, $p);
//$this->fhc_menu_semesterplan($menu, $angemeldet, $user_is_allowed_to_upload, $rechte, $lvid, $studiengang_kz, $kurzbz, $semester, $short_short_name, $p);
//$this->fhc_menu_stundenupload($menu, $angemeldet, $lvid, $is_lector, $studiengang_kz, $kurzbz, $semester, $short, $short_short_name, $p);
//$this->fhc_menu_pinboard($menu, $angemeldet, $is_lector, $studiengang_kz, $semester, $p);
// Addons Menu Logic
// ##########################################################################################
$params = [
'sprache'=>$sprache,
'p'=>$p,
//'p'=>$p,
'ci_p'=> $this->p,
//'db'=>$db,
'user'=>$user,
@@ -301,7 +285,7 @@ class LvMenu extends FHCAPI_Controller
}
private function fhc_menu_digitale_anwesenheiten(&$menu, $angemeldet, $studiengang_kz, $semester, $lvid, $angezeigtes_stsem, $p){
private function fhc_menu_digitale_anwesenheiten(&$menu, $angemeldet, $studiengang_kz, $semester, $lvid, $angezeigtes_stsem){
// DIGITALE ANWESENHEITEN
if (CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN && $angemeldet) {
@@ -310,7 +294,7 @@ class LvMenu extends FHCAPI_Controller
(
'id' => 'core_menu_digitale_anwesenheitslisten',
'position' => '50',
'name' => $p->t('lehre/digiAnw'),
'name' => $this->p->t('lehre', 'digiAnw'),
'c4_icon' => base_url('skin/images/button_kreuzerltool.png'),
'c4_link' => base_url("index.ci.php/extensions/FHC-Core-Anwesenheiten/?stg_kz=$studiengang_kz&sem=$semester&lvid=$lvid&sem_kurzbz=$angezeigtes_stsem&nav=false"),
'c4_linkList' => []
@@ -318,7 +302,7 @@ class LvMenu extends FHCAPI_Controller
}
}
private function fhc_menu_lvinfo(&$menu, $lvid, $studiengang_kz, $lektor_der_lv, $is_lector, $lehrfach_oe_kurzbz_arr, $p){
private function fhc_menu_lvinfo(&$menu, $lvid, $studiengang_kz, $lektor_der_lv, $is_lector, $lehrfach_oe_kurzbz_arr){
// LVINFO
if(!defined('CIS_LEHRVERANSTALTUNG_LVINFO_ANZEIGEN') || CIS_LEHRVERANSTALTUNG_LVINFO_ANZEIGEN)
@@ -333,7 +317,7 @@ class LvMenu extends FHCAPI_Controller
|| $this->PermissionLib->isBerechtigtMultipleOe('lehre/lvinfo', $lehrfach_oe_kurzbz_arr)
)
{
$c4_linkList[]= [$p->t('lehre/lvInfoBearbeiten'), 'ects/index.php?lvid='.$lvid];
$c4_linkList[]= [$this->p->t('lehre', 'lvInfoBearbeiten'), 'ects/index.php?lvid='.$lvid];
}
elseif ($is_lector)
{
@@ -344,7 +328,7 @@ class LvMenu extends FHCAPI_Controller
(
'id'=>'core_menu_lvinfo',
'position'=>'10',
'name'=>$p->t('lehre/lehrveranstaltungsinformation'),
'name'=>$this->p->t('lehre', 'lehrveranstaltungsinformation'),
'icon'=>'../../../skin/images/button_lvinfo.png',
'link'=>'',
'c4_icon'=> base_url('skin/images/button_lvinfo.png'),
@@ -354,7 +338,7 @@ class LvMenu extends FHCAPI_Controller
}
}
private function fhc_menu_feedback(&$menu, $angemeldet, $lvid, $p){
private function fhc_menu_feedback(&$menu, $angemeldet, $lvid){
//FEEDBACK
if((!defined('CIS_LEHRVERANSTALTUNG_FEEDBACK_ANZEIGEN') || CIS_LEHRVERANSTALTUNG_FEEDBACK_ANZEIGEN) && $angemeldet)
{
@@ -362,14 +346,14 @@ class LvMenu extends FHCAPI_Controller
(
'id'=>'core_menu_feedback',
'position'=>'60',
'name'=>$p->t('lehre/feedback'),
'name'=>$this->p->t('lehre', 'feedback'),
'c4_icon'=> base_url('skin/images/button_feedback.png'),
'c4_link'=> base_url('feedback.php?lvid='.$lvid),
);
}
}
private function fhc_menu_gesamtnote(&$menu, $angemeldet, $lvid, $lv_obj, $is_lector, $angezeigtes_stsem, $p){
private function fhc_menu_gesamtnote(&$menu, $angemeldet, $lvid, $lv_obj, $is_lector, $angezeigtes_stsem){
//Gesamtnote
if($is_lector && ((!defined('CIS_LEHRVERANSTALTUNG_GESAMTNOTE_ANZEIGEN') || CIS_LEHRVERANSTALTUNG_GESAMTNOTE_ANZEIGEN) && $angemeldet))
{
@@ -379,7 +363,7 @@ class LvMenu extends FHCAPI_Controller
(
'id'=>'core_menu_gesamtnote',
'position'=>'80',
'name'=>$p->t('lehre/gesamtnote'),
'name'=>$this->p->t('lehre', 'gesamtnote'),
'c4_icon'=> base_url('skin/images/button_endnote.png'),
'c4_link'=> base_url('cis/private/lehre/benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem))
//'c4_link'=> base_url('benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem))
@@ -391,10 +375,10 @@ class LvMenu extends FHCAPI_Controller
(
'id'=>'core_menu_gesamtnote',
'position'=>'80',
'name'=>$p->t('lehre/gesamtnote'),
'name'=>$this->p->t('lehre', 'gesamtnote'),
'c4_icon'=>base_url('skin/images/button_endnote.png'),
'c4_link'=>'#',
'c4_linkList'=>[[$p->t('lehre/noteneingabedeaktiviert'),'#']],
'c4_linkList'=>[[$this->p->t('lehre', 'noteneingabedeaktiviert'),'#']],
);
}
}
@@ -402,7 +386,7 @@ class LvMenu extends FHCAPI_Controller
private function fhc_menu_emailStudierende(&$menu, $user, $angemeldet, $lvid, $angezeigtes_stsem, $p){
private function fhc_menu_emailStudierende(&$menu, $user, $angemeldet, $lvid, $angezeigtes_stsem){
// Email an Studierende
if((!defined('CIS_LEHRVERANSTALTUNG_MAILSTUDIERENDE_ANZEIGEN') || CIS_LEHRVERANSTALTUNG_MAILSTUDIERENDE_ANZEIGEN) && $angemeldet)
{
@@ -448,10 +432,10 @@ class LvMenu extends FHCAPI_Controller
if($nomail != '')
{
$c4_linkList[] = array(
$p->t('lehre/keinMailverteiler', array($nomail)),
$this->p->t('lehre', 'keinMailverteiler', array('nomail' => $nomail)),
'#'
);
$link_onclick = 'alert(\''.$p->t('lehre/keinMailverteiler', array($nomail)) . '\');';
$link_onclick = 'alert(\''.$this->p->t('lehre', 'keinMailverteiler', array('nomail' => $nomail)) . '\');';
}
else
{
@@ -463,7 +447,7 @@ class LvMenu extends FHCAPI_Controller
(
'id'=>'core_menu_mailanstudierende',
'position'=>'100',
'name'=>$p->t('lehre/mail'),
'name'=>$this->p->t('lehre', 'mail'),
'c4_icon'=>base_url('skin/images/button_feedback.png'),
'c4_icon2' => 'fa-regular fa-envelope',
'c4_link'=>$mailto,
@@ -475,7 +459,7 @@ class LvMenu extends FHCAPI_Controller
private function fhc_menu_abmeldung(&$menu, $user, $is_lector, $lvid, $angezeigtes_stsem, $p){
private function fhc_menu_abmeldung(&$menu, $user, $is_lector, $lvid, $angezeigtes_stsem){
if(!defined('CIS_LEHRVERANSTALTUNG_ABMELDUNG_ANZEIGEN') || CIS_LEHRVERANSTALTUNG_ABMELDUNG_ANZEIGEN)
{
if(!$is_lector)
@@ -487,7 +471,7 @@ class LvMenu extends FHCAPI_Controller
(
'id'=>'core_menu_abmeldung',
'position'=>'120',
'name'=>$p->t('lehre/abmelden'),
'name'=>$this->p->t('lehre', 'abmelden'),
'c4_icon'=>base_url('skin/images/button_studiupload.png'),
'c4_link'=>base_url('abmeldung.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem)),
);
@@ -512,7 +496,7 @@ class LvMenu extends FHCAPI_Controller
}
}
private function fhc_menu_anrechnungStudent(&$menu, $lvid, $angezeigtes_stsem, $p){
private function fhc_menu_anrechnungStudent(&$menu, $lvid, $angezeigtes_stsem){
// Anerkennung nachgewiesener Kenntnisse (Anrechnung) - Anzeige fuer Studenten
if((!defined('CIS_LEHRVERANSTALTUNG_ANRECHNUNG_ANZEIGEN') || CIS_LEHRVERANSTALTUNG_ANRECHNUNG_ANZEIGEN)
&& $this->PermissionLib->isBerechtigt('student/anrechnung_beantragen'))
@@ -521,7 +505,7 @@ class LvMenu extends FHCAPI_Controller
(
'id' => 'core_menu_anerkennungNachgewiesenerKenntnisse',
'position' => '128',
'name' => $p->t('lehre/anrechnung'),
'name' => $this->p->t('lehre', 'anrechnung'),
'c4_icon' => base_url('skin/images/button_listen.png'),
'c4_icon2' => 'fa-regular fa-folder-open',
'c4_link' => base_url('cis.php/lehre/anrechnung/RequestAnrechnung?studiensemester='.urlencode($angezeigtes_stsem).'&lv_id='.urlencode($lvid))
@@ -529,7 +513,7 @@ class LvMenu extends FHCAPI_Controller
}
}
private function fhc_menu_anrechnungLector(&$menu, $angezeigtes_stsem, $p){
private function fhc_menu_anrechnungLector(&$menu, $angezeigtes_stsem){
// Anerkennung nachgewiesener Kenntnisse (Anrechnung) - Anzeige fuer LektorInnen
if((!defined('CIS_LEHRVERANSTALTUNG_ANRECHNUNG_ANZEIGEN') || CIS_LEHRVERANSTALTUNG_ANRECHNUNG_ANZEIGEN)
&& $this->PermissionLib->isBerechtigt('lehre/anrechnung_empfehlen'))
@@ -538,7 +522,7 @@ class LvMenu extends FHCAPI_Controller
(
'id' => 'core_menu_anerkennungNachgewiesenerKenntnisse_empfehlen',
'position' => '128',
'name' => $p->t('lehre/anrechnungen'),
'name' => $this->p->t('lehre', 'anrechnungen'),
'c4_icon'=> base_url('skin/images/button_listen.png'),
'c4_icon2' => 'fa-regular fa-folder-open',
'c4_link' => base_url('cis.php/lehre/anrechnung/ReviewAnrechnungUebersicht?studiensemester='.urlencode($angezeigtes_stsem))
+2 -2
View File
@@ -565,6 +565,7 @@ class FilterCmptLib
getAuthPersonId()
);
// If filters were loaded
if (hasData($filters))
{
@@ -1172,5 +1173,4 @@ class FilterCmptLib
return $filterName;
}
}
}
+1 -2
View File
@@ -177,5 +177,4 @@
</body>
</html>
<!-- Footer end -->
<!-- Footer end -->
+1
View File
@@ -27,6 +27,7 @@ define('CIS_LEHRVERANSTALTUNG_SEMESTERINFO_ANZEIGEN',true);
define('CIS_LEHRVERANSTALTUNG_LEHRFACH_ANZEIGEN',false);
define('CIS_LEHRVERANSTALTUNG_GESAMTNOTE_ANZEIGEN', true);
define('CIS_LEHRVERANSTALTUNG_ANRECHNUNG_ANZEIGEN', true);
define('CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN', true);
// Im CIS Menue Links bei Modulen anzeigen wenn Lehrauftrag
define('CIS_LEHRVERANSTALTUNG_MODULE_LINK',true);
+26 -2
View File
@@ -240,13 +240,37 @@ function checkZeilenUmbruch()
);
}
// Digitale Anwesenheiten
if(CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN && $angemeldet
&& ($rechte->isBerechtigt('extension/anw_ent_admin')
|| $rechte->isBerechtigt('extension/anwesenheit_lektor')
|| $rechte->isBerechtigt('extension/anwesenheit_student')
|| $rechte->isBerechtigt('extension/anwesenheit_admin')))
{
$link='';
$text='';
$link= APP_ROOT."index.ci.php/extensions/FHC-Core-Anwesenheiten/?stg_kz=$studiengang_kz&sem=$semester&lvid=$lvid&sem_kurzbz=$angezeigtes_stsem";
$menu[]=array
(
'id'=>'core_menu_digitale_anwesenheitslisten',
'position'=>'50',
'name'=> $p->t('lehre/digiAnw'),
'icon'=>'../../../skin/images/button_kreuzerltool.png',
'link'=>$link,
'text'=>$text
);
}
//FEEDBACK
if((!defined('CIS_LEHRVERANSTALTUNG_FEEDBACK_ANZEIGEN') || CIS_LEHRVERANSTALTUNG_FEEDBACK_ANZEIGEN) && $angemeldet)
{
$menu[]=array
(
'id'=>'core_menu_feedback',
'position'=>'50',
'position'=>'60',
'name'=>$p->t('lehre/feedback'),
'icon'=>'../../../skin/images/button_feedback.png',
'link'=>'feedback.php?lvid='.$lvid,
@@ -584,4 +608,4 @@ function isDirectoryEmpty($DOC_ROOT, $kurzbz, $semester, $short_short_name, $typ
}
?>
</tr>
</table>
</table>
+1
View File
@@ -16,6 +16,7 @@ $this->phrasen['lehre/upload']='Upload';
$this->phrasen['lehre/ziparchiv']='Zip-Archiv';
$this->phrasen['lehre/ziparchivTitle']='Alle Dateien im Download Verzeichnis als Zip-Archiv herunterladen';
$this->phrasen['lehre/anwesenheitsUndNotenlisten']='Anwesenheits- und Notenlisten';
$this->phrasen['lehre/digiAnw']='Digitale Anwesenheiten';
$this->phrasen['lehre/leistungsuebersicht']='Leistungsübersicht';
$this->phrasen['lehre/newsgroups']='Newsgroups';
$this->phrasen['lehre/feedback']='Feedback';
+1
View File
@@ -16,6 +16,7 @@ $this->phrasen['lehre/upload']='Upload';
$this->phrasen['lehre/ziparchiv']='Zip-File';
$this->phrasen['lehre/ziparchivTitle']='Download all Files as Zip-File';
$this->phrasen['lehre/anwesenheitsUndNotenlisten']='Attendance and grade list';
$this->phrasen['lehre/digiAnw']='Digital Attendances';
$this->phrasen['lehre/leistungsuebersicht']='Performance List';
$this->phrasen['lehre/newsgroups']='Newsgroups';
$this->phrasen['lehre/feedback']='Feedback';
+16 -1
View File
@@ -68,6 +68,21 @@
box-shadow: 0 0 0 1px #dee2e6 !important;
}
.fhc-calendar-no-events-overlay{
position: relative;
}
.fhc-calendar-no-events-overlay::before {
content: "";
position: absolute;
top: 0;
left: 0;
margin:auto;
width: 100%;
height: 100%;
background-image: linear-gradient(120deg, white, gray );
opacity: .7;
}
.fhc-calendar-day-page {
/*aspect-ratio: 7/6;*/
min-height: 0;
@@ -77,7 +92,7 @@
/*width: calc(100% + 1.5em);*/
height: calc(100% + 1.5em);
}
.fhc-calendar-day-page .flex-column > div {
.fhc-calendar-day-page .flex-column > .flex-grow-1 {
padding-left: 3em;
}
.fhc-calendar-day-page .flex-column {
+4 -3
View File
@@ -33,6 +33,7 @@ const app = Vue.createApp({
monthLastDay: function () {
return this.calendarDateToString(this.calendarDate.cdLastDayOfCalendarMonth);
},
},
methods:{
getLvID: function () {
@@ -57,7 +58,7 @@ const app = Vue.createApp({
if (checkDate(new CalendarDate(start)) && checkDate(new CalendarDate(end))){
// reset the events before querying the new events to activate the loading spinner
this.events = null;
this.calendarDate = tmp_date;
this.calendarDate = new CalendarDate(end);
Vue.nextTick(() => {
this.loadEvents();
});
@@ -102,9 +103,9 @@ const app = Vue.createApp({
});
},
},
created(){
created()
{
this.getLvID()
this.loadEvents();
},
//TODO: Stundenplan phrase
template:/*html*/`
+74 -3
View File
@@ -26,6 +26,7 @@ export default {
focusDate: this.focusDate,
size: Vue.computed({ get: () => this.size, set: v => this.size = v }),
events: Vue.computed(() => this.eventsPerDay),
filteredEvents: Vue.computed(() => this.filteredEvents),
minimized: Vue.computed({ get: () => this.minimized, set: v => this.$emit('update:minimized', v) }),
showWeeks: this.showWeeks,
noMonthView: this.noMonthView,
@@ -55,7 +56,28 @@ export default {
},
minimized: Boolean,
noWeekView: Boolean,
noMonthView: Boolean
noMonthView: Boolean,
},
watch:{
// scroll to the first event if the html element was found
async scrollTime(newScrollTime){
// scroll the Stundenplan to the closest event
await Vue.nextTick();
let previousScrollAnchor = document.getElementById('scroll' + (newScrollTime-1) + this.focusDate.d + this.focusDate.w)
let scrollAnchor = document.getElementById('scroll' + newScrollTime+this.focusDate.d+this.focusDate.w);
if (previousScrollAnchor)
{
previousScrollAnchor.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
else
{
if (scrollAnchor)
{
scrollAnchor.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}
}
},
emits: [
'select:day',
@@ -102,7 +124,52 @@ export default {
}
return result;
}, {});
}
},
// returns the hour of the earliest event, used to scroll to the events in the calendar (week / day view)
scrollTime: function () {
// return the first beginning time of the filtered events
if(this.filteredEvents && Array.isArray(this.filteredEvents) && this.filteredEvents.length > 0)
{
return parseInt(this.filteredEvents.sort((a, b) => parseInt(a.beginn) - parseInt(b.beginn))[0].beginn);
}
// there is no event that matches the current view mode constraints
else
{
return null;
}
},
// filters the events based on the current calendar view mode
// week view - filter events based on their week
// day view - filter events based on their day and week
// month view - does not filter the events
filteredEvents: function(){
if (this.events && Array.isArray(this.events) && this.events.length > 0) {
let filteredEvents = this.events.filter(event => {
let eventDate = new CalendarDate(new Date(event.datum));
if (this.mode == 'week')
{
// week view filters the elements only for the same week
return this.focusDate.w == eventDate.w;
}
else if (this.mode == 'day')
{
// day view filters the elements for the same day and the same week
return this.focusDate.d == eventDate.d && this.focusDate.w == eventDate.w;
}
else
{
// returns all the events, does not filter the events
return true;
}
})
return filteredEvents;
}
else
{
return null;
}
},
},
methods: {
handleInput(day) {
@@ -136,9 +203,10 @@ export default {
}
}).observe(this.$refs.container);
}
},
template: /*html*/`
<div ref="container" class="fhc-calendar card" :class="sizeClass">
<div ref="container" class="fhc-calendar card h-100" :class="sizeClass">
<component :is="'calendar-' + mode" @updateMode="mode = $event" @change:range="$emit('change:range',$event)" @input="handleInput" >
<template #weekPage="{event,day}">
<slot name="weekPage" :event="event" :day="day"></slot>
@@ -146,6 +214,9 @@ export default {
<template #dayPage="{event,day}">
<slot name="dayPage" :event="event" :day="day"></slot>
</template>
<template #minimizedPage="{event,day}">
<slot name="minimizedPage" :event="event" :day="day"></slot>
</template>
</component>
</div>`
}
+27 -12
View File
@@ -15,7 +15,11 @@ export default {
'focusDate',
'size',
'events',
'noMonthView'
'noMonthView',
'filteredEvents',
'isSliding',
'calendarScrollTop',
'calendarClientHeight',
],
props: {
year: Number,
@@ -28,6 +32,9 @@ export default {
'input'
],
computed: {
noEventsTextPosition(){
return this.calendarScrollTop + 100;
},
hours() {
// returns an array with elements starting at 7 and ending at 24
return [...Array(24).keys()].filter(hour => hour >= 7 && hour <= 24);
@@ -39,6 +46,9 @@ export default {
return result;
},
eventsPerDayAndHour() {
// return early if the calendar pane is sliding
if (this.isSliding) return {};
const res = {};
this.days.forEach(day => {
let key = day.toDateString();
@@ -75,7 +85,7 @@ export default {
},
smallestTimeFrame() {
return [30, 15, 10, 5][this.size];
}
},
},
methods: {
calcHourPosition(event) {
@@ -149,23 +159,28 @@ export default {
</div>
</div>
<div ref="eventcontainer" class="position-relative flex-grow-1" @mousemove="calcHourPosition" @mouseleave="" >
<div v-for="hour in hours" :key="hour" class="position-absolute box-shadow-border-top" style="pointer-events: none;" :style="{top:getAbsolutePositionForHour(hour),left:0,right:0,'z-index':0}"></div>
<div :id="'scroll'+hour+focusDate.d+week" v-for="hour in hours" :key="hour" class="position-absolute box-shadow-border-top" style="pointer-events: none;" :style="{top:getAbsolutePositionForHour(hour),left:0,right:0,'z-index':0}"></div>
<div v-if="hourPosition" class="position-absolute border-top small" style="pointer-events: none; padding-left:3.5rem; margin-top:-1px;z-index:2;border-color:#00649C !important" :style="{top:hourPosition+'px',left:0,right:0}">
<span class="border border-top-0 px-2 bg-white">{{hourPositionTime}}</span>
</div>
<div class="events">
<div class="hours">
<div v-for="hour in hours" style="min-height:100px" :key="hour" class="text-muted text-end small" :ref="'hour' + hour">{{hour}}:00</div>
</div>
<div v-for="day in eventsPerDayAndHour" :key="day" class=" day border-start" :style="{'grid-template-columns': '1 1fr', 'grid-template-rows': 'repeat(' + (hours.length * 60 / smallestTimeFrame) + ', 1fr)'}">
<div :style="{'background-color':event.orig.color}" class="mx-2 border border-dark border-2 small rounded overflow-hidden " @click.prevent="$emit('input', event.orig)" :style="{'z-index':1,'grid-column-start': 1+(event.lane-1)*day.lanes/event.maxLane, 'grid-column-end': 1+event.lane*day.lanes/event.maxLane, 'grid-row-start': dateToMinutesOfDay(event.start), 'grid-row-end': dateToMinutesOfDay(event.end) ,'--test': dateToMinutesOfDay(event.end)}" v-for="event in day.events" :key="event">
<slot name="dayPage" :event="event" :day="day">
<p>this is a placeholder which means that no template was passed to the Calendar Page slot</p>
</slot>
<div>
<h1 v-if="filteredEvents.length==0" class="m-0 text-secondary" ref="noEventsText" :style="{'top':noEventsTextPosition+'px'}" style="position:absolute; left:0; text-align:center; width: 100%; z-index:1">Keine Lehrveranstaltungen</h1>
<div class="events" :class="{'fhc-calendar-no-events-overlay':filteredEvents.length==0}">
<div class="hours">
<div v-for="hour in hours" style="min-height:100px" :key="hour" class="text-muted text-end small" :ref="'hour' + hour">{{hour}}:00</div>
</div>
<div v-for="day in eventsPerDayAndHour" :key="day" class=" day border-start" :style="{'grid-template-columns': '1 1fr', 'grid-template-rows': 'repeat(' + (hours.length * 60 / smallestTimeFrame) + ', 1fr)'}">
<div :style="{'background-color':event.orig.color}" class="mx-2 border border-dark border-2 small rounded overflow-hidden " @click.prevent="$emit('input', event.orig)" :style="{'z-index':1,'grid-column-start': 1+(event.lane-1)*day.lanes/event.maxLane, 'grid-column-end': 1+event.lane*day.lanes/event.maxLane, 'grid-row-start': dateToMinutesOfDay(event.start), 'grid-row-end': dateToMinutesOfDay(event.end) ,'--test': dateToMinutesOfDay(event.end)}" v-for="event in day.events" :key="event">
<slot name="dayPage" :event="event" :day="day">
<p>this is a placeholder which means that no template was passed to the Calendar Page slot</p>
</slot>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
+29 -28
View File
@@ -3,10 +3,11 @@ export default {
return{
selected: this.mode,
modes:{
week:"Woche",
month:"Monat",
day: "Tag",
week: { mode_bezeichnung: "Woche", icon: "fa-calendar-week", condition: !this.noWeekView },
month: { mode_bezeichnung: "Monat", icon: "fa-calendar-days", condition: !this.noMonthView },
day: { mode_bezeichnung: "Tag", icon: "fa-sun" , condition:true},
},
headerPadding:null,
}
},
inject: [
@@ -44,36 +45,36 @@ export default {
return c;
}
},
template: `
template: /*html*/`
<div class="calendar-header card-header w-100" :class="classHeader">
<div v-if="!noWeekView && !noMonthView" class="row justify-content-end" style="position: absolute; width: 98%; pointer-events: none;">
<div class="col-auto" style="pointer-events: all;">
<div role="group" aria-label="Kalender Modus">
<button type="button" :class="{'active':mode_kurzbz == mode}" style="margin-right: 4px;" @click.prevent="$emit('updateMode',mode_kurzbz)" class="btn btn-outline-secondary" v-for="(mode_bezeichnung,mode_kurzbz) in modes">
<i v-if="!noWeekView && mode_kurzbz == 'week'" class="fa fa-calendar-week"></i>
<i v-else-if="!noMonthView && mode_kurzbz == 'month'" class="fa fa-calendar-days"></i>
<i v-else-if="mode_kurzbz == 'day'" class="fa-solid fa-sun"></i>
</button>
<div class="row align-items-center ">
<div class="col offset-0 offset-md-3" :style="{'padding-left':headerPadding}">
<div class="row align-items-center justify-content-center">
<div class="col-auto ">
<button class="btn btn-outline-secondary border-0" :class="{'btn-sm':!this.size}" @click="$emit('prev')"><i class="fa fa-chevron-left"></i></button>
</div>
<div class="justify-content-center text-center col-auto">
<div class="d-flex justify-content-center align-items-center">
<button class="btn btn-link link-secondary text-decoration-none" :class="{'btn-sm': !this.size}" @click="$emit('click')">
{{ title }}
<i v-if="eventsAreNull" class="fa fa-spinner fa-pulse"></i>
</button>
</div>
</div>
<div class="col-auto ">
<button class="btn btn-outline-secondary border-0" :class="{'btn-sm': !this.size}" @click="$emit('next')"><i class="fa fa-chevron-right"></i></button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-5 d-flex justify-content-end">
<button class="btn btn-outline-secondary border-0" :class="{'btn-sm':!this.size}" @click="$emit('prev')"><i class="fa fa-chevron-left"></i></button>
</div>
<div class="justify-content-center text-center col-2">
<div class="d-flex justify-content-center align-items-center">
<button class="btn btn-link link-secondary text-decoration-none" :class="{'btn-sm': !this.size}" @click="$emit('click')">
{{ title }}
<i v-if="eventsAreNull" class="fa fa-spinner fa-pulse"></i>
</button>
<div ref="viewButtons" v-if="!noWeekView && !noMonthView" class=" col-12 col-md-3 d-flex justify-content-center justify-content-md-end align-items-center" style="pointer-events: none;">
<div style="pointer-events: all;">
<div role="group" aria-label="Kalender Modus">
<button type="button" :class="{'active':mode_kurzbz === mode}" style="margin-right: 4px;" @click.prevent="$emit('updateMode',mode_kurzbz)" class="btn btn-outline-secondary" v-for="({mode_bezeichnung,icon,condition},mode_kurzbz) in modes">
<i v-if="condition" class="fa" :class="icon" ></i>
</button>
</div>
</div>
</div>
<div class="col-5 d-flex justify-content-start">
<button class="btn btn-outline-secondary border-0" :class="{'btn-sm': !this.size}" @click="$emit('next')"><i class="fa fa-chevron-right"></i></button>
</div>
</div>
</div>`
}
+2 -1
View File
@@ -22,9 +22,10 @@ export default {
}
},
template: `
<div class="fhc-calendar-minimized">
<div class="fhc-calendar-minimized h-100 d-flex flex-column">
<div class="card-header d-grid" :class="classHeader">
<button class="btn btn-link link-secondary text-decoration-none" @click="maximize">{{ date.format({dateStyle: ['long','full','full','full'][this.size]}) }}</button>
</div>
<slot name="minimizedPage"></slot>
</div>`
}
+23 -3
View File
@@ -6,7 +6,17 @@ export default {
return {
carousel: null,
queue: 0,
offset: 0
offset: 0,
slideAnimation:false,
scrollTop:null,
clientHeight:null,
}
},
provide() {
return {
isSliding: Vue.computed(() => this.slideAnimation),
calendarScrollTop: Vue.computed(() =>this.scrollTop),
calendarClientHeight: Vue.computed(() => this.clientHeight),
}
},
computed: {
@@ -15,6 +25,10 @@ export default {
}
},
methods: {
scrollCalendar(event){
this.scrollTop = this.$refs.calendarContainer.scrollTop;
this.clientHeight = this.$refs.calendarContainer.clientHeight;
},
prev() {
if (!this.queue--)
this.carousel.prev();
@@ -34,6 +48,10 @@ export default {
else
this.carousel.prev();
}
this.slideAnimation = false;
},
slide(evt) {
this.slideAnimation = true;
}
},
mounted() {
@@ -43,11 +61,13 @@ export default {
interval: false
});
}
this.scrollTop = this.$refs.calendarContainer.scrollTop;
this.clientHeight = this.$refs.calendarContainer.clientHeight;
},
template: `
<div ref="carousel" class="calendar-pane carousel slide" @[\`slid.bs.carousel\`]="slid" :data-queue="queue">
<div ref="carousel" class="calendar-pane carousel slide" @[\`slide.bs.carousel\`]="slide" @[\`slid.bs.carousel\`]="slid" :data-queue="queue">
<!--height calc function just for user testing purpose (has to be fixed)-->
<div class="carousel-inner " style="height:calc(100vh - 220px); overflow:scroll">
<div @scroll="scrollCalendar" ref="calendarContainer" class="carousel-inner " style="height:calc(100vh - 220px); overflow:scroll">
<div v-for="i in [...Array(3).keys()]" :key="i" class="carousel-item">
<slot :index="i" :offset="offsets[i]" />
</div>
+9 -4
View File
@@ -15,7 +15,8 @@ export default {
'focusDate',
'size',
'events',
'noMonthView'
'noMonthView',
'isSliding'
],
props: {
year: Number,
@@ -45,6 +46,9 @@ export default {
},
eventsPerDayAndHour() {
// return early if the calendar pane is sliding
if (this.isSliding) return {};
const res = {};
this.days.forEach(day => {
let key = day.toDateString();
@@ -137,11 +141,12 @@ export default {
}
},
mounted() {
setTimeout(() => this.$refs.eventcontainer.scrollTop = this.$refs.eventcontainer.scrollHeight / 3 + 1, 0);
},
template: `
template: /*html*/`
<div class="fhc-calendar-week-page">
<div class="d-flex flex-column">
<div class="fhc-calendar-week-page-header d-grid border-2 border-bottom text-center" :style="{'z-index':4,'grid-template-columns': 'repeat(' + days.length + ', 1fr)', 'grid-template-rows':1}" style="position:sticky; top:0; " >
<div type="button" v-for="day in days" :key="day" class="flex-grow-1" :title="day.toLocaleString(undefined, {dateStyle:'short'})" @click.prevent="changeToMonth(day)">
@@ -150,7 +155,7 @@ export default {
</div>
</div>
<div ref="eventcontainer" class="position-relative flex-grow-1" @mousemove="calcHourPosition" @mouseleave="" >
<div v-for="hour in hours" :key="hour" class="position-absolute box-shadow-border-top" style="pointer-events: none;" :style="{top:getAbsolutePositionForHour(hour),left:0,right:0,'z-index':0}"></div>
<div :id="'scroll'+hour+focusDate.d+week" v-for="hour in hours" :key="hour" class="position-absolute box-shadow-border-top" style="pointer-events: none;" :style="{top:getAbsolutePositionForHour(hour),left:0,right:0,'z-index':0}"></div>
<div v-if="hourPosition" class="position-absolute border-top small" style="pointer-events: none; padding-left:3.5rem; margin-top:-1px;z-index:2;border-color:#00649C !important" :style="{top:hourPosition+'px',left:0,right:0}">
<span class="border border-top-0 px-2 bg-white">{{hourPositionTime}}</span>
</div>
@@ -43,7 +43,7 @@ export default {
}, []))
},
currentEvents() {
return (this.events || []).filter(evt => evt.end < this.dayAfterCurrentDay && evt.start >= this.currentDay)//.sort((e1, e2) => e1.beginn > e2.beginn);
return (this.events || []).filter(evt => evt.end < this.dayAfterCurrentDay && evt.start >= this.currentDay);
},
dayAfterCurrentDay() {
let currentDay = new Date(this.currentDay);
@@ -61,7 +61,7 @@ export default {
},
monthLastDay: function () {
return this.calendarDateToString(this.calendarDate.cdLastDayOfCalendarMonth);
}
},
},
methods: {
getEventStyle: function(evt) {
@@ -192,6 +192,30 @@ export default {
<div class="dashboard-widget-stundenplan d-flex flex-column h-100">
<lv-modal v-if="selectedEvent" ref="lvmodal" :event="selectedEvent" />
<content-modal :contentID="roomInfoContentID" :ort_kurzbz="" dialogClass="modal-lg" ref="contentModal"/>
<fhc-calendar @change:range="updateRange" :initial-date="currentDay" class="border-0" class-header="p-0" @select:day="selectDay" v-model:minimized="minimized" :events="events" no-week-view :show-weeks="false" >
<template #minimizedPage >
<div class="flex-grow-1 overflow-scroll">
<div v-if="events === null" class="d-flex h-100 justify-content-center align-items-center">
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>
<div v-else-if="currentEvents.length" class="list-group list-group-flush">
<div role="button" @click="showLvUebersicht(evt)" class="" v-for="evt in currentEvents" :key="evt.id" class="list-group-item small" :style="{'background-color':evt.color}">
<b>{{evt.topic}}</b>
<br>
<small class="d-flex w-100 justify-content-between">
<!-- event modifier stop to prevent opening the modal for the lv Uebersicht when clicking on the ort_kurzbz -->
<span @click.stop="showRoomInfoModal(evt.ort_kurzbz)" style="text-decoration:underline" type="button">{{evt.ort_kurzbz}}</span>
<span>{{evt.start.toLocaleTimeString(undefined, {hour:'numeric',minute:'numeric'})}}-{{evt.end.toLocaleTimeString(undefined, {hour:'numeric',minute:'numeric'})}}</span>
</small>
</div>
</div>
<div v-else class="d-flex h-100 justify-content-center align-items-center fst-italic text-center">
{{ p.t('lehre/noLvFound') }}
</div>
</div>
</template>
</fhc-calendar>
<fhc-calendar ref="calendar" @change:range="updateRange" :initial-date="currentDay" class="border-0" class-header="p-0" @select:day="selectDay" v-model:minimized="minimized" :events="events" no-week-view :show-weeks="false" />
<div v-show="minimized" class="flex-grow-1 overflow-scroll">
<div v-if="events === null" class="d-flex h-100 justify-content-center align-items-center">
+8 -4
View File
@@ -16,7 +16,11 @@ export default {
id: String,
name: String,
inputClass: [String, Array, Object],
noList: Boolean
noList: Boolean,
accept: {
type: String,
default: ''
}
},
methods: {
stringifyFile(file) {
@@ -84,8 +88,8 @@ export default {
},
template: `
<div class="form-upload-dms">
<input ref="upload" class="form-control" :class="inputClass" :id="id" :name="name" :multiple="multiple" type="file" @change="addFiles">
<ul v-if="modelValue.length && multiple && !noList" class="list-unstyled m-0">
<input ref="upload" class="form-control" :accept="accept" :class="inputClass" :id="id" :name="name" :multiple="multiple" type="file" @change="addFiles">
<ul v-if="modelValue.length && multiple && !noList" :accept="accept" class="list-unstyled m-0">
<dms-item
v-for="(file, index) in modelValue"
:key="index"
@@ -96,4 +100,4 @@ export default {
</dms-item>
</ul>
</div>`
}
}
+13 -12
View File
@@ -43,7 +43,8 @@ export const CoreFilterCmpt = {
emits: [
'nwNewEntry',
'click:new',
'tableBuilt'
'tableBuilt',
'uuidDefined'
],
props: {
onNwNewEntry: Function, // NOTE(chris): Hack to get the nwNewEntry listener into $props
@@ -200,16 +201,16 @@ export const CoreFilterCmpt = {
}
// Define a default tabulator options in case it was not provided
let tabulatorOptions = {...{
height: 500,
layout: "fitDataStretch",
movableColumns: true,
columnDefaults:{
tooltip: true,
},
placeholder,
reactiveData: true,
persistence: true
}, ...(this.tabulatorOptions || {})};
height: 500,
layout: "fitDataStretch",
movableColumns: true,
columnDefaults:{
tooltip: true,
},
placeholder,
reactiveData: true,
persistence: true
}, ...(this.tabulatorOptions || {})};
if (!this.tableOnly) {
tabulatorOptions.data = this.filteredData;
@@ -573,6 +574,7 @@ export const CoreFilterCmpt = {
if (this.sideMenu && (!this.$props.onNwNewEntry || !(this.$props.onNwNewEntry instanceof Function)))
alert('"nwNewEntry" listener is mandatory when sideMenu is true');
this.uuid = _uuid++;
this.$emit('uuidDefined', this.uuid)
if (!this.tableOnly)
this.getFilter(); // get the filter data
},
@@ -664,4 +666,3 @@ export const CoreFilterCmpt = {
<div ref="table" :id="'filterTableDataset' + idExtra" class="filter-table-dataset"></div>
`
};
+19
View File
@@ -227,6 +227,25 @@ export default {
});
});
},
confirm(options) {
return new Promise((resolve, reject) => {
helperAppInstance.$confirm.require({
group: options?.group ?? 'fhcAlertConfirm',
header: options?.header ?? 'Achtung',
message: options?.message ?? '',
acceptLabel: options?.acceptLabel ?? 'Ok',
acceptClass: options?.acceptClass ?? 'btn btn-primary',
rejectLabel: options?.rejectLabel ?? 'Abbrechen',
rejectClass: options?.rejectClass ?? 'btn btn-outline-secondary',
accept() {
resolve(true);
},
reject() {
resolve(false);
},
});
});
},
alertDefault(severity, title, message, sticky = false) {
let options = { severity: severity, summary: title, detail: message};
+7 -7
View File
@@ -46,9 +46,9 @@ export default {
result.meta.response = response;
return result;
}
const baseURL = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + "/";
const fhcApiAxios = axios.create({
timeout: 5000,
timeout: 500000,
baseURL: FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + "/"
});
@@ -58,7 +58,7 @@ export default {
if (config.data instanceof FormData)
return config;
if (!Object.values(config.data).every(item => {
if (item instanceof FileList)
return false;
@@ -96,7 +96,7 @@ export default {
|| response.config?.errorHandling === false
|| response.config?.errorHandling == 'fail')
return _clean_return_value(response);
// NOTE(chris): loop through errors
if (response.data.errors)
response.data.errors = response.data.errors.filter(
@@ -107,7 +107,7 @@ export default {
}, error => {
if (error.code == 'ERR_CANCELED')
return Promise.reject({...{handled: true}, ...error});
if (error.config?.errorHandling == 'off'
|| error.config?.errorHandling === false
|| error.config?.errorHandling == 'success')
@@ -118,7 +118,7 @@ export default {
app.config.globalProperties.$fhcAlert.alertDefault('error', error.message, error.request.responseURL, true);
return Promise.reject({...{handled: true}, ...error});
}
// NOTE(chris): loop through errors
error.response.data.errors = error.response.data.errors.filter(
err => (error.config[err.type + 'ErrorHandler'] || app.config.globalProperties.$fhcApi._defaultErrorHandlers[err.type])(err, error.config)
@@ -132,7 +132,7 @@ export default {
app.config.globalProperties.$fhcAlert.alertError(error.message);
return Promise.reject({...{handled: true}, ...error});
}
return Promise.reject(error);
});
+1 -1
View File
@@ -374,4 +374,4 @@ if($neue==false)
echo '<br>Keine neuen Webservicerechte';
echo '</body></html>';
?>
?>
@@ -0,0 +1,16 @@
<?php
if (! defined('DB_NAME')) exit('No direct script access allowed');
// add app fhctemplate
if($result = $db->db_query("SELECT 1 FROM system.tbl_app WHERE app='anwesenheiten'"))
{
if($db->db_num_rows($result) === 0)
{
$qry = "INSERT INTO system.tbl_app (app) VALUES('anwesenheiten');";
if(!$db->db_query($qry))
echo '<strong>System Tabelle app: '.$db->db_last_error().'</strong><br>';
else
echo '<br>app anwesenheiten hinzugefuegt';
}
}
+262 -2
View File
@@ -31224,6 +31224,66 @@ array(
)
)
),
array(
'app' => 'anwesenheiten',
'category' => 'global',
'phrase' => 'notAuthorizedForLva',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Keine Berechtigung für die Lehrveranstaltung.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'No Authorization for that Course.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'anwesenheiten',
'category' => 'global',
'phrase' => 'notAuthorizedForLe',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Keine Berechtigung für die Lehreinheit.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'No Authorization for that Teaching Unit.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'anwesenheiten',
'category' => 'global',
'phrase' => 'noAuthorization',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Keine Berechtigung.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'No Authorization.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'anwesenheiten',
'category' => 'global',
@@ -31279,7 +31339,7 @@ array(
array(
'app' => 'anwesenheiten',
'category' => 'global',
'phrase' => 'tooltipStudentAssistenz',
'phrase' => 'tooltipAssistenz',
'insertvon' => 'system',
'phrases' => array(
array(
@@ -36856,6 +36916,206 @@ array(
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'global',
'phrase' => 'nichtAngemeldet',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Sie sind nicht angemeldet. Es wurde keine Benutzer UID gefunden',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'You are not logged in. No UID found',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'lvInfoBearbeiten',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Bearbeiten',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Edit',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'lehrveranstaltungsinformation',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Lehrveranstaltungsinformation',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Course Information',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'gesamtnote',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Gesamtnote',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Final Grade',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'noteneingabedeaktiviert',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Noteneingabe deaktiviert',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Grading disabled',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'keinMailverteiler',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Für die Gruppe(n) {nomail} existiert kein Verteiler! Die Studierenden in diesen Gruppen bekommen kein Mail.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'There is no e-mail distribution list for the group(s) {nomail}! The students in these groups will not receive an e-mail.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'mail',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'E-Mail an Studierende',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'E-Mail to students',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'abmelden',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Abmelden',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Unsubscribe',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'anrechnung',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Anrechnung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Exemption',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'anrechnungen',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Anrechnungen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Exemptions',
'description' => '',
'insertvon' => 'system'
)
)
)
// CIS4 phrases from legacy code end
);
@@ -36981,4 +37241,4 @@ foreach ($phrases as $phrase)
}
if(!$new)
echo '<b>Keine neuen Phrasen</b><br>';
echo '<b>Keine neuen Phrasen</b><br>';