mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-07-20 00:12:15 +00:00
Merge branch 'master' into feature-53599/Eingabe_von_Bankdaten_durch_Studierende
This commit is contained in:
@@ -5,3 +5,5 @@ if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
|
||||
// CMS Content Id for CIS4 Menu Root
|
||||
$config['cis_menu_root_content_id'] = 11066;
|
||||
// send Mails for ProfilUpdate
|
||||
$config['cis_send_profil_update_mails'] = true;
|
||||
|
||||
@@ -36,6 +36,7 @@ class ProfilUpdate extends Auth_Controller
|
||||
'getTopic' => ['basis/cis:r'],
|
||||
]);
|
||||
|
||||
$this->load->config('cis');
|
||||
|
||||
$this->load->model('person/Profil_update_model', 'ProfilUpdateModel');
|
||||
$this->load->model('person/Kontakt_model', 'KontaktModel');
|
||||
@@ -111,6 +112,10 @@ class ProfilUpdate extends Auth_Controller
|
||||
|
||||
private function sendEmail_onProfilUpdate_response($uid, $topic, $status)
|
||||
{
|
||||
if($this->config->item('cis_send_profil_update_mails') === false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->load->helper('hlp_sancho_helper');
|
||||
$email = $uid . "@" . DOMAIN;
|
||||
@@ -138,6 +143,10 @@ class ProfilUpdate extends Auth_Controller
|
||||
|
||||
private function sendEmail_onProfilUpdate_insertion($uid, $profil_update_id, $topic)
|
||||
{
|
||||
if($this->config->item('cis_send_profil_update_mails') === false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->load->helper('hlp_sancho_helper');
|
||||
$emails = [];
|
||||
|
||||
@@ -28,14 +28,12 @@ class Cis4 extends Auth_Controller
|
||||
public function index()
|
||||
{
|
||||
$this->load->model('person/Person_model','PersonModel');
|
||||
$begruesung = $this->PersonModel->getFirstName(getAuthUID());
|
||||
if(isError($begruesung))
|
||||
{
|
||||
show_error("name couldn't be loaded for username ".getAuthUID());
|
||||
}
|
||||
$begruesung = getData($begruesung);
|
||||
$personData = getData($this->PersonModel->getByUid(getAuthUID()))[0];
|
||||
|
||||
$viewData = array(
|
||||
'name' => $begruesung
|
||||
'uid' => getAuthUID(),
|
||||
'name' => $personData->vorname,
|
||||
'person_id' => $personData->person_id
|
||||
);
|
||||
|
||||
$this->load->view('CisVue/Dashboard.php',['viewData' => $viewData]);
|
||||
|
||||
@@ -125,7 +125,7 @@ class Cms extends FHCAPI_Controller
|
||||
|
||||
//get the data or terminate with error
|
||||
$news = $this->getDataOrTerminateWithError($news);
|
||||
|
||||
|
||||
// collect the content of the news
|
||||
foreach($news as $news_element){
|
||||
$this->addMeta("content_id",$news_element->content_id);
|
||||
@@ -134,12 +134,17 @@ class Cms extends FHCAPI_Controller
|
||||
$this->NewsModel->resetQuery();
|
||||
$content = $this->cmslib->getContent($news_element->content_id);
|
||||
|
||||
$content = $this->getDataOrTerminateWithError($content);
|
||||
$content = getData($content);
|
||||
|
||||
$news_element->content_obj = $content;
|
||||
}
|
||||
$withContent = function($news) {
|
||||
return $news->content_obj != null;
|
||||
};
|
||||
|
||||
$newsWithContent = array_filter($news, $withContent);
|
||||
|
||||
$this->terminateWithSuccess($news);
|
||||
$this->terminateWithSuccess($newsWithContent);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@ class Phrasen extends FHCAPI_Controller
|
||||
{
|
||||
parent::__construct([
|
||||
'loadModule' => self::PERM_ANONYMOUS,
|
||||
'setLanguage' => self::PERM_ANONYMOUS
|
||||
'setLanguage' => self::PERM_ANONYMOUS,
|
||||
'getLanguage' => self::PERM_ANONYMOUS,
|
||||
'getAllLanguages' => self::PERM_ANONYMOUS,
|
||||
]);
|
||||
|
||||
$this->load->helper('hlp_language');
|
||||
@@ -60,4 +62,23 @@ class Phrasen extends FHCAPI_Controller
|
||||
$phrases = $this->p->setPhrases($categories, $language);
|
||||
$this->terminateWithSuccess($phrases);
|
||||
}
|
||||
|
||||
// gets the langauge of the currently logged in user session and otherwhise the system language
|
||||
public function getLanguage()
|
||||
{
|
||||
$lang = getUserLanguage();
|
||||
$this->terminateWithSuccess($lang);
|
||||
}
|
||||
|
||||
// gets all languages that are set as active in the database
|
||||
public function getAllLanguages()
|
||||
{
|
||||
$langs = getDBActiveLanguages();
|
||||
$langs = $this->getDataOrTerminateWithError($langs);
|
||||
$langs = array_map(function($lang){
|
||||
return $lang->sprache;
|
||||
}, $langs);
|
||||
$this->terminateWithSuccess($langs);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -47,6 +47,8 @@ class ProfilUpdate extends FHCAPI_Controller
|
||||
'show' => self::PERM_LOGGED,
|
||||
]);
|
||||
|
||||
$this->load->config('cis');
|
||||
|
||||
// Load language phrases
|
||||
$this->loadPhrases(
|
||||
array(
|
||||
@@ -504,6 +506,10 @@ class ProfilUpdate extends FHCAPI_Controller
|
||||
|
||||
private function sendEmail_onProfilUpdate_insertion($uid, $profil_update_id, $topic)
|
||||
{
|
||||
if($this->config->item('cis_send_profil_update_mails') === false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->load->helper('hlp_sancho_helper');
|
||||
$emails = [];
|
||||
@@ -573,6 +579,11 @@ class ProfilUpdate extends FHCAPI_Controller
|
||||
|
||||
private function sendEmail_onProfilUpdate_response($uid, $topic, $status)
|
||||
{
|
||||
if($this->config->item('cis_send_profil_update_mails') === false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->load->helper('hlp_sancho_helper');
|
||||
$email = $uid . "@" . DOMAIN;
|
||||
|
||||
|
||||
@@ -151,7 +151,12 @@ class Stundenplan extends FHCAPI_Controller
|
||||
// getting the student_lehrverbaende of the student in the different studiensemester
|
||||
$student_lehrverband = $this->fetchStudentlehrverbandFromStudiensemester($semester_range);
|
||||
|
||||
$stundenplan_data = $this->StundenplanModel->stundenplanGruppierung($this->StundenplanModel->getStundenplanQuery($start_date, $end_date, $semester_range, $benutzer_gruppen, $student_lehrverband));
|
||||
$stundenplan_query = $this->StundenplanModel->getStundenplanQuery($start_date, $end_date, $semester_range, $benutzer_gruppen, $student_lehrverband);
|
||||
if(!$stundenplan_query)
|
||||
{
|
||||
$this->terminateWithSuccess([]);
|
||||
}
|
||||
$stundenplan_data = $this->StundenplanModel->stundenplanGruppierung($stundenplan_query);
|
||||
$stundenplan_data = $this->getDataOrTerminateWithError($stundenplan_data) ?? [];
|
||||
|
||||
$this->expand_object_information($stundenplan_data);
|
||||
|
||||
@@ -29,6 +29,7 @@ class Gruppenmanagement extends Auth_Controller
|
||||
$this->load->model('person/benutzer_model', 'BenutzerModel');
|
||||
$this->load->model('ressource/mitarbeiter_model', 'MitarbeiterModel');
|
||||
$this->load->model('person/benutzergruppe_model', 'BenutzergruppeModel');
|
||||
$this->load->model('person/gruppe_manager_model', 'GruppemanagerModel');
|
||||
$this->load->model('system/Log_model', 'LogModel');
|
||||
|
||||
$this->load->library('WidgetLib');
|
||||
@@ -117,6 +118,27 @@ class Gruppenmanagement extends Auth_Controller
|
||||
$result = error('Uid missing');
|
||||
else
|
||||
{
|
||||
$this->GruppemanagerModel->addSelect('1');
|
||||
$isManagerRes = $this->GruppemanagerModel->loadWhere(
|
||||
array(
|
||||
'uid' => $this->_uid,
|
||||
'gruppe_kurzbz' => $gruppe_kurzbz
|
||||
)
|
||||
);
|
||||
|
||||
if (isError($isManagerRes))
|
||||
{
|
||||
$this->outputJsonError(getError($isManagerRes));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasData($isManagerRes))
|
||||
{
|
||||
$this->outputJsonError($this->p->t('gruppenmanagement', 'nichtZumEditierenDerGruppeBerechtigt'));
|
||||
return;
|
||||
}
|
||||
|
||||
$this->BenutzergruppeModel->addSelect('1');
|
||||
$benutzerExistsRes = $this->BenutzergruppeModel->loadWhere(
|
||||
array(
|
||||
'uid' => $uid,
|
||||
@@ -170,6 +192,26 @@ class Gruppenmanagement extends Auth_Controller
|
||||
$result = error('Uid missing');
|
||||
else
|
||||
{
|
||||
$this->GruppemanagerModel->addSelect('1');
|
||||
$isManagerRes = $this->GruppemanagerModel->loadWhere(
|
||||
array(
|
||||
'uid' => $this->_uid,
|
||||
'gruppe_kurzbz' => $gruppe_kurzbz
|
||||
)
|
||||
);
|
||||
|
||||
if (isError($isManagerRes))
|
||||
{
|
||||
$this->outputJsonError(getError($isManagerRes));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasData($isManagerRes))
|
||||
{
|
||||
$this->outputJsonError($this->p->t('gruppenmanagement', 'nichtZumEditierenDerGruppeBerechtigt'));
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $this->BenutzergruppeModel->delete(
|
||||
array(
|
||||
'uid' => $uid,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
class Gruppe_manager_model extends DB_Model
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->dbTable = 'public.tbl_gruppe_manager';
|
||||
$this->pk = 'gruppe_manager_id';
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,7 @@ class Stundenplan_model extends DB_Model
|
||||
|
||||
|
||||
/**
|
||||
* function that takes a query that fetches lehre.vw_stundenplan rows and groups them so that they can be displayed in a calendar
|
||||
* groups rows of a subquery that fetches data from the lehre.vw_stundenplan table
|
||||
* @param string $stundenplanViewQuery the subquery used to group the result
|
||||
*
|
||||
* @return stdClass
|
||||
@@ -190,7 +190,7 @@ class Stundenplan_model extends DB_Model
|
||||
* NO STANDALONE FUNCTION - Generates a SQL query string to fetch 'stundenplan' events for a specific student within the current semester.
|
||||
* @param string $uid the user id that is used to fetch the stundenplan rows from the lehre.vw_stundenplan table
|
||||
*
|
||||
* @return string
|
||||
* @return mixed
|
||||
*/
|
||||
public function getStundenplanQuery($start_date, $end_date,$semester,$gruppen,$studentlehrverbaende){
|
||||
|
||||
@@ -206,7 +206,13 @@ class Stundenplan_model extends DB_Model
|
||||
}
|
||||
return $result;
|
||||
};
|
||||
|
||||
|
||||
// if both the gruppen and the studentlehrverbaende are empty we early return
|
||||
if($emptyCheck($gruppen) && $emptyCheck($studentlehrverbaende))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$query =
|
||||
"select sp.*
|
||||
from lehre.vw_stundenplan sp
|
||||
@@ -233,15 +239,15 @@ class Stundenplan_model extends DB_Model
|
||||
// converts the array of gruppen strings into a sql IN (_,_,_) chain
|
||||
$query .="(sp.gruppe_kurzbz IN (" .implode(',',$gruppen[$sem_date]).") AND sp.datum BETWEEN ".$this->escape($sem_date_range->start)." AND ".$this->escape($sem_date_range->ende)." )";
|
||||
|
||||
// adds the OR sql chain only if the $studentlehrverbaende array is not empty
|
||||
// DOES not include the sql OR if the $studentlehrverbaende are empty and it is the last gruppen element in the iteration
|
||||
if(key($semester) != $sem || !$emptyCheck($studentlehrverbaende))
|
||||
{
|
||||
$query .="OR";
|
||||
}
|
||||
|
||||
$query .="OR";
|
||||
}
|
||||
}
|
||||
|
||||
// if there are no studentlehrverbaende and the gruppen are not empty, we can remove the last OR added after the groups
|
||||
if($emptyCheck($studentlehrverbaende) && !$emptyCheck($gruppen))
|
||||
{
|
||||
$query = substr($query, 0, -2);
|
||||
}
|
||||
|
||||
foreach($semester as $sem=>$semester_date_range)
|
||||
{
|
||||
@@ -253,20 +259,24 @@ class Stundenplan_model extends DB_Model
|
||||
}
|
||||
foreach($studentlehrverbaende[$sem_date] as $key=>$lehrverband)
|
||||
{
|
||||
// adds the OR sql chain only if its not the first element in the first semester of the $studentlehrverbaende array
|
||||
if($sem != array_keys($semester)[0] || $key != array_keys($semester)[0])
|
||||
{
|
||||
$query .="OR";
|
||||
}
|
||||
$query .= "((sp.studiengang_kz = ".$this->escape($lehrverband->studiengang_kz)." AND sp.semester = ".$this->escape($lehrverband->semester)." AND sp.verband = ".$this->escape($lehrverband->verband)." AND sp.gruppe = ".$this->escape($lehrverband->gruppe)." AND sp.datum BETWEEN ".$this->escape($sem_date_range->start)." AND ".$this->escape($sem_date_range->ende).")";
|
||||
// Eintraege fuer den ganzen Verband
|
||||
$query .= "OR (sp.studiengang_kz = ".$this->escape($lehrverband->studiengang_kz)." AND sp.semester = ".$this->escape($lehrverband->semester)." AND sp.verband = ".$this->escape($lehrverband->verband)." AND (sp.gruppe is null OR sp.gruppe='') AND sp.datum BETWEEN ".$this->escape($sem_date_range->start)." AND ".$this->escape($sem_date_range->ende).")";
|
||||
// Eintraege fuer das ganze Semester
|
||||
$query .= "OR (sp.studiengang_kz = ".$this->escape($lehrverband->studiengang_kz)." AND sp.semester = ".$this->escape($lehrverband->semester)." AND (sp.verband is null OR sp.verband='') AND sp.datum BETWEEN ".$this->escape($sem_date_range->start)." AND ".$this->escape($sem_date_range->ende).") AND gruppe_kurzbz is null)";
|
||||
|
||||
$query .="OR";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the studentlehrverbaende is not empty we can remove the last OR that was added to the query
|
||||
if(!$emptyCheck($studentlehrverbaende))
|
||||
{
|
||||
$query = substr($query, 0, -2);
|
||||
}
|
||||
|
||||
// closes the AND sql chain only if it was opened previously
|
||||
if(!$emptyCheck($gruppen) || !$emptyCheck($studentlehrverbaende))
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
$STUDIENGANG_TYP = '\''.$this->variablelib->getVar('infocenter_studiensgangtyp').'\'';
|
||||
$TAETIGKEIT_KURZBZ = '\'bewerbung\', \'kommunikation\'';
|
||||
$LOGDATA_NAME = '\'Login with code\', \'Login with user\', \'Interessent rejected\', \'Attempt to register with existing mailadress\', \'Access code sent\', \'Personal data saved\'';
|
||||
$LOGDATA_DELETED_BY_USER = '\'% deleted by user\'';
|
||||
$POSTPONE_STATUS_PARKED = '\'parked\'';
|
||||
$STATUS_KURZBZ = '\'Wartender\', \'Bewerber\', \'Aufgenommener\', \'Student\'';
|
||||
$ADDITIONAL_STG = $this->config->item('infocenter_studiengang_kz');
|
||||
@@ -283,6 +284,7 @@
|
||||
FROM system.tbl_log l
|
||||
WHERE l.taetigkeit_kurzbz IN ('.$TAETIGKEIT_KURZBZ.')
|
||||
AND l.logdata->>\'name\' NOT IN ('.$LOGDATA_NAME.')
|
||||
AND l.logdata->>\'message\' NOT LIKE ('.$LOGDATA_DELETED_BY_USER.')
|
||||
AND l.person_id = p.person_id
|
||||
ORDER BY l.log_id DESC
|
||||
LIMIT 1
|
||||
|
||||
@@ -29,6 +29,9 @@ define('CIS_LEHRVERANSTALTUNG_GESAMTNOTE_ANZEIGEN', true);
|
||||
define('CIS_LEHRVERANSTALTUNG_ANRECHNUNG_ANZEIGEN', true);
|
||||
define('CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN', true);
|
||||
|
||||
// Wenn gesetzt, werden die Digitale Anwesenheit-Icons nur fuer diese Studiengaenge angezeigt, sonst für alle
|
||||
// define('CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN_STG', serialize(array('257')));
|
||||
|
||||
// Im CIS Menue Links bei Modulen anzeigen wenn Lehrauftrag
|
||||
define('CIS_LEHRVERANSTALTUNG_MODULE_LINK',true);
|
||||
|
||||
|
||||
@@ -208,6 +208,7 @@ else
|
||||
<menupopup id="student-filter-menu-popup" >
|
||||
<menuitem id="student-toolbar-filter-dokumente" label="fehlende Dokumente" oncommand="InteressentDokumenteFilter()" disabled="false" tooltiptext="Liste aller Studenten mit Fehlenden Dokumenten"/>
|
||||
<menuitem id="student-toolbar-filter-offenebuchungen" label="offene Buchungen" oncommand="StudentKontoFilterStudenten('konto')" disabled="false" tooltiptext="Liste aller Studenten mit offenen Buchungen"/>
|
||||
<menuitem id="student-toolbar-filter-ueberfaelligebuchungen" label="überfällige Buchungen" oncommand="StudentKontoFilterStudenten('ueberfaelligebuchungen')" disabled="false" tooltiptext="Liste aller Studenten mit überfälligen Buchungen"/>
|
||||
<menuitem id="student-toolbar-filter-studiengebuehr" label="nicht gebuchte Studiengebuehr" oncommand="StudentKontoFilterStudenten('studiengebuehr')" disabled="false" tooltiptext="Liste aller Studenten die noch nicht mit der Studiengebühr belastet wurden" />
|
||||
<menuitem id="student-toolbar-filter-erhoehte-studiengebuehr" label="erhöhten Studienbeitrag" oncommand="StudentKontoFilterStudenten('StudiengebuehrErhoeht')" disabled="false" tooltiptext="Liste aller Studenten denen der erhöhte Studienbeitrag eingebucht wurde" />
|
||||
<menuitem id="student-toolbar-filter-zgvohnedatum" label="ZGV eingetragen ohne Datum" oncommand="StudentKontoFilterStudenten('zgvohnedatum')" disabled="false" tooltiptext="Liste aller Studenten die ZGV eingetragen haben bei denen aber kein ZGV Datum gesetzt ist" />
|
||||
|
||||
@@ -242,12 +242,12 @@ function checkZeilenUmbruch()
|
||||
|
||||
// Digitale Anwesenheiten
|
||||
if(defined('CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN') && CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN && $angemeldet
|
||||
&& (!defined('CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN_STG') || in_array($lv->studiengang_kz, unserialize(CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN_STG)))
|
||||
&& ($rechte->isBerechtigt('extension/anw_ent_admin')
|
||||
|| $rechte->isBerechtigt('extension/anwesenheit_lektor')
|
||||
|| $rechte->isBerechtigt('extension/anwesenheit_student')
|
||||
|| $rechte->isBerechtigt('extension/anwesenheit_admin')))
|
||||
{
|
||||
|
||||
$link='';
|
||||
$text='';
|
||||
|
||||
|
||||
+39
-1
@@ -182,6 +182,10 @@ html {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
#nav-sprachen{
|
||||
transition: none;
|
||||
}
|
||||
|
||||
/* searchbar */
|
||||
#nav-search {
|
||||
z-index: 1;
|
||||
@@ -478,15 +482,28 @@ html {
|
||||
}
|
||||
|
||||
.fhc-entry:hover{
|
||||
background-color:#0088d6 !important;
|
||||
background-color:#005585 !important;
|
||||
color:white !important;
|
||||
}
|
||||
|
||||
.fhc-entry.btn:focus {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.fhc-entry.btn {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
.fhc-entry {
|
||||
transition-property: background,color;
|
||||
transition-duration: 0.3s,0.2s;
|
||||
transition-timing-function: ease-out,ease-out;
|
||||
}
|
||||
|
||||
[selected].fhc-entry {
|
||||
background-color: #00649C !important;
|
||||
}
|
||||
|
||||
@media screen and ( max-width: 767px ) {
|
||||
#nav-search {
|
||||
position: static;
|
||||
@@ -527,4 +544,25 @@ html {
|
||||
padding-left: 2.5rem;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
|
||||
/* classes used for the Vue <Transition> component*/
|
||||
.v-enter-active,
|
||||
.v-leave-active {
|
||||
transition: opacity 0.2s ease-out;
|
||||
}
|
||||
|
||||
.v-enter-from,
|
||||
.v-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.height-enter-active,
|
||||
.height-leave-active {
|
||||
transition: height 0.3s ease-out;
|
||||
}
|
||||
|
||||
.height-enter-from,
|
||||
.height-leave-to {
|
||||
height: 0px;
|
||||
}
|
||||
@@ -1,27 +1,72 @@
|
||||
.widgets-news .card-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start !important;
|
||||
|
||||
}
|
||||
|
||||
:root{
|
||||
--news-widget-height: 1;
|
||||
}
|
||||
|
||||
.widgets-news .news-content > div,
|
||||
.widgets-news .news-content .row:nth-child(1),
|
||||
.widgets-news .news-content .news-list,
|
||||
.widgets-news .news-content .news-list-item,
|
||||
.widgets-news .news-content .card-body
|
||||
{
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.widgets-news img
|
||||
{
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.widgets-news .card-body{
|
||||
overflow: hidden;
|
||||
}
|
||||
.fhc-news-menu-item {
|
||||
padding: 0.375rem;
|
||||
color: var(--fhc-cis-menu-lvl-1-color);
|
||||
min-height: 5%;
|
||||
max-height: 30%;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #00649c;
|
||||
border: 1px solid #f1f1f1;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, border-color 0.2s ease; /* Smooth transition */
|
||||
}
|
||||
|
||||
.fhc-news-menu-item:hover {
|
||||
background-color: var(--fhc-cis-menu-lvl-1-bg-hover);
|
||||
border-color: #f1f1f1;
|
||||
}
|
||||
|
||||
.fhc-news-menu-item:active {
|
||||
background-color: var(--fhc-cis-menu-lvl-1-color-hover);
|
||||
border-color: #f1f1f1;
|
||||
}
|
||||
|
||||
.fhc-news-menu-item.selected {
|
||||
background-color: var(--fhc-cis-menu-lvl-1-bg-hover);
|
||||
border-right: 2px solid #fff;
|
||||
|
||||
}
|
||||
|
||||
.fhc-news-menu-item:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.fhc-news-menu-item-betreff
|
||||
{
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.fhc-news-menu-item-date {
|
||||
text-align: end;
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.fhc-carousel .carousel-item {
|
||||
transition: transform 0.375s ease-in-out, opacity 0.75s ease-in-out;
|
||||
}
|
||||
|
||||
.fhc-carousel .carousel-item-next,
|
||||
.fhc-carousel .carousel-item-prev {
|
||||
transition: transform 0.44s ease-in-out, opacity 0.8s ease-in-out;
|
||||
}
|
||||
|
||||
.fhc-carousel .carousel-item-start,
|
||||
.fhc-carousel .carousel-item-end {
|
||||
transition: transform 0.44s ease-in-out, opacity 0.8s ease-in-out;
|
||||
}
|
||||
|
||||
@@ -22,5 +22,11 @@ export default {
|
||||
setLanguage(categories,language) {
|
||||
const payload = {categories, language}
|
||||
return this.$fhcApi.post('/api/frontend/v1/phrasen/setLanguage', payload);
|
||||
},
|
||||
getLanguage() {
|
||||
return this.$fhcApi.get('/api/frontend/v1/phrasen/getLanguage', {});
|
||||
},
|
||||
getActiveDbLanguages() {
|
||||
return this.$fhcApi.get('/api/frontend/v1/phrasen/getAllLanguages', {});
|
||||
}
|
||||
};
|
||||
@@ -2,6 +2,8 @@ import FhcCalendar from "../../components/Calendar/Calendar.js";
|
||||
import Phrasen from "../../plugin/Phrasen.js";
|
||||
import CalendarDate from "../../composables/CalendarDate.js";
|
||||
import LvModal from "../../components/Cis/Mylv/LvModal.js";
|
||||
import LvInfo from "../../components/Cis/Mylv/LvInfo.js"
|
||||
import LvMenu from "../../components/Cis/Mylv/LvMenu.js"
|
||||
|
||||
|
||||
const app = Vue.createApp({
|
||||
@@ -17,7 +19,7 @@ const app = Vue.createApp({
|
||||
}
|
||||
},
|
||||
components: {
|
||||
FhcCalendar, LvModal
|
||||
FhcCalendar, LvModal, LvMenu, LvInfo
|
||||
},
|
||||
computed:{
|
||||
weekFirstDay: function () {
|
||||
@@ -35,6 +37,9 @@ const app = Vue.createApp({
|
||||
|
||||
},
|
||||
methods:{
|
||||
setSelectedEvent: function (event) {
|
||||
this.currentlySelectedEvent = event;
|
||||
},
|
||||
getLvID: function () {
|
||||
this.lv_id = window.location.pathname
|
||||
},
|
||||
@@ -111,7 +116,7 @@ const app = Vue.createApp({
|
||||
<h2>{{$p.t('lehre/stundenplan')}}</h2>
|
||||
<hr>
|
||||
<lv-modal v-if="currentlySelectedEvent" :event="currentlySelectedEvent" ref="lvmodal" />
|
||||
<fhc-calendar :initial-date="currentDay" @change:range="updateRange" :events="events" initial-mode="week" show-weeks @select:day="selectDay" v-model:minimized="minimized">
|
||||
<fhc-calendar @selectedEvent="setSelectedEvent" :initial-date="currentDay" @change:range="updateRange" :events="events" initial-mode="week" show-weeks @select:day="selectDay" v-model:minimized="minimized">
|
||||
<template #monthPage="{event,day,isSelected}">
|
||||
<span class="fhc-entry" :class="{'selectedEvent':isSelected}" style="color:white" :style="{'background-color': event.color}">
|
||||
{{event.topic}}
|
||||
@@ -124,8 +129,8 @@ const app = Vue.createApp({
|
||||
<span>{{event?.orig.ort_kurzbz}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #dayPage="{event,day}">
|
||||
<div type="button" class="fhc-entry border border-secondary border row h-100 justify-content-center align-items-center text-center">
|
||||
<template #dayPage="{event,day,mobile}">
|
||||
<div @click="mobile? showModal(event?.orig):null" type="button" class="fhc-entry border border-secondary border row m-0 h-100 justify-content-center align-items-center text-center">
|
||||
<div class="col ">
|
||||
<p>Lehrveranstaltung:</p>
|
||||
<p class="m-0">{{event?.orig.topic}}</p>
|
||||
@@ -140,6 +145,17 @@ const app = Vue.createApp({
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #pageMobilContent="{lvMenu}">
|
||||
<h3 >{{$p.t('lvinfo','lehrveranstaltungsinformationen')}}</h3>
|
||||
<div class="w-100">
|
||||
<lv-info :event="currentlySelectedEvent" />
|
||||
</div>
|
||||
<h3 >Lehrveranstaltungs Menu</h3>
|
||||
<lv-menu :containerStyles="['p-0']" :rowStyles="['m-0']" v-show="lvMenu" :menu="lvMenu" />
|
||||
</template>
|
||||
<template #pageMobilContentEmpty >
|
||||
<h3>Keine Lehrveranstaltungen</h3>
|
||||
</template>
|
||||
</fhc-calendar>
|
||||
`
|
||||
});
|
||||
|
||||
@@ -66,6 +66,12 @@ export default {
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
selectedEvent:{
|
||||
handler(newSelectedEvent) {
|
||||
this.$emit('selectedEvent', newSelectedEvent);
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
// scroll to the first event if the html element was found
|
||||
scrollTime({focusDate,scrollTime}){
|
||||
// return early if the scrollTime is not set
|
||||
@@ -89,7 +95,8 @@ export default {
|
||||
'select:day',
|
||||
'select:event',
|
||||
'change:range',
|
||||
'update:minimized'
|
||||
'update:minimized',
|
||||
'selectedEvent'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
@@ -223,8 +230,14 @@ export default {
|
||||
<template #weekPage="{event,day,isSelected}">
|
||||
<slot name="weekPage" :event="event" :day="day" :isSelected="isSelected"></slot>
|
||||
</template>
|
||||
<template #dayPage="{event,day}">
|
||||
<slot name="dayPage" :event="event" :day="day"></slot>
|
||||
<template #dayPage="{event,day,mobile}">
|
||||
<slot name="dayPage" :event="event" :day="day" :mobile="mobile"></slot>
|
||||
</template>
|
||||
<template #pageMobilContent="{lvMenu}">
|
||||
<slot name="pageMobilContent" :lvMenu="lvMenu"></slot>
|
||||
</template>
|
||||
<template #pageMobilContentEmpty>
|
||||
<slot name="pageMobilContentEmpty" ></slot>
|
||||
</template>
|
||||
<template #minimizedPage="{event,day}">
|
||||
<slot name="minimizedPage" :event="event" :day="day"></slot>
|
||||
|
||||
@@ -43,8 +43,14 @@ export default {
|
||||
<calendar-header :title="title" @prev="prev" @next="next" @updateMode="$emit('updateMode', $event)" @click="$emit('updateMode', 'week')"/>
|
||||
<calendar-pane ref="pane" v-slot="slot" @slid="paneChanged">
|
||||
<calendar-day-page :active="slot.active" :year="focusDate.y" :week="focusDate.w+slot.offset" @updateMode="$emit('updateMode', $event)" @page:back="prev" @page:forward="next" @input="selectEvent" >
|
||||
<template #dayPage="{event,day}">
|
||||
<slot name="dayPage" :event="event" :day="day" ></slot>
|
||||
<template #dayPage="{event,day,mobile}">
|
||||
<slot name="dayPage" :event="event" :day="day" :mobile="mobile" ></slot>
|
||||
</template>
|
||||
<template #pageMobilContent="{lvMenu}">
|
||||
<slot name="pageMobilContent" :lvMenu="lvMenu" ></slot>
|
||||
</template>
|
||||
<template #pageMobilContentEmpty>
|
||||
<slot name="pageMobilContentEmpty" ></slot>
|
||||
</template>
|
||||
</calendar-day-page>
|
||||
</calendar-pane>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import CalendarDate from '../../../composables/CalendarDate.js';
|
||||
import LvMenu from "../../../components/Cis/Mylv/LvMenu.js"
|
||||
import LvInfo from "../../../components/Cis/Mylv/LvInfo.js"
|
||||
import LvModal from "../../../components/Cis/Mylv/LvModal.js";
|
||||
|
||||
function ggt(m, n) { return n == 0 ? m : ggt(n, m % n); }
|
||||
@@ -8,8 +6,6 @@ function kgv(m, n) { return (m * n) / ggt(m, n); }
|
||||
|
||||
export default {
|
||||
components:{
|
||||
LvMenu,
|
||||
LvInfo,
|
||||
LvModal,
|
||||
},
|
||||
data() {
|
||||
@@ -79,9 +75,26 @@ export default {
|
||||
this.fetchLvMenu(event);
|
||||
},
|
||||
immediate:true,
|
||||
},
|
||||
isSliding:{
|
||||
handler(value){
|
||||
if(value)
|
||||
{
|
||||
this.setSelectedEvent(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pageHeaderStyle(){
|
||||
return {
|
||||
'z-index': 4,
|
||||
'grid-template-columns': 'repeat(' + this.day.length + ', 1fr)',
|
||||
'grid-template-rows': 1,
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
}
|
||||
},
|
||||
dayGridStyle(){
|
||||
return {
|
||||
'grid-template-columns': '1 1fr',
|
||||
@@ -191,12 +204,16 @@ export default {
|
||||
'z-index': 0,
|
||||
}
|
||||
},
|
||||
showModal: function (evt) {
|
||||
let event = evt.orig;
|
||||
this.setSelectedEvent(event);
|
||||
Vue.nextTick(() => {
|
||||
this.$refs.lvmodal.show();
|
||||
});
|
||||
eventGridStyle(day, event) {
|
||||
return {
|
||||
'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': this.dateToMinutesOfDay(event.start),
|
||||
'grid-row-end': this.dateToMinutesOfDay(event.end),
|
||||
'background-color': event.orig.color,
|
||||
'--test': this.dateToMinutesOfDay(event.end),
|
||||
}
|
||||
},
|
||||
eventClick(evt) {
|
||||
let event = evt.orig;
|
||||
@@ -258,36 +275,26 @@ export default {
|
||||
dateToMinutesOfDay(day) {
|
||||
return Math.floor(((day.getHours() - 7) * 60 + day.getMinutes()) / this.smallestTimeFrame) + 1;
|
||||
},
|
||||
eventGridStyle(day,event){
|
||||
return {
|
||||
'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': this.dateToMinutesOfDay(event.start),
|
||||
'grid-row-end': this.dateToMinutesOfDay(event.end),
|
||||
'background-color': event.orig.color,
|
||||
'--test': this.dateToMinutesOfDay(event.end),
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
template: /*html*/`
|
||||
<div class="fhc-calendar-day-page ">
|
||||
<!-- lvModal for mobile view -->
|
||||
<lv-modal v-if="selectedEvent" :event="selectedEvent" ref="lvmodal" />
|
||||
<div class="row m-0">
|
||||
<div class="col-12 col-xl-6 p-0">
|
||||
<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(' + day.length + ', 1fr)', 'grid-template-rows':1}" style="position:sticky; top:0; " >
|
||||
<div class="fhc-calendar-week-page-header d-grid border-2 border-bottom text-center" :style="pageHeaderStyle" >
|
||||
<div type="button" class="flex-grow-1" :title="day.toLocaleString(undefined, {dateStyle:'short'})" @click.prevent="changeToMonth(day)">
|
||||
<div class="fw-bold">{{day.toLocaleString(undefined, {weekday: size < 2 ? 'narrow' : (size < 3 ? 'short' : 'long')})}}</div>
|
||||
<a href="#" class="small text-secondary text-decoration-none" >{{day.toLocaleString(undefined, [{day:'numeric',month:'numeric'},{day:'numeric',month:'numeric'},{day:'numeric',month:'numeric'},{dateStyle:'short'}][this.size])}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="eventcontainer" class="position-relative flex-grow-1" @mousemove="calcHourPosition" @mouseleave="" >
|
||||
<div ref="eventcontainer" class="position-relative flex-grow-1" @mousemove="calcHourPosition" @mouseleave="hourPosition = null" >
|
||||
<div :id="hourGridIdentifier(hour)" v-for="hour in hours" :key="hour" class="position-absolute box-shadow-border-top" :style="hourGridStyle(hour)"></div>
|
||||
<div v-if="hourPosition" class="position-absolute border-top small" :style="indicatorStyle">
|
||||
<span class="border border-top-0 px-2 bg-white">{{hourPositionTime}}</span>
|
||||
</div>
|
||||
<Transition>
|
||||
<div v-if="hourPosition" class="position-absolute border-top small" :style="indicatorStyle">
|
||||
<span class="border border-top-0 px-2 bg-white">{{hourPositionTime}}</span>
|
||||
</div>
|
||||
</Transition>
|
||||
<div>
|
||||
<h1 v-if="noEventsCondition" class="m-0 text-secondary" ref="noEventsText" :style="noLvStyle">Keine Lehrveranstaltungen</h1>
|
||||
<div :class="{'fhc-calendar-no-events-overlay':noEventsCondition, 'events':true}">
|
||||
@@ -297,16 +304,16 @@ export default {
|
||||
</div>
|
||||
<div v-for="day in eventsPerDayAndHour" :key="day" class=" day border-start" :style="dayGridStyle">
|
||||
<div v-for="event in day.events" :key="event" :style="eventGridStyle(day,event)" :class="{'selectedEvent':event.orig == selectedEvent}" class="mx-2 small rounded overflow-hidden " >
|
||||
<!-- desktop version opens the lvMenu next to the calendar -->
|
||||
<!-- desktop version of the page template, parent receives slotProp mobile = false -->
|
||||
<div class="d-none d-xl-block h-100 " @click.prevent="eventClick(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 name="dayPage" :event="event" :day="day" :mobile="false">
|
||||
<p>this is a slot placeholder</p>
|
||||
</slot>
|
||||
</div>
|
||||
<!-- mobile version opens the lvModal in a modal -->
|
||||
<div class="d-block d-xl-none h-100" @click.prevent="showModal(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>
|
||||
<!-- mobile version of the page template, parent receives slotProp mobile = true -->
|
||||
<div class="d-block d-xl-none h-100" @click.prevent="eventClick(event)">
|
||||
<slot name="dayPage" :event="event" :day="day" :mobile="true">
|
||||
<p>this is a slot placeholder</p>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
@@ -318,18 +325,17 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-xl-block col-xl-6 p-0">
|
||||
<div class="p-5 sticky-top d-flex justify-content-center align-items-center flex-column">
|
||||
<div style="z-index:0" class="p-5 sticky-top d-flex justify-content-center align-items-center flex-column">
|
||||
<div style="max-height: calc(var(--fhc-calendar-pane-height) - 100px); overflow-y:auto;" class="w-100">
|
||||
<template v-if="selectedEvent && lvMenu">
|
||||
<h3 >{{$p.t('lvinfo','lehrveranstaltungsinformationen')}}</h3>
|
||||
<div class="w-100">
|
||||
<lv-info :event="selectedEvent" />
|
||||
</div>
|
||||
<h3 >Lehrveranstaltungs Menu</h3>
|
||||
<lv-menu :containerStyles="['p-0']" :rowStyles="['m-0']" v-show="lvMenu" :menu="lvMenu" />
|
||||
<slot name="pageMobilContent" :lvMenu="lvMenu" >
|
||||
<p>this is a slot placeholder</p>
|
||||
</slot>
|
||||
</template>
|
||||
<template v-else-if="noEventsCondition">
|
||||
<h3>Keine Lehrveranstaltungen</h3>
|
||||
<slot name="pageMobilContentEmpty" >
|
||||
<h3>This is an slot placeholder</h3>
|
||||
</slot>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="p-4 d-flex w-100 justify-content-center align-items-center">
|
||||
|
||||
@@ -31,6 +31,27 @@ export default {
|
||||
'input',
|
||||
],
|
||||
computed: {
|
||||
pageHeaderStyle(){
|
||||
return {
|
||||
'z-index': 4,
|
||||
'grid-template-columns': 'repeat(' + this.days.length + ', 1fr)',
|
||||
'grid-template-rows': 1,
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
}
|
||||
},
|
||||
indicatorStyle() {
|
||||
return {
|
||||
'pointer-events': 'none',
|
||||
'padding-left': '3.5rem',
|
||||
'margin-top': '-1px',
|
||||
'z-index': 2,
|
||||
'border-color': '#00649C!important',
|
||||
top: this.hourPosition + 'px',
|
||||
left: 0,
|
||||
right: 0,
|
||||
}
|
||||
},
|
||||
hours(){
|
||||
// returns an array with elements starting at 7 and ending at 24
|
||||
return [...Array(24).keys()].filter(hour => hour >= 7 && hour <= 24);
|
||||
@@ -88,6 +109,36 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hourGridIdentifier(hour) {
|
||||
// this is the id attribute that is responsible to scroll the calender to the first event
|
||||
return 'scroll' + hour + this.focusDate.d + this.week;
|
||||
},
|
||||
hourGridStyle(hour) {
|
||||
return {
|
||||
'pointer-events': 'none',
|
||||
top: this.getAbsolutePositionForHour(hour),
|
||||
left: 0,
|
||||
right: 0,
|
||||
'z-index': 0,
|
||||
}
|
||||
},
|
||||
dayGridStyle(day) {
|
||||
return {
|
||||
'grid-template-columns': 'repeat(' + day.lanes + ', 1fr)',
|
||||
'grid-template-rows': 'repeat(' + (this.hours.length * 60 / this.smallestTimeFrame) + ', 1fr)',
|
||||
}
|
||||
},
|
||||
eventGridStyle(day, event) {
|
||||
return {
|
||||
'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': this.dateToMinutesOfDay(event.start),
|
||||
'grid-row-end': this.dateToMinutesOfDay(event.end),
|
||||
'background-color': event.orig.color,
|
||||
'--test': this.dateToMinutesOfDay(event.end),
|
||||
}
|
||||
},
|
||||
calcHourPosition(event) {
|
||||
let height = this.$refs.eventcontainer.getBoundingClientRect().height;
|
||||
let top = this.$refs.eventcontainer.getBoundingClientRect().top;
|
||||
@@ -155,23 +206,25 @@ export default {
|
||||
<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 class="fhc-calendar-week-page-header d-grid border-2 border-bottom text-center" :style="pageHeaderStyle" >
|
||||
<div type="button" v-for="day in days" :key="day" class="flex-grow-1" :title="day.toLocaleString(undefined, {dateStyle:'short'})" @click.prevent="changeToMonth(day)">
|
||||
<div class="fw-bold">{{day.toLocaleString(undefined, {weekday: size < 2 ? 'narrow' : (size < 3 ? 'short' : 'long')})}}</div>
|
||||
<a href="#" class="small text-secondary text-decoration-none" >{{day.toLocaleString(undefined, [{day:'numeric',month:'numeric'},{day:'numeric',month:'numeric'},{day:'numeric',month:'numeric'},{dateStyle:'short'}][this.size])}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="eventcontainer" class="position-relative flex-grow-1" @mousemove="calcHourPosition" @mouseleave="" >
|
||||
<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 ref="eventcontainer" class="position-relative flex-grow-1" @mousemove="calcHourPosition" @mouseleave="hourPosition = null" >
|
||||
<div :id="hourGridIdentifier(hour)" v-for="hour in hours" :key="hour" class="position-absolute box-shadow-border-top" :style="hourGridStyle(hour)"></div>
|
||||
<Transition>
|
||||
<div v-if="hourPosition" class="position-absolute border-top small" :style="indicatorStyle">
|
||||
<span class="border border-top-0 px-2 bg-white">{{hourPositionTime}}</span>
|
||||
</div>
|
||||
</Transition>
|
||||
<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': 'repeat(' + day.lanes + ', 1fr)', 'grid-template-rows': 'repeat(' + (hours.length * 60 / smallestTimeFrame) + ', 1fr)'}">
|
||||
<div :style="{'background-color':event.orig.color}" class="mx-2 small rounded overflow-hidden " @click.prevent="weekPageClick(event.orig, day)" :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">
|
||||
<div v-for="day in eventsPerDayAndHour" :key="day" class=" day border-start" :style="dayGridStyle(day)">
|
||||
<div v-for="event in day.events" :key="event" @click.prevent="weekPageClick(event.orig, day)" :style="eventGridStyle(day,event)" class="mx-2 small rounded overflow-hidden " >
|
||||
<slot name="weekPage" :event="event" :day="day" :isSelected="event.orig == selectedEvent" >
|
||||
<p>this is a placeholder which means that no template was passed to the Calendar Page slot</p>
|
||||
</slot>
|
||||
|
||||
@@ -53,7 +53,7 @@ export default {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<!-- div that contains the content -->
|
||||
<component :is="computeContentType" v-if="content" :content="content" />
|
||||
<component :is="computeContentType" v-if="content" :content="content" :content_id="content_id" />
|
||||
<p v-else>No content is available to display</p>
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ export default {
|
||||
RaumContent,
|
||||
},
|
||||
props:{
|
||||
contentID:{
|
||||
content_id:{
|
||||
type: Number
|
||||
},
|
||||
ort_kurzbz:{
|
||||
@@ -34,8 +34,8 @@ export default {
|
||||
// this method is always called when the modal is shown
|
||||
modalShown: function(){
|
||||
|
||||
if(this.contentID){
|
||||
this.$fhcApi.factory.cms.content(this.contentID).then(res =>{
|
||||
if(this.content_id){
|
||||
this.$fhcApi.factory.cms.content(this.content_id).then(res =>{
|
||||
this.content = res.data.content;
|
||||
this.type = res.data.type;
|
||||
|
||||
@@ -55,7 +55,7 @@ export default {
|
||||
<span v-else>Raum Informationen</span>
|
||||
</template>
|
||||
<template #default>
|
||||
<RaumContent v-if="content" :content="content"></RaumContent>
|
||||
<RaumContent v-if="content" :content="content" :content_id="content_id"></RaumContent>
|
||||
<div v-else>Der Content für diesen Raum konnte nicht geladen werden</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
|
||||
@@ -5,6 +5,9 @@ export default {
|
||||
type:String,
|
||||
required:true,
|
||||
},
|
||||
content_id:{
|
||||
type:Number,
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
// replaces the tablesorter with the tabulator
|
||||
@@ -21,6 +24,34 @@ export default {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// tries to wrap the Raum titel with a link tag that redirects to the Reservierungen of that Raum
|
||||
let title = document.getElementsByTagName("h1");
|
||||
title = title.length ? title[0] : null;
|
||||
if (title)
|
||||
{
|
||||
let room_name = title.innerText;
|
||||
let room_name_reg_exp = new RegExp("\\w*\\s([a-zA-Z][0-9\\.]+)$");
|
||||
let room_name_reg_exp_result = room_name.match(room_name_reg_exp);
|
||||
if(room_name_reg_exp_result)
|
||||
{
|
||||
room_name = room_name_reg_exp_result[0];
|
||||
room_name = room_name.replace(" ","_");
|
||||
let link_element = document.createElement("a");
|
||||
link_element.href = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + "/CisVue/Cms/getRoomInformation/" + room_name;
|
||||
link_element.appendChild(title.cloneNode(true));
|
||||
title.replaceWith(link_element);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.error(`the regular expression did not match the room name: ${room_name}`);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
console.error(`was not able to get the title of the raum_contentmittitel by searching for the first h1 element`);
|
||||
}
|
||||
},
|
||||
template: /*html*/ `
|
||||
<!-- div that contains the content -->
|
||||
|
||||
@@ -37,7 +37,8 @@ export default {
|
||||
template: /*html*/ `
|
||||
<h2 >News</h2>
|
||||
<hr/>
|
||||
<pagination :page_size="page_size" @page="loadNewPageContent" :maxPageCount="maxPageCount">
|
||||
<pagination :page_size="page_size" @page="loadNewPageContent" :maxPageCount="maxPageCount">
|
||||
</pagination>
|
||||
<div v-html="content"></div>
|
||||
</pagination>`,
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import CisMenuEntry from "./Menu/Entry.js";
|
||||
import FhcSearchbar from "../searchbar/searchbar.js";
|
||||
import CisSprachen from "./Sprachen.js"
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CisMenuEntry,
|
||||
FhcSearchbar
|
||||
FhcSearchbar,
|
||||
CisSprachen,
|
||||
},
|
||||
props: {
|
||||
menu: Array,
|
||||
@@ -49,34 +51,18 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getLanguageButtonClass(lang) {
|
||||
let classString = 'btn btn-level-2 rounded-0'
|
||||
const langCookie = (function(lang) {
|
||||
const cookieString = document.cookie;
|
||||
const cookies = cookieString.split('; ');
|
||||
|
||||
for (let cookie of cookies) {
|
||||
const [key, value] = cookie.split('=');
|
||||
if (key === lang) {
|
||||
return decodeURIComponent(value);
|
||||
}
|
||||
}
|
||||
|
||||
return null; // Return null if the cookie is not found
|
||||
})('sprache');
|
||||
if(langCookie === lang) classString += ' fhc-active';
|
||||
return classString
|
||||
},
|
||||
toggleCollapsibles(target){
|
||||
switch(target){
|
||||
case 'settings':
|
||||
this.navUserDropdown?.hide();
|
||||
break;
|
||||
case 'navUserDropdown':
|
||||
this.$refs.searchbar?.settingsDropdown?.hide();
|
||||
break;
|
||||
checkSettingsVisibility: function (event) {
|
||||
// hides the settings collapsible if the user clicks somewhere else
|
||||
if (!this.$refs.navUserDropdown.contains(event.target)) {
|
||||
this.navUserDropdown.hide();
|
||||
}
|
||||
},
|
||||
handleShowNavUser(){
|
||||
document.addEventListener("click", this.checkSettingsVisibility);
|
||||
},
|
||||
handleHideNavUser(){
|
||||
document.removeEventListener("click", this.checkSettingsVisibility);
|
||||
},
|
||||
makeParentContentActive(content_id, collection=this.entries, parent=null){
|
||||
for(let entry of collection){
|
||||
if(entry.content_id == content_id){
|
||||
@@ -93,20 +79,6 @@ export default {
|
||||
setActiveEntry(content_id){
|
||||
this.activeEntry = content_id;
|
||||
},
|
||||
handleChangeLanguage(lang) {
|
||||
this.$p.setLanguage(lang, this.$fhcApi)
|
||||
const gerButton = this.$refs.ger
|
||||
const engButton = this.$refs.eng
|
||||
|
||||
if(lang === 'German') {
|
||||
gerButton.classList.add('fhc-active')
|
||||
engButton.classList.remove('fhc-active')
|
||||
} else if(lang === 'English') {
|
||||
engButton.classList.add('fhc-active')
|
||||
gerButton.classList.remove('fhc-active')
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
this.entries = this.menu;
|
||||
@@ -116,14 +88,10 @@ export default {
|
||||
});
|
||||
},
|
||||
template: /*html*/`
|
||||
<!--<p>CISVUE HEADER</p>
|
||||
<p>highest count : {{highestMatchingUrlCount}}</p>
|
||||
<p>active entry content_id : {{activeEntry}}</p>
|
||||
-->
|
||||
<button id="nav-main-btn" class="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#nav-main" aria-controls="nav-main" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<button id="nav-main-btn" class="navbar-toggler rounded-0" type="button" data-bs-toggle="offcanvas" data-bs-target="#nav-main" aria-controls="nav-main" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<fhc-searchbar @showSettings="toggleCollapsibles" ref="searchbar" id="nav-search" class="fhc-searchbar w-100" :searchoptions="searchbaroptions" :searchfunction="searchfunction"></fhc-searchbar>
|
||||
<fhc-searchbar ref="searchbar" id="nav-search" class="fhc-searchbar w-100" :searchoptions="searchbaroptions" :searchfunction="searchfunction"></fhc-searchbar>
|
||||
<a id="nav-logo" class="d-none d-lg-block" :href="rootUrl">
|
||||
<img :src="logoUrl" alt="Logo">
|
||||
</a>
|
||||
@@ -131,27 +99,28 @@ export default {
|
||||
<button id="nav-user-btn" class="btn btn-link rounded-0" type="button" data-bs-toggle="collapse" data-bs-target="#nav-user-menu" aria-expanded="false" aria-controls="nav-user-menu">
|
||||
<img :src="avatarUrl" class="avatar rounded-circle"/>
|
||||
</button>
|
||||
<ul ref="navUserDropdown" @[\`show.bs.collapse\`]="toggleCollapsibles('navUserDropdown')" id="nav-user-menu" class="top-100 end-0 collapse list-unstyled" aria-labelledby="nav-user-btn">
|
||||
<ul ref="navUserDropdown"
|
||||
@[\`shown.bs.collapse\`]="handleShowNavUser"
|
||||
@[\`hide.bs.collapse\`]="handleHideNavUser"
|
||||
id="nav-user-menu" class="top-100 end-0 collapse list-unstyled" aria-labelledby="nav-user-btn">
|
||||
<li class="btn-level-2"><a class="btn btn-level-2 rounded-0 d-block" :href="site_url + '/Cis/Profil'" id="menu-profil">Profil</a></li>
|
||||
<li class="fhc-languages btn-level-2" style="text-align: center;">
|
||||
<div class="btn-group">
|
||||
<a :class="getLanguageButtonClass('German')" ref="ger" href="#" @click="handleChangeLanguage('German')">Deutsch</a>
|
||||
<a :class="getLanguageButtonClass('English')" ref="eng" href="#" @click="handleChangeLanguage('English')">English</a>
|
||||
</div>
|
||||
<li class="btn-level-2">
|
||||
<cis-sprachen></cis-sprachen>
|
||||
</li>
|
||||
<li class="btn-level-2"><hr class="dropdown-divider p-0 "></li>
|
||||
<li class="btn-level-2"><hr class="dropdown-divider m-0 "></li>
|
||||
<li><a class="btn btn-level-2 rounded-0 d-block" :href="logoutUrl">Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<nav id="nav-main" class="offcanvas offcanvas-start bg-dark" tabindex="-1" aria-labelledby="nav-main-btn" data-bs-backdrop="false">
|
||||
<div id="nav-main-sticky">
|
||||
<div id="nav-main-toggle" class="position-static d-none d-lg-block bg-dark">
|
||||
<button type="button" class="btn bg-dark text-light rounded-0 p-1 d-flex align-items-center" data-bs-toggle="collapse" data-bs-target="#nav-main-menu" aria-expanded="true" aria-controls="nav-main-menu">
|
||||
<button type="button" class="btn bg-dark text-light rounded-0 p-1 d-flex align-items-center" data-bs-toggle="collapse" data-bs-target=".nav-menu-collapse" aria-expanded="true" aria-controls="nav-sprachen nav-main-menu">
|
||||
<i class="fa fa-arrow-circle-left"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="offcanvas-body p-0">
|
||||
<div id="nav-main-menu" class="collapse collapse-horizontal show">
|
||||
|
||||
<div id="nav-main-menu" class="nav-menu-collapse collapse collapse-horizontal show">
|
||||
<div>
|
||||
<cis-menu-entry :highestMatchingUrlCount="highestMatchingUrlCount" :activeContent="activeEntry" v-for="entry in entries" :key="entry.content_id" :entry="entry" />
|
||||
</div>
|
||||
|
||||
@@ -196,11 +196,6 @@ export default {
|
||||
this.checkActiveUrl(new URL(window.location.href));
|
||||
},
|
||||
template: /*html*/`
|
||||
<!-- DEBUGGIING PRINTS
|
||||
<p>entry content_id: {{JSON.stringify(entry.content_id,null,2)}}</p>
|
||||
<p>entry menu: {{JSON.stringify(entry.menu_open,null,2)}}</p>
|
||||
<p>highest count : {{urlCount}}</p>
|
||||
-->
|
||||
<div v-if="entry.template_kurzbz == 'include'">
|
||||
INCLUDE
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,15 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
lektorenLinks: function(){
|
||||
if (!this.event || !Array.isArray(this.event.lektor) || !this.event.lektor.length) return "a";
|
||||
|
||||
let lektorenLinks ={};
|
||||
this.event.lektor.forEach((lektor)=>{
|
||||
lektorenLinks[lektor.kurzbz] = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + `/Cis/Profil/View/${lektor.mitarbeiter_uid}`;
|
||||
})
|
||||
return lektorenLinks;
|
||||
},
|
||||
start_time: function () {
|
||||
if (!this.event.start) return 'N/A';
|
||||
if (!this.event.start instanceof Date) {
|
||||
@@ -67,7 +76,12 @@ export default {
|
||||
$p.t('lehre','lektor')+':'
|
||||
:''
|
||||
}}</th>
|
||||
<td>{{event.lektor.map(lektor=>lektor.kurzbz).join("/")}}</td>
|
||||
<td>
|
||||
<div v-for="lektor in event.lektor" class="d-block">
|
||||
<a v-if="lektorenLinks[lektor.kurzbz]" :href="lektorenLinks[lektor.kurzbz]"><i class="fa fa-arrow-up-right-from-square me-1" style="color:#00649C"></i></a>
|
||||
{{lektor.kurzbz}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{
|
||||
|
||||
@@ -17,6 +17,10 @@ export default {
|
||||
type:String,
|
||||
default:"title"
|
||||
},
|
||||
showMenu:{
|
||||
type:Boolean,
|
||||
default:true,
|
||||
},
|
||||
/*
|
||||
* NOTE(chris):
|
||||
* Hack to expose in "emits" declared events to $props which we use
|
||||
@@ -39,6 +43,9 @@ export default {
|
||||
methods:{
|
||||
onModalShow: function()
|
||||
{
|
||||
// do not load the menu if the menu is not getting rendered
|
||||
if(!this.showMenu) return;
|
||||
|
||||
if (this.event.type == 'lehreinheit') {
|
||||
this.$fhcApi.factory.stundenplan.getLehreinheitStudiensemester(this.event.lehreinheit_id[0]).then(
|
||||
res=>res.data
|
||||
@@ -69,8 +76,10 @@ export default {
|
||||
<template v-slot:default>
|
||||
<h3 >{{$p.t('lvinfo','lehrveranstaltungsinformationen')}}</h3>
|
||||
<lv-info :event="event"></lv-info>
|
||||
<h3 >Lehrveranstaltungs Menu</h3>
|
||||
<lv-menu :menu="menu"></lv-menu>
|
||||
<template v-if="showMenu">
|
||||
<h3 >Lehrveranstaltungs Menu</h3>
|
||||
<lv-menu :menu="menu"></lv-menu>
|
||||
</template>
|
||||
</template>
|
||||
<!-- optional footer -->
|
||||
<template v-slot:footer >
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import FhcCalendar from "../../Calendar/Calendar.js";
|
||||
import CalendarDate from "../../../composables/CalendarDate.js";
|
||||
|
||||
import LvModal from "../../../components/Cis/Mylv/LvModal.js";
|
||||
import LvInfo from "../../../components/Cis/Mylv/LvInfo.js"
|
||||
|
||||
export default{
|
||||
props:{
|
||||
@@ -10,7 +11,9 @@ export default{
|
||||
}
|
||||
},
|
||||
components: {
|
||||
FhcCalendar
|
||||
FhcCalendar,
|
||||
LvModal,
|
||||
LvInfo,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -40,6 +43,9 @@ export default{
|
||||
},
|
||||
},
|
||||
methods:{
|
||||
setSelectedEvent: function(event){
|
||||
this.currentlySelectedEvent = event;
|
||||
},
|
||||
getLvID: function () {
|
||||
this.lv_id = window.location.pathname
|
||||
},
|
||||
@@ -51,6 +57,7 @@ export default{
|
||||
Vue.nextTick(() => {
|
||||
this.$refs.lvmodal.show();
|
||||
});
|
||||
|
||||
},
|
||||
updateRange: function ({ start, end }) {
|
||||
|
||||
@@ -112,7 +119,8 @@ export default{
|
||||
this.loadEvents();
|
||||
},
|
||||
template: /*html*/`
|
||||
<fhc-calendar :initial-date="currentDay" @change:range="updateRange" :events="events" initial-mode="week" show-weeks @select:day="selectDay" v-model:minimized="minimized">
|
||||
<lv-modal v-if="currentlySelectedEvent" :showMenu="false" :event="currentlySelectedEvent" ref="lvmodal" />
|
||||
<fhc-calendar @selectedEvent="setSelectedEvent" :initial-date="currentDay" @change:range="updateRange" :events="events" initial-mode="week" show-weeks @select:day="selectDay" v-model:minimized="minimized">
|
||||
<template #monthPage="{event,day,isSelected}">
|
||||
<span class="fhc-entry" :class="{'selectedEvent':isSelected}" style="color:white" :style="{'background-color': event.color}">
|
||||
{{event.topic}}
|
||||
@@ -125,8 +133,8 @@ export default{
|
||||
<span>{{event?.orig.ort_kurzbz}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #dayPage="{event,day}">
|
||||
<div type="button" class="fhc-entry border border-secondary border row h-100 justify-content-center align-items-center text-center">
|
||||
<template #dayPage="{event,day,mobile}">
|
||||
<div @click="mobile? showModal(event?.orig):null" type="button" class="fhc-entry border border-secondary border row h-100 justify-content-center align-items-center text-center">
|
||||
<div class="col ">
|
||||
<p>Lehrveranstaltung:</p>
|
||||
<p class="m-0">{{event?.orig.topic}}</p>
|
||||
@@ -141,6 +149,15 @@ export default{
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #pageMobilContent>
|
||||
<h3 >{{$p.t('lvinfo','lehrveranstaltungsinformationen')}}</h3>
|
||||
<div class="w-100">
|
||||
<lv-info :event="currentlySelectedEvent" />
|
||||
</div>
|
||||
</template>
|
||||
<template #pageMobilContentEmpty >
|
||||
<h3>Keine Raum Reservierung</h3>
|
||||
</template>
|
||||
</fhc-calendar>
|
||||
`,
|
||||
};
|
||||
@@ -19,8 +19,16 @@ export default {
|
||||
info: null,
|
||||
}),
|
||||
computed: {
|
||||
lektorNames() {
|
||||
return this.info.lektoren.map(e => ((e.titelpre || '') + ' ' + (e.vorname || '') + ' ' + (e.nachname || '') + ' ' + (e.titelpost || '')).trim());
|
||||
lektorNamesLinks(){
|
||||
let lektorenLinks = {};
|
||||
this.info.lektoren.forEach(e => {
|
||||
let name = ((e.titelpre || '') + ' ' + (e.vorname || '') + ' ' + (e.nachname || '') + ' ' + (e.titelpost || '')).trim();
|
||||
lektorenLinks[name] = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + `/Cis/Profil/View/${e.uid}`;
|
||||
});
|
||||
return lektorenLinks;
|
||||
},
|
||||
lektorNames(){
|
||||
return this.info.lektoren.map((e)=>((e.titelpre || '') + ' ' + (e.vorname || '') + ' ' + (e.nachname || '') + ' ' + (e.titelpost || '')).trim());
|
||||
},
|
||||
lvLeitung() {
|
||||
return this.info.lvLeitung && this.info.lvLeitung.length ? this.info.lvLeitung.map(e => ((e.titelpre || '') + ' ' + (e.vorname || '') + ' ' + (e.nachname || '') + ' ' + (e.titelpost || '')).trim()) : null;
|
||||
@@ -69,16 +77,11 @@ export default {
|
||||
this.info = infos[this.lehrveranstaltung_id];
|
||||
} else {
|
||||
axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Cis/Mylv/Info/' + this.studien_semester + '/' + this.lehrveranstaltung_id).then(res => {
|
||||
|
||||
this.info = infos[this.lehrveranstaltung_id] = res.data.retval || [];
|
||||
}).catch(() => this.info = {});
|
||||
}
|
||||
},
|
||||
template: /*html*/`
|
||||
|
||||
<!-- debugging print
|
||||
<p>{{JSON.stringify(info,null,2)}}</p>
|
||||
-->
|
||||
<h1>{{$p.t('lvinfo/lehrveranstaltungsinformationen')}}</h1>
|
||||
<hr>
|
||||
<div v-if="!info" class="text-center">
|
||||
@@ -110,8 +113,8 @@ export default {
|
||||
<th>{{$p.t('lehre/lehrbeauftragter')}}</th>
|
||||
<td>
|
||||
<ul v-if="lektorNames.length" class="list-unstyled mb-0">
|
||||
<li v-for="name in lektorNames" :key="name">
|
||||
<!-- TODO(chris): link? -->
|
||||
<li v-for="name in new Set(lektorNames)" :key="name">
|
||||
<a :href="lektorNamesLinks[name]?lektorNamesLinks[name]:null"><i class="fa fa-arrow-up-right-from-square me-1" style="color:#00649C"></i></a>
|
||||
{{name}}
|
||||
</li>
|
||||
</ul>
|
||||
@@ -125,7 +128,7 @@ export default {
|
||||
<td>
|
||||
<ul class="list-unstyled mb-0">
|
||||
<li v-for="name in lvLeitung" :key="name">
|
||||
<!-- TODO(chris): link? -->
|
||||
<a :href="lektorNamesLinks[name]?lektorNamesLinks[name]:null"><i class="fa fa-arrow-up-right-from-square me-1" style="color:#00649C"></i></a>
|
||||
{{name}}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
export default {
|
||||
data(){
|
||||
return {
|
||||
allActiveLanguages: null,
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
changeLanguage: function(lang){
|
||||
if(this.allActiveLanguages.some(l => l === lang))
|
||||
{
|
||||
this.$p.setLanguage(lang, this.$fhcApi);
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted(){
|
||||
this.$fhcApi.factory.phrasen.getActiveDbLanguages()
|
||||
.then(res => res.data)
|
||||
.then(
|
||||
(langs) => {
|
||||
this.allActiveLanguages = langs;
|
||||
}
|
||||
);
|
||||
},
|
||||
template:/*html*/`
|
||||
<div class="container">
|
||||
<div class="row justify-content-center align-items-center flex-nowrap overflow-hidden">
|
||||
<button v-for="lang in allActiveLanguages" @click.prevent="changeLanguage(lang)" class="col text-white fhc-entry btn text-center w-100" :selected="$p.user_language.value==lang?'':null">{{lang}}</button>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
@@ -22,6 +22,7 @@ export default {
|
||||
provide() {
|
||||
return {
|
||||
editMode: Vue.computed(()=>this.editMode),
|
||||
viewData: Vue.computed(()=>Vue.reactive(this.viewData)),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import BsModal from "../Bootstrap/Modal.js";
|
||||
import CachedWidgetLoader from "../../composables/Dashboard/CachedWidgetLoader.js";
|
||||
import HeightTransition from "../Tranistion/HeightTransition.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BsModal,
|
||||
HeightTransition
|
||||
},
|
||||
data: () => ({
|
||||
component: "",
|
||||
@@ -118,7 +120,9 @@ export default {
|
||||
</div>
|
||||
<div v-else-if="!hidden || editMode" class="dashboard-item card overflow-hidden h-100" :class="arguments && arguments.className ? arguments.className : ''">
|
||||
<div v-if="widget" class="card-header d-flex ps-0 pe-2">
|
||||
<span v-if="editMode" drag-action="move" class="col-auto mx-2 px-2 cursor-move"><i class="fa-solid fa-grip-vertical"></i></span>
|
||||
<Transition>
|
||||
<span v-if="editMode" drag-action="move" class="col-auto mx-2 px-2 cursor-move"><i class="fa-solid fa-grip-vertical"></i></span>
|
||||
</Transition>
|
||||
<span class="col mx-2 px-2">{{ widget.setup.name }}</span>
|
||||
<a v-if="widget.setup.cis4link" :href="getWidgetC4Link(widget)" class="ms-auto mb-2">
|
||||
<i class="fa fa-arrow-up-right-from-square me-1"></i>
|
||||
@@ -127,9 +131,11 @@ export default {
|
||||
<a v-if="custom && editMode" class="col-auto px-1" href="#" @click.prevent="$emit('remove')">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</a>
|
||||
<div v-else-if="editMode" class="col-auto px-1 form-switch">
|
||||
<input class="form-check-input ms-0" type="checkbox" role="switch" id="flexSwitchCheckChecked" :checked="!hidden" @input="$emit('remove', hidden)">
|
||||
</div>
|
||||
<Transition>
|
||||
<div v-if="!custom && editMode" class="col-auto px-1 form-switch">
|
||||
<input class="form-check-input ms-0" type="checkbox" role="switch" id="flexSwitchCheckChecked" :checked="!hidden" @input="$emit('remove', hidden)">
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
<div v-if="ready" class="card-body overflow-hidden" style="padding: 0px;">
|
||||
<component :is="component" v-model:shared-data="sharedData" :config="arguments" :width="width" :height="height" @setConfig="setConfig" @change="changeConfigManually"></component>
|
||||
@@ -148,8 +154,10 @@ export default {
|
||||
<button type="button" class="btn btn-primary" @click="changeConfig">Save changes</button>
|
||||
</template>
|
||||
</bs-modal>
|
||||
<div v-if="editMode && isResizeable" class="card-footer d-flex justify-content-end p-0">
|
||||
<span drag-action="resize" class="col-auto px-1 cursor-nw-resize"><i class="fa-solid fa-up-right-and-down-left-from-center mirror-x"></i></span>
|
||||
</div>
|
||||
<height-transition>
|
||||
<div v-if="editMode && isResizeable" class="card-footer d-flex justify-content-end p-0">
|
||||
<span drag-action="resize" class="col-auto px-1 cursor-nw-resize"><i class="fa-solid fa-up-right-and-down-left-from-center mirror-x"></i></span>
|
||||
</div>
|
||||
</height-transition>
|
||||
</div>`,
|
||||
};
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import AbstractWidget from './Abstract';
|
||||
import BsModal from '../Bootstrap/Modal';
|
||||
|
||||
const MAX_LOADED_NEWS = 10;
|
||||
const MAX_LOADED_NEWS = 30;
|
||||
|
||||
export default {
|
||||
name: "WidgetsNews",
|
||||
components: {BsModal},
|
||||
components: {
|
||||
BsModal
|
||||
},
|
||||
data: () => ({
|
||||
allNewsList: [],
|
||||
singleNews: {},
|
||||
selected: null
|
||||
}),
|
||||
mixins: [AbstractWidget],
|
||||
computed: {
|
||||
@@ -17,10 +20,12 @@ export default {
|
||||
},
|
||||
newsList() {
|
||||
//Return news amount depending on widget width and size
|
||||
let quantity = this.width;
|
||||
// let quantity = this.width;
|
||||
let quantity = MAX_LOADED_NEWS;
|
||||
|
||||
|
||||
if (this.width === 1) {
|
||||
quantity = this.height === 1 ? 4 : 10;
|
||||
quantity = this.height === 1 ? 4 : MAX_LOADED_NEWS;
|
||||
}
|
||||
|
||||
return this.allNewsList.slice(0, quantity);
|
||||
@@ -31,12 +36,18 @@ export default {
|
||||
"skin/images/fh_technikum_wien_illustration_klein.png"
|
||||
);
|
||||
},
|
||||
activeNews() {
|
||||
return this.allNewsList.find(news => news.minimized === false) ?? this.allNewsList[0] ?? null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$fhcApi.factory.cms
|
||||
.news(MAX_LOADED_NEWS)
|
||||
.then((res) => {
|
||||
this.allNewsList = res.data;
|
||||
this.allNewsList = Array.from(Object.values(res.data));
|
||||
|
||||
this.selected = this.allNewsList.length ? this.allNewsList[0] : null
|
||||
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("ERROR: ", err.response.data);
|
||||
@@ -45,6 +56,63 @@ export default {
|
||||
this.$emit("setConfig", false);
|
||||
},
|
||||
methods: {
|
||||
setNext(){
|
||||
const thisIndex = this.allNewsList.findIndex(n=>n.news_id == this.selected.news_id)
|
||||
const nextIndex = thisIndex == (this.allNewsList.length - 1) ? 0 : thisIndex + 1
|
||||
this.setSelected(this.allNewsList[nextIndex])
|
||||
},
|
||||
setPrev() {
|
||||
const thisIndex = this.allNewsList.findIndex(n=>n.news_id == this.selected.news_id)
|
||||
const prevIndex = thisIndex ? thisIndex - 1 : this.allNewsList.length - 1
|
||||
this.setSelected(this.allNewsList[prevIndex], 'prev')
|
||||
},
|
||||
getMenuItemClass(news) {
|
||||
let classString = ''
|
||||
if(this.selected && this.selected.news_id === news.news_id) {
|
||||
classString += 'selected'
|
||||
}
|
||||
return classString
|
||||
},
|
||||
getDynClassCarouselItem(news, index) {
|
||||
// sets classes prev/active/next for bootstrap carousel
|
||||
let classString = ''
|
||||
|
||||
// return active class to news === selected OR very first news
|
||||
if((this.selected.news_id === news.news_id) || (this.selected === null && index === 0)) {
|
||||
classString = 'active';
|
||||
} else { // set prev/next class for news
|
||||
const selectedIndex = this.newsList.indexOf(this.selected)
|
||||
const ownIndex = this.newsList.indexOf(news)
|
||||
const isPrev = (ownIndex + 1) === selectedIndex || (ownIndex === this.newsList.length - 1 && selectedIndex === 0)
|
||||
if(isPrev) {
|
||||
classString += ' carousel-item-prev'
|
||||
}
|
||||
const isNext = (ownIndex - 1) === selectedIndex || (ownIndex === 0 && selectedIndex === this.newsList.length - 1)
|
||||
if(isNext) {
|
||||
classString += ' carousel-item-next'
|
||||
}
|
||||
}
|
||||
|
||||
return classString;
|
||||
},
|
||||
setSelected(news, direction = "next") {
|
||||
if (this.selected && news && this.selected === news) return
|
||||
|
||||
const oldCard = document.getElementById('card-'+this.selected.news_id)
|
||||
|
||||
// TODO: to show animation of non neighbour item through menu reapply css classes
|
||||
if(direction === 'next') {
|
||||
// set nextCard .carousel-item-next.carousel-item-start
|
||||
oldCard.classList.add('carousel-item-start')
|
||||
|
||||
} else {
|
||||
// set prevCard .carousel-item-prev.carousel-item-end
|
||||
oldCard.classList.add('carousel-item-end')
|
||||
}
|
||||
|
||||
this.selected = news
|
||||
|
||||
},
|
||||
contentURI: function (content_id) {
|
||||
return (
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
@@ -67,23 +135,53 @@ export default {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div class="widgets-news h-100" :style="getNewsWidgetStyle">
|
||||
<div class="d-flex flex-column h-100 ">
|
||||
<div class="d-flex flex-column h-100">
|
||||
|
||||
<div class="h-100" style="overflow-y: auto" v-if="width == 1">
|
||||
<div v-for="(news, index) in newsList" :key="news.id" class="mt-2">
|
||||
<div v-for="(news, index) in newsList" :key="news.news_id" class="mt-2">
|
||||
<div v-if="index > 0 " class="fhc-seperator"></div>
|
||||
<a :href="contentURI(news.content_id)" >{{ news.content_obj.betreff?news.content_obj.betreff:getDate(news.insertamum) }}</a><br>
|
||||
<span class="small text-muted">{{ formatDateTime(news.insertamum) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="width > 1 && height === 1" class="h-100" :class="'row row-cols-' + width">
|
||||
<div class="h-100" v-for="news in newsList" :key="news.id">
|
||||
<div class="news-content h-100" :style="'--news-widget-height: '+height" ref="htmlContent" v-html="news.content_obj.content"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="h-100" :class="'row row-cols-' + width + ' gx-2'">
|
||||
<div class="h-100" v-for="news in newsList" :key="news.id">
|
||||
<div class="news-content h-100" :style="'--news-widget-height: '+height" ref="htmlContent" v-html="news.content_obj.content"></div>
|
||||
</div>
|
||||
<a :href="contentURI(news.content_id)" >{{ news.content_obj.betreff?news.content_obj.betreff:getDate(news.insertamum) }}</a><br>
|
||||
<span class="small text-muted">{{ formatDateTime(news.insertamum) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="row h-100">
|
||||
<!-- TODO: mobile responsiveness of this part-->
|
||||
<div :class="'col-'+(width == 2? 6 : 4) + ' h-100 g-0'" style="overflow: auto;">
|
||||
<template v-for="news in newsList" :key="'menu-'+news.news_id">
|
||||
|
||||
<div class="row fhc-news-menu-item" @click="setSelected(news)" :class="getMenuItemClass(news)" style="margin-right: 0px; margin-left: 0px;">
|
||||
<div class="col-8 fhc-news-menu-item-betreff" style="overflow-y: hidden;"><p>{{news.content_obj.betreff ?? ''}}</p></div>
|
||||
<span class="fhc-news-menu-item-date fw-bold"
|
||||
>{{ news.datum ?? ''}}</span>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</div>
|
||||
<div :class="'col-'+(width == 2? 6 : 8) + ' h-100'" style="padding-left: 0px; padding-right: 0px;" ref="htmlContent">
|
||||
<div class="container h-100" style="padding: 0px;" ref="carocontainer">
|
||||
|
||||
<div id="carouselExample" style="height: 100%;" class="carousel slide fhc-carousel" data-bs-ride="carousel"
|
||||
data-bs-interval="false"
|
||||
ref="carocontrols">
|
||||
<div class="carousel-indicators">
|
||||
<button v-for="(news, index) in newsList" :id="'indicator-'+news_news_id" type="button" data-bs-target="#carouselExample" data-bs-slide-to="index"></button>
|
||||
</div>
|
||||
|
||||
<div class="carousel-inner" style="height: 100%; max-width: 100%;">
|
||||
<div v-for="(news, index) in newsList" class="carousel-item" :class="getDynClassCarouselItem(news, index)" style="overflow-y: auto; height: 100%;" :id="'card-'+news.news_id" v-html="news.content_obj.content">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO: prev/next button styling && placement-->
|
||||
<button @click="setPrev" style="z-index: 9999; color: black; opacity: 1;" data-bs-target="#carouselExample" class="carousel-control-prev" type="button">
|
||||
<i class="fa fa-chevron-left"></i>
|
||||
</button>
|
||||
<button @click="setNext" style="z-index: 9999; color: black; opacity: 1;" data-bs-target="#carouselExample" class="carousel-control-next" type="button">
|
||||
<i class="fa fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`,
|
||||
|
||||
@@ -188,8 +188,13 @@ export default {
|
||||
template: /*html*/`
|
||||
<div class="dashboard-widget-stundenplan d-flex flex-column h-100">
|
||||
<lv-modal v-if="selectedEvent" ref="lvmodal" :event="selectedEvent" />
|
||||
<content-modal :contentID="roomInfoContentID" dialogClass="modal-lg" ref="contentModal"/>
|
||||
<content-modal :content_id="roomInfoContentID" dialogClass="modal-lg" ref="contentModal"/>
|
||||
<fhc-calendar @change:range="updateRange" :initial-date="currentDay" class="border-0" class-header="p-0" @select:day="selectDay" :widget="true" v-model:minimized="minimized" :events="events" no-week-view :show-weeks="false" >
|
||||
<template #monthPage="{event,day,isSelected}">
|
||||
<span class="fhc-entry" :class="{'selectedEvent':isSelected}" style="color:white" :style="{'background-color': event.color}">
|
||||
{{event.topic}}
|
||||
</span>
|
||||
</template>
|
||||
<template #minimizedPage >
|
||||
<div class="flex-grow-1" style="overflow-y: auto; overflow-x: hidden">
|
||||
<div v-if="events === null" class="d-flex h-100 justify-content-center align-items-center">
|
||||
|
||||
@@ -356,29 +356,31 @@ export default {
|
||||
@drop="dragEnd"
|
||||
@mousemove="updateCursor"
|
||||
@mouseleave="mouseLeave">
|
||||
<grid-item
|
||||
v-for="item in (mode == 0 && active? placedItems_withPlaceholders : placedItems)"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@start-move="startMove"
|
||||
@start-resize="startResize"
|
||||
@end-drag="dragCancel"
|
||||
@drop-drag="dragEnd"
|
||||
class="position-absolute"
|
||||
:active="active"
|
||||
:style="{
|
||||
top: 'calc(' + item.y + ' * var(--fhc-dg-row-height))',
|
||||
left: 'calc(' + item.x + ' * var(--fhc-dg-col-width))',
|
||||
width: 'calc(' + item.w + ' * var(--fhc-dg-col-width))',
|
||||
height: 'calc(' + item.h + ' * var(--fhc-dg-row-height))',
|
||||
paddingTop: 'var(--fhc-dg-item-padding-top)',
|
||||
paddingLeft: 'var(--fhc-dg-item-padding-horizontal)',
|
||||
paddingRight: 'var(--fhc-dg-item-padding-horizontal)'
|
||||
}">
|
||||
<template v-slot="item">
|
||||
<slot v-bind="item.data" v-bind="item" :x="item.x" :y="item.y" ></slot>
|
||||
</template>
|
||||
</grid-item>
|
||||
<TransitionGroup tag="div">
|
||||
<grid-item
|
||||
v-for="item in (mode == 0 && active? placedItems_withPlaceholders : placedItems)"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@start-move="startMove"
|
||||
@start-resize="startResize"
|
||||
@end-drag="dragCancel"
|
||||
@drop-drag="dragEnd"
|
||||
class="position-absolute"
|
||||
:active="active"
|
||||
:style="{
|
||||
top: 'calc(' + item.y + ' * var(--fhc-dg-row-height))',
|
||||
left: 'calc(' + item.x + ' * var(--fhc-dg-col-width))',
|
||||
width: 'calc(' + item.w + ' * var(--fhc-dg-col-width))',
|
||||
height: 'calc(' + item.h + ' * var(--fhc-dg-row-height))',
|
||||
paddingTop: 'var(--fhc-dg-item-padding-top)',
|
||||
paddingLeft: 'var(--fhc-dg-item-padding-horizontal)',
|
||||
paddingRight: 'var(--fhc-dg-item-padding-horizontal)'
|
||||
}">
|
||||
<template v-slot="item">
|
||||
<slot v-bind="item.data" v-bind="item" :x="item.x" :y="item.y" ></slot>
|
||||
</template>
|
||||
</grid-item>
|
||||
</TransitionGroup>
|
||||
|
||||
</div>`
|
||||
}
|
||||
|
||||
@@ -265,7 +265,10 @@ export default {
|
||||
<component
|
||||
v-else-if="tag == 'PvAutocomplete'"
|
||||
ref="input"
|
||||
:is="tag"
|
||||
:type="type"
|
||||
v-model="modelValueCmp"
|
||||
v-bind="$attrs"
|
||||
:id="idCmp"
|
||||
:input-props="{name}"
|
||||
:class="validationClass"
|
||||
|
||||
@@ -23,11 +23,15 @@ export default {
|
||||
},
|
||||
mounted() {},
|
||||
template: /*html*/ `
|
||||
|
||||
<paginator v-model:rows="page_size" @page="(data)=>$emit('page',{...data, page:data.page+1})" :rows="page_size" :totalRecords="maxPageCount" :rowsPerPageOptions="[10, 20, 30]" ></paginator>
|
||||
<slot>
|
||||
Placeholder
|
||||
</slot>
|
||||
|
||||
<!-- Desktop -->
|
||||
<div class="d-none d-md-block">
|
||||
<paginator v-model:rows="page_size" @page="(data)=>$emit('page',{...data, page:data.page+1})" :rows="page_size" :totalRecords="maxPageCount" :rowsPerPageOptions="[10, 20, 30]" >
|
||||
</paginator>
|
||||
</div>
|
||||
<!-- Mobile -->
|
||||
<div class="d-block d-md-none">
|
||||
<paginator v-model:rows="page_size" @page="(data)=>$emit('page',{...data, page:data.page+1})" :rows="page_size" :totalRecords="maxPageCount" :rowsPerPageOptions="[10, 20, 30]" template="FirstPageLink PrevPageLink CurrentPageReport NextPageLink LastPageLink RowsPerPageDropdown">
|
||||
</paginator>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
export default {
|
||||
data(){
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
onEnter(el,done){
|
||||
el.style.height = '0';
|
||||
el.style.height = el.scrollHeight + 'px';
|
||||
},
|
||||
onLeave(el,done){
|
||||
el.style.height = el.scrollHeight + 'px';
|
||||
el.style.height = '0';
|
||||
}
|
||||
},
|
||||
template:
|
||||
/*html*/`
|
||||
<Transition name="height" @enter="onEnter" @leave="onLeave">
|
||||
<slot>
|
||||
</slot>
|
||||
</Transition>
|
||||
`,
|
||||
};
|
||||
@@ -7,12 +7,10 @@ import prestudent from "./prestudent.js";
|
||||
|
||||
export default {
|
||||
props: [ "searchoptions", "searchfunction" ],
|
||||
emits: ['showSettings'],
|
||||
data: function() {
|
||||
return {
|
||||
searchtimer: null,
|
||||
hidetimer: null,
|
||||
showsettings: false,
|
||||
searchsettings: {
|
||||
searchstr: '',
|
||||
types: [],
|
||||
@@ -65,8 +63,10 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="searchSettings" ref="settings" @[\`show.bs.collapse\`]="$emit('showSettings','settings')"
|
||||
class="top-100 end-0 searchbar_settings text-white collapse" tabindex="-1">
|
||||
<div id="searchSettings" ref="settings"
|
||||
@[\`shown.bs.collapse\`]="handleShowSettings"
|
||||
@[\`hide.bs.collapse\`]="handleHideSettings"
|
||||
class="top-100 end-0 searchbar_settings text-white collapse" tabindex="-1">
|
||||
<div class="d-flex flex-column m-3" v-if="this.searchoptions.types.length > 0">
|
||||
<span class="fw-light mb-2">Suche filtern nach:</span>
|
||||
<template v-for="(type, index) in this.searchoptions.types" :key="type">
|
||||
@@ -87,7 +87,6 @@ export default {
|
||||
},
|
||||
beforeMount: function() {
|
||||
this.updateSearchOptions();
|
||||
|
||||
},
|
||||
mounted(){
|
||||
this.settingsDropdown = new bootstrap.Collapse(this.$refs.settings, {
|
||||
@@ -102,6 +101,21 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
checkSettingsVisibility: function(event) {
|
||||
// hides the settings collapsible if the user clicks somewhere else
|
||||
if (!this.$refs.settings.contains(event.target))
|
||||
{
|
||||
this.settingsDropdown.hide();
|
||||
}
|
||||
},
|
||||
handleShowSettings: function() {
|
||||
// adds the event listener checkSettingsVisibility only when the collapsible is shown
|
||||
document.addEventListener("click", this.checkSettingsVisibility);
|
||||
},
|
||||
handleHideSettings: function () {
|
||||
// removes the event listener checkSettingsVisibility when the collapsible is hidden
|
||||
document.removeEventListener("click", this.checkSettingsVisibility);
|
||||
},
|
||||
updateSearchOptions: function() {
|
||||
this.searchsettings.types = [];
|
||||
for( const idx in this.searchoptions.types ) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import FhcApi from './FhcApi.js';
|
||||
|
||||
const categories = Vue.reactive({});
|
||||
const user_language = Vue.ref(FHC_JS_DATA_STORAGE_OBJECT.user_language);
|
||||
const loadingModules = {};
|
||||
let reload = false;
|
||||
|
||||
@@ -30,6 +31,9 @@ const phrasen = {
|
||||
categories[row.category][row.phrase] = row.text
|
||||
})
|
||||
|
||||
// update the reactive data that holds the current active user_language
|
||||
user_language.value = language;
|
||||
|
||||
return res
|
||||
})
|
||||
},
|
||||
@@ -81,6 +85,7 @@ export default {
|
||||
t: phrasen.t,
|
||||
loadCategory: cat => phrasen.loadCategory.call(app, cat),
|
||||
setLanguage: phrasen.setLanguage,
|
||||
user_language: user_language,
|
||||
t_ref: phrasen.t_ref
|
||||
};
|
||||
app.provide('$p', app.config.globalProperties.$p);
|
||||
|
||||
@@ -190,6 +190,25 @@ function checkfilter($row, $filter2, $buchungstyp = null)
|
||||
: false;
|
||||
return $filtered;
|
||||
}
|
||||
else if ($filter2 === 'ueberfaelligebuchungen')
|
||||
{
|
||||
$qry = "SELECT sum(betrag) as summe
|
||||
FROM tbl_konto
|
||||
WHERE person_id=".$db->db_add_param($row->person_id, FHC_INTEGER) ."
|
||||
AND buchungsdatum < NOW()
|
||||
"
|
||||
;
|
||||
|
||||
if($kontofilterstg=='true')
|
||||
$qry.=" AND studiengang_kz=".$db->db_add_param($row->studiengang_kz);
|
||||
if($buchungstyp != null && $buchungstyp != "alle")
|
||||
$qry.=" AND buchungstyp_kurzbz=".$db->db_add_param($buchungstyp);
|
||||
|
||||
if($db->db_query($qry))
|
||||
if($row_filter = $db->db_fetch_object())
|
||||
if($row_filter->summe=='0.00' || $row_filter->summe=='' || $row_filter->summe=='0')
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+11
-4
@@ -27,8 +27,6 @@ require_once('dbupdate_3.4/example.php');
|
||||
require_once('dbupdate_3.4/example2.php');
|
||||
...
|
||||
*/
|
||||
require_once('dbupdate_3.4/25003_notenimport_nachpruefung.php');
|
||||
|
||||
require_once('dbupdate_3.4/dbupdate_dashboard.php');
|
||||
require_once('dbupdate_3.4/26173_index_webservicelog.php');
|
||||
require_once('dbupdate_3.4/24682_reihungstest_zugangscode_fuer_login.php');
|
||||
@@ -66,6 +64,7 @@ require_once('dbupdate_3.4/44031_stv_favorites.php');
|
||||
require_once('dbupdate_3.4/40896_kennzeichnung_unruly_person.php');
|
||||
require_once('dbupdate_3.4/39911_tabulator_in_contentmittitel.php');
|
||||
require_once('dbupdate_3.4/25999_C4_permission.php');
|
||||
require_once('dbupdate_3.4/33683_digitale_anwesenheitsliste_und_entschuldigungsmanagement_fuer_studierende_prototyp.php');
|
||||
|
||||
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
|
||||
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
|
||||
@@ -158,7 +157,7 @@ $tabellen=array(
|
||||
"campus.tbl_studierendenantrag_statustyp"=> array("studierendenantrag_statustyp_kurzbz","bezeichnung"),
|
||||
"campus.tbl_studentbeispiel" => array("student_uid","beispiel_id","vorbereitet","probleme","updateamum","updatevon","insertamum","insertvon"),
|
||||
"campus.tbl_studentuebung" => array("student_uid","mitarbeiter_uid","abgabe_id","uebung_id","note","mitarbeitspunkte","punkte","anmerkung","benotungsdatum","updateamum","updatevon","insertamum","insertvon"),
|
||||
"campus.tbl_template" => array("template_kurzbz","bezeichnung","xsd","xslt_xhtml","xslfo_pdf"),
|
||||
"campus.tbl_template" => array("template_kurzbz","bezeichnung","xsd","xslt_xhtml","xslfo_pdf","xslt_xhtml_c4"),
|
||||
"campus.tbl_uebung" => array("uebung_id","gewicht","punkte","angabedatei","freigabevon","freigabebis","abgabe","beispiele","statistik","bezeichnung","positiv","defaultbemerkung","lehreinheit_id","maxstd","maxbsp","liste_id","prozent","nummer","updateamum","updatevon","insertamum","insertvon"),
|
||||
"campus.tbl_veranstaltung" => array("veranstaltung_id","titel","beschreibung","veranstaltungskategorie_kurzbz","inhalt","start","ende","freigabevon","freigabeamum","updateamum","updatevon","insertamum","insertvon"),
|
||||
"campus.tbl_veranstaltungskategorie" => array("veranstaltungskategorie_kurzbz","bezeichnung","bild","farbe"),
|
||||
@@ -168,6 +167,11 @@ $tabellen=array(
|
||||
"campus.tbl_zeitsperretyp" => array("zeitsperretyp_kurzbz","beschreibung","farbe"),
|
||||
"campus.tbl_zeitwunsch" => array("stunde","mitarbeiter_uid","tag","gewicht","updateamum","updatevon","insertamum","insertvon", "zeitwunsch_id", "zeitwunsch_gueltigkeit_id"),
|
||||
"campus.tbl_zeitwunsch_gueltigkeit" => array("zeitwunsch_gueltigkeit_id","mitarbeiter_uid","von","bis","insertamum","insertvon", "updateamum","updatevon"),
|
||||
"dashboard.tbl_dashboard" => array("dashboard_id", "dashboard_kurzbz", "beschreibung"),
|
||||
"dashboard.tbl_dashboard_benutzer_override" => array("override_id","dashboard_id","uid","override"),
|
||||
"dashboard.tbl_dashboard_preset" => array("preset_id","dashboard_id","funktion_kurzbz","preset"),
|
||||
"dashboard.tbl_dashboard_widget" => array("dashboard_id","widget_id"),
|
||||
"dashboard.tbl_widget" => array("widget_id","widget_kurzbz","beschreibung","arguments","setup"),
|
||||
"fue.tbl_aktivitaet" => array("aktivitaet_kurzbz","beschreibung","sort"),
|
||||
"fue.tbl_aufwandstyp" => array("aufwandstyp_kurzbz","bezeichnung"),
|
||||
"fue.tbl_projekt" => array("projekt_kurzbz","nummer","titel","beschreibung","beginn","ende","oe_kurzbz","budget","farbe","aufwandstyp_kurzbz","ressource_id","anzahl_ma","aufwand_pt","projekt_id","projekttyp_kurzbz","zeitaufzeichnung"),
|
||||
@@ -323,7 +327,10 @@ $tabellen=array(
|
||||
"public.tbl_preoutgoing_status" => array("preoutgoing_status_kurzbz","bezeichnung"),
|
||||
"public.tbl_prestudent" => array("prestudent_id","aufmerksamdurch_kurzbz","person_id","studiengang_kz","berufstaetigkeit_code","ausbildungcode","zgv_code","zgvort","zgvdatum","zgvmas_code","zgvmaort","zgvmadatum","aufnahmeschluessel","facheinschlberuf","reihungstest_id","anmeldungreihungstest","reihungstestangetreten","rt_gesamtpunkte","rt_punkte1","rt_punkte2","bismelden","anmerkung","dual","insertamum","insertvon","updateamum","updatevon","ext_id","ausstellungsstaat","rt_punkte3", "zgvdoktor_code", "zgvdoktorort", "zgvdoktordatum","mentor","zgvnation","zgvmanation","zgvdoktornation","gsstudientyp_kurzbz","aufnahmegruppe_kurzbz","udf_values","priorisierung","foerderrelevant","standort_code","zgv_erfuellt","zgvmas_erfuellt","zgvdoktor_erfuellt"),
|
||||
"public.tbl_prestudentstatus" => array("prestudent_id","status_kurzbz","studiensemester_kurzbz","ausbildungssemester","datum","orgform_kurzbz","insertamum","insertvon","updateamum","updatevon","ext_id","studienplan_id","bestaetigtam","bestaetigtvon","fgm","faktiv", "anmerkung","bewerbung_abgeschicktamum","rt_stufe","statusgrund_id"),
|
||||
"public.tbl_raumtyp" => array("raumtyp_kurzbz","beschreibung","kosten"),
|
||||
"public.tbl_profil_update" => array("profil_update_id","uid","topic","requested_change","updateamum","updatevon","insertamum","insertvon","status","status_timestamp","status_message","attachment_id"),
|
||||
"public.tbl_profil_update_status" => array("status_kurzbz","beschreibung","bezeichnung_mehrsprachig"),
|
||||
"public.tbl_profil_update_topic" => array("topic_kurzbz","beschreibung","bezeichnung_mehrsprachig"),
|
||||
"public.tbl_raumtyp" => array("raumtyp_kurzbz","beschreibung","kosten","aktiv"),
|
||||
"public.tbl_reihungstest" => array("reihungstest_id","studiengang_kz","ort_kurzbz","anmerkung","datum","uhrzeit","updateamum","updatevon","insertamum","insertvon","ext_id","freigeschaltet","max_teilnehmer","oeffentlich","studiensemester_kurzbz","aufnahmegruppe_kurzbz","stufe","anmeldefrist","zugangs_ueberpruefung","zugangscode"),
|
||||
"public.tbl_rueckstellung" => array("rueckstellung_id","person_id","status_kurzbz","datum_bis","insertamum","insertvon"),
|
||||
"public.tbl_rueckstellung_status" => array("status_kurzbz", "bezeichnung_mehrsprachig", "sort", "aktiv"),
|
||||
|
||||
@@ -26453,7 +26453,7 @@ array(
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
array(
|
||||
'app' => 'personalverwaltung',
|
||||
'category' => 'gehaltsband',
|
||||
@@ -27518,7 +27518,7 @@ array(
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'profilUpdate',
|
||||
@@ -28879,7 +28879,7 @@ array(
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
|
||||
//ProfilUpdate Phrasen ende
|
||||
array(
|
||||
'app' => 'core',
|
||||
@@ -31345,7 +31345,7 @@ array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Im Entschuldigungsmanagement können Sie als Studiengangsassistenz beziehungsweise als Administrator die von Studenten hochgeladenen Entschuldigungsdokumente überprüfen und den Status entsprechend vergeben.
|
||||
|
||||
|
||||
Bitte beachten Sie dass nur Entschuldigungen INNERHALB des angegebenen Zeitraumes angezeigt werden. Sollten Sie nach einer lang wirken Entschuldigung suchen, müssen Sie die Zeitspanne entsprechend weit setzen.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
@@ -31393,9 +31393,9 @@ array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Um eine Anwesenheitskontrolle für Ihre ausgewählte Unterrichtsgruppe durchzuführen, wählen Sie bitte einen Termin aus dem Stundenplan aus oder geben händisch die gewünschte Gültigkeitkeitsdauer der Kontrolle an.
|
||||
|
||||
|
||||
Die Gültigkeitsdauer bestimmt die Gewichtung der Anwesenheit in Relation zum Gesamtausmaß, sie können diese aber nach eigenem Ermessen anpassen und müssen sich nicht streng an die Termine im Stundenplan halten.
|
||||
|
||||
|
||||
Sie können pro Datum und Unterrichtsgruppe eine Anwesenheitskontrolle pro Tag eröffnen, welche jedoch beliebig oft aufgerufen und von Studenten eingecheckt werden kann. Es gelten dabei ihre zuletzt eingetragenen Zeiten. Ein Student muss nur einmal am Tag pro Gruppe einchecken um als anwesend registriert zu sein, egal wie oft Sie die Kontrolle starten.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
@@ -31421,9 +31421,9 @@ array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'In dieser Detailansicht können Sie einzelne Anwesenheiten eines Studenten bearbeiten, falls ein anwesender Student aus technischen Gründen den Zugangscode nicht eingeben kann. Ebenso steht es Ihnen frei Studenten auszutragen, welche nicht anwesend sind aber den Zugangscode mit Hilfe von anwesenden Studenten erhalten haben.
|
||||
|
||||
|
||||
Falls eine Anwesenheit durch eine akzeptierte Entschuldigung entstanden ist, können Sie den Status nicht verändern.
|
||||
|
||||
|
||||
Es steht Ihnen frei die Anwesenheitseinträge mit Notiztexten zu versehen, welche dem Studenten nicht zugänglich sind.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
@@ -31440,6 +31440,26 @@ array(
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'anwesenheiten',
|
||||
'category' => 'global',
|
||||
'phrase' => 'tooltipStudentTestphase',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Hinweis: Es handelt sich um einen nicht repräsentativen Testbetrieb und die Ergebnisse werden nicht für die Benotung herangezogen.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Note: This is a non-representative test operation and the results are not used for grading.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
//
|
||||
// DIGITALE ANWESENHEITEN PHRASEN END
|
||||
//
|
||||
@@ -37176,8 +37196,28 @@ array(
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
// CIS4 phrases from legacy code end
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'gruppenmanagement',
|
||||
'phrase' => 'nichtZumEditierenDerGruppeBerechtigt',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => "Nicht zum Editieren der Gruppe berechtigt",
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => "No authorization for editing the group",
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -23,12 +23,12 @@ require_once("../../config/vilesci.config.inc.php");
|
||||
require_once("../../include/functions.inc.php");
|
||||
require_once("../../include/benutzerberechtigung.class.php");
|
||||
require_once("../../include/datum.class.php");
|
||||
|
||||
require_once("../../include/studiengang.class.php");
|
||||
require_once("../../include/studiensemester.class.php");
|
||||
require_once("../../include/studienjahr.class.php");
|
||||
require_once("../../include/student.class.php");
|
||||
require_once("../../include/konto.class.php");
|
||||
require_once("../../include/bankverbindung.class.php");
|
||||
|
||||
// Get the uid of the logged user
|
||||
$user = get_uid();
|
||||
@@ -71,7 +71,7 @@ $fileTmpName = null;
|
||||
$fileMimeType = null;
|
||||
|
||||
// Constants
|
||||
$L_CSV_N_COLS = 6; // Number of columns of the CSV file
|
||||
$L_CSV_N_COLS = 10; // Number of columns of the CSV file
|
||||
$L_ERROR = "Error";
|
||||
$L_WARNING = "Warning";
|
||||
$L_INFO = "Info";
|
||||
@@ -246,15 +246,19 @@ if (!$errorOccurred && $dataPosted)
|
||||
// Checks if character encoding is UTF-8
|
||||
if (mb_detect_encoding(implode(";", $fileRow), "UTF-8", true))
|
||||
{
|
||||
$rowName = $fileRow[0];
|
||||
$rowSurname = $fileRow[1];
|
||||
$rowCode = $fileRow[2]; // uid or matrikelnr
|
||||
$rowStudiengang = $fileRow[3];
|
||||
$rowAmount = $fileRow[4];
|
||||
$rowDate = $fileRow[5];
|
||||
$rowSurname = $fileRow[0];
|
||||
$rowName = $fileRow[1];
|
||||
$rowGebdat = $fileRow[2];
|
||||
$rowCode = $fileRow[3]; // uid or matrikelnr
|
||||
$rowStudiengang = $fileRow[4];
|
||||
$rowAmount = $fileRow[5];
|
||||
$rowIBAN = $fileRow[6];
|
||||
$rowBIC = $fileRow[7];
|
||||
$rowBank = $fileRow[8];
|
||||
$rowDate = date('Y-m-d');
|
||||
|
||||
// If this row is not the header
|
||||
if (strtolower($rowName) != "nachname")
|
||||
if (strtolower($rowSurname) != "nachname")
|
||||
{
|
||||
// If $rowCode is a matrikelnr gets the uid
|
||||
if (($uid = $student->getUidFromMatrikelnummer($rowCode)) === false)
|
||||
@@ -293,6 +297,12 @@ if (!$errorOccurred && $dataPosted)
|
||||
// Inserting positive amount
|
||||
if ($konto->save(true) === true)
|
||||
{
|
||||
lAddToLogArray(
|
||||
$L_INFO,
|
||||
$lineNumber,
|
||||
"Added!!!"
|
||||
);
|
||||
/* Keine Gegenbuchung erstellen
|
||||
lDebit($konto); // Negative amount
|
||||
if ($konto->save(true) === true) // Inserting negative amount
|
||||
{
|
||||
@@ -310,6 +320,7 @@ if (!$errorOccurred && $dataPosted)
|
||||
"This file row has been discarted because an error has occurred while inserting in DB"
|
||||
);
|
||||
}
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -321,6 +332,79 @@ if (!$errorOccurred && $dataPosted)
|
||||
}
|
||||
}
|
||||
|
||||
// Bankverbindung hinterlegen
|
||||
$bank = new bankverbindung();
|
||||
$found = false;
|
||||
if($bank->load_pers($student->person_id))
|
||||
{
|
||||
foreach($bank->result as $row_bank)
|
||||
{
|
||||
if(str_replace(' ', '', $row_bank->iban) == str_replace(' ', '', $rowIBAN))
|
||||
{
|
||||
lAddToLogArray(
|
||||
$L_WARNING,
|
||||
$lineNumber,
|
||||
"Bank IBAN already found for PersonID ".$student->person_id
|
||||
);
|
||||
$found = true;
|
||||
|
||||
// Update Datum aktualisieren damit Update in Fremdsystem getriggert wird
|
||||
$row_bank->new=false;
|
||||
$row_bank->updateamum = date('Y-m-d H:i:s');
|
||||
$row_bank->updatevon = 'Leistungsimport';
|
||||
if($row_bank->save())
|
||||
{
|
||||
lAddToLogArray(
|
||||
$L_INFO,
|
||||
$lineNumber,
|
||||
"Bank Date Update for PersonID ".$student->person_id
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
lAddToLogArray(
|
||||
$L_WARNING,
|
||||
$lineNumber,
|
||||
"Bank Date Update Failed for PersonID ".$student->person_id
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!$found)
|
||||
{
|
||||
$bank = new bankverbindung();
|
||||
$bank->new = true;
|
||||
$bank->iban = $rowIBAN;
|
||||
$bank->person_id = $student->person_id;
|
||||
$bank->bic = $rowBIC;
|
||||
$bank->name = $rowBank;
|
||||
$bank->typ = 'p';
|
||||
$bank->verrechnung = true;
|
||||
$bank->insertamum = date('Y-m-d H:i:s');
|
||||
$bank->insertvon = 'Leistungsimport';
|
||||
$bank->updateamum = date('Y-m-d H:i:s');
|
||||
$bank->updatevon = 'Leistungsimport';
|
||||
if($bank->save())
|
||||
{
|
||||
lAddToLogArray(
|
||||
$L_INFO,
|
||||
$lineNumber,
|
||||
"Bankdaten hinzugefügt"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
lAddToLogArray(
|
||||
$L_WARNING,
|
||||
$lineNumber,
|
||||
"Failed to Add Bankdata".$bank->errormsg
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -2411,12 +2411,13 @@ else
|
||||
});
|
||||
}
|
||||
}
|
||||
function prueflingEntSperren(person_id, name, art)
|
||||
function prueflingEntSperren(element)
|
||||
{
|
||||
if (art === true)
|
||||
var text = "sperren";
|
||||
else if (art === false)
|
||||
var text = "entsperren";
|
||||
var person_id = element.getAttribute("data-person-id");
|
||||
var name = element.getAttribute("data-person-name");
|
||||
var art = element.getAttribute("data-art") === "true";
|
||||
|
||||
let text = art ? "sperren" : "entsperren";
|
||||
|
||||
if (confirm("Wollen Sie den Studenten "+ name + " wirklich " + text + "?"))
|
||||
{
|
||||
@@ -3279,10 +3280,18 @@ else
|
||||
|
||||
|
||||
echo "<td class='textcentered ".$inaktiv ."'>
|
||||
<a href='#' class='prueflingsperren_".$erg->person_id . ((isset($gesperrt_arr[$erg->person_id]) && $gesperrt_arr[$erg->person_id]->gesperrt === true) ? " hidden" : "") ."' onclick='prueflingEntSperren(" . $erg->person_id . ", \"" . $erg->vorname . " " . $erg->nachname ."\"" .", true)'>
|
||||
<a href='#' class='prueflingsperren_".$erg->person_id . ((isset($gesperrt_arr[$erg->person_id]) && $gesperrt_arr[$erg->person_id]->gesperrt === true) ? " hidden" : "") ."'
|
||||
data-person-id='".$erg->person_id."'
|
||||
data-person-name='".htmlspecialchars($erg->vorname . " " . $erg->nachname, ENT_QUOTES, 'UTF-8')."'
|
||||
data-art='true'
|
||||
onclick='prueflingEntSperren(this)'>
|
||||
<span class='glyphicon glyphicon-remove'></span>
|
||||
</a>
|
||||
<a href='#' class='prueflingentsperren_".$erg->person_id . ((isset($gesperrt_arr[$erg->person_id]) && $gesperrt_arr[$erg->person_id]->gesperrt !== true ? " hidden" : "")) . "' onclick='prueflingEntSperren(" . $erg->person_id . ", \"" . $erg->vorname . " " . $erg->nachname ."\"" .", false);'>
|
||||
<a href='#' class='prueflingentsperren_".$erg->person_id . ((isset($gesperrt_arr[$erg->person_id]) && $gesperrt_arr[$erg->person_id]->gesperrt !== true ? " hidden" : "")) . "'
|
||||
data-person-id='".$erg->person_id."'
|
||||
data-person-name='".htmlspecialchars($erg->vorname . " " . $erg->nachname, ENT_QUOTES, 'UTF-8')."'
|
||||
data-art='false'
|
||||
onclick='prueflingEntSperren(this)'>
|
||||
<span class='glyphicon glyphicon-ok'></span>
|
||||
</a>
|
||||
</td>";
|
||||
|
||||
Reference in New Issue
Block a user