mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-07-21 17:02:19 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9983a48102 | |||
| 9abaf30ed3 | |||
| 81c16a3bf2 | |||
| a413ae17b8 | |||
| 8172df5782 | |||
| 6565399633 | |||
| 72692812ff | |||
| 80dabf5ce6 | |||
| e4a1264072 | |||
| 517f1a9d93 | |||
| e62848c773 | |||
| 9ac9804563 | |||
| 24a0e74281 | |||
| 57597dac29 | |||
| 7240b0c798 | |||
| ab5294de2f | |||
| 8dd42361a0 | |||
| 229de14f9c | |||
| e990bb3d81 | |||
| 43a1d163a3 | |||
| 93388ff06c | |||
| 352fc53e74 |
@@ -33,5 +33,27 @@ Events::on('loadRenderers', function ($renderers) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Tempus Renderers:
|
||||||
|
Events::on('loadTempusRenderers', function ($renderers) {
|
||||||
|
$fhc_core_renderers =& $renderers();
|
||||||
|
$fhc_core_renderers["reservierung"] = array(
|
||||||
|
'calendarEvent' => absoluteJsImportUrl('public/js/components/Tempus/Renderer/Reservierungen/calendarEvent.js'),
|
||||||
|
'modalTitle' => absoluteJsImportUrl('public/js/components/Tempus/Renderer/Reservierungen/modalTitle.js'),
|
||||||
|
'modalContent' => absoluteJsImportUrl('public/js/components/Tempus/Renderer/Reservierungen/modalContent.js'),
|
||||||
|
'calendarEventStyles' => APP_ROOT . 'public/css/Cis4/CoreCalendarEvents.css'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Events::on('loadTempusRenderers', function ($renderers) {
|
||||||
|
$fhc_core_renderers =& $renderers();
|
||||||
|
$fhc_core_renderers["lehreinheit"] = array(
|
||||||
|
'calendarEvent' => absoluteJsImportUrl('public/js/components/Tempus/Renderer/Lehreinheit/calendarEvent.js'),
|
||||||
|
'modalTitle' => absoluteJsImportUrl('public/js/components/Tempus/Renderer/Lehreinheit/modalTitle.js'),
|
||||||
|
'modalContent' => absoluteJsImportUrl('public/js/components/Tempus/Renderer/Lehreinheit/modalContent.js'),
|
||||||
|
'calendarEventStyles' => APP_ROOT . 'public/css/Cis4/CoreCalendarEvents.css'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$config['send_update_mails'] = false;
|
||||||
|
$config['calendar_start'] = 7;
|
||||||
|
$config['calendar_end'] = 23;
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class Tempus extends Auth_Controller
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$permissions = [];
|
||||||
|
$router = load_class('Router');
|
||||||
|
$permissions[$router->method] = ['admin:r', 'assistenz:r'];
|
||||||
|
parent::__construct($permissions);
|
||||||
|
|
||||||
|
// Load Libraries
|
||||||
|
$this->load->library('VariableLib', ['uid' => getAuthUID()]);
|
||||||
|
|
||||||
|
// Load Config
|
||||||
|
$this->load->config('calendar');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$this->_loadView();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sync()
|
||||||
|
{
|
||||||
|
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
|
||||||
|
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
|
||||||
|
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||||
|
$this->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
|
||||||
|
$this->load->model('ressource/Kalenderstatus_model', 'KalenderStatusModel');
|
||||||
|
|
||||||
|
$this->StudiengangModel->addOrder('typ');
|
||||||
|
$this->StudiengangModel->addOrder('kurzbz');
|
||||||
|
$this->StudiengangModel->addJoin('tbl_organisationseinheit', 'oe_kurzbz', 'LEFT');
|
||||||
|
$organisationen = getData($this->StudiengangModel->loadWhere(array('tbl_studiengang.aktiv' => 'true')));
|
||||||
|
|
||||||
|
$this->StudiensemesterModel->addOrder('start', 'DESC');
|
||||||
|
$studiensemestern = getData($this->StudiensemesterModel->load());
|
||||||
|
|
||||||
|
$this->_loadView(array('organisationen' => $organisationen, 'studiensemestern' => $studiensemestern));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _loadView($extraVariables = [])
|
||||||
|
{
|
||||||
|
$this->load->view('Tempus', [
|
||||||
|
'permissions' => [
|
||||||
|
'admin' => $this->permissionlib->isBerechtigt('admin')
|
||||||
|
],
|
||||||
|
'variables' => array_merge([
|
||||||
|
'semester_aktuell' => $this->variablelib->getVar('semester_aktuell'),
|
||||||
|
'timezone' => $this->config->item('timezone')
|
||||||
|
], $extraVariables)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,7 +31,7 @@ class RendererLoader extends FHCAPI_Controller
|
|||||||
|
|
||||||
parent::__construct([
|
parent::__construct([
|
||||||
'GetRenderers' => self::PERM_LOGGED,
|
'GetRenderers' => self::PERM_LOGGED,
|
||||||
|
'GetTempusRenderers' => self::PERM_LOGGED,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->load->library('LogLib');
|
$this->load->library('LogLib');
|
||||||
@@ -66,6 +66,26 @@ class RendererLoader extends FHCAPI_Controller
|
|||||||
$this->terminateWithSuccess($renderer_paths);
|
$this->terminateWithSuccess($renderer_paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function GetTempusRenderers(){
|
||||||
|
$renderer_paths = [];
|
||||||
|
Events::trigger(
|
||||||
|
'loadRenderers',
|
||||||
|
function & () use (&$renderer_paths)
|
||||||
|
{
|
||||||
|
return $renderer_paths;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Events::trigger(
|
||||||
|
'loadTempusRenderers',
|
||||||
|
function & () use (&$renderer_paths)
|
||||||
|
{
|
||||||
|
return $renderer_paths;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$this->terminateWithSuccess($renderer_paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -118,54 +118,17 @@ class Gruppe extends FHCAPI_Controller
|
|||||||
|
|
||||||
$query_words = explode(' ', $query);
|
$query_words = explode(' ', $query);
|
||||||
|
|
||||||
$this->_ci->GruppeModel->addSelect('gruppe_kurzbz,
|
$gruppen_result = $this->_ci->GruppeModel->search($query_words);
|
||||||
studiengang_kz,
|
|
||||||
semester,
|
|
||||||
bezeichnung,
|
|
||||||
gid,
|
|
||||||
\'false\' as lehrverband');
|
|
||||||
$this->_ci->GruppeModel->db->where(array('sichtbar' => true, 'aktiv' => true, 'lehre' => true, 'direktinskription' => false, 'semester IS NOT NULL' => null));
|
|
||||||
$this->_ci->GruppeModel->db->group_start();
|
|
||||||
foreach ($query_words as $word)
|
|
||||||
{
|
|
||||||
$this->_ci->GruppeModel->db->group_start();
|
|
||||||
$this->_ci->GruppeModel->db->where('gruppe_kurzbz ILIKE', "%" . $word . "%");
|
|
||||||
$this->_ci->GruppeModel->db->or_where('bezeichnung ILIKE', "%" . $word . "%");
|
|
||||||
$this->_ci->GruppeModel->db->group_end();
|
|
||||||
}
|
|
||||||
$this->_ci->GruppeModel->db->group_end();
|
|
||||||
|
|
||||||
$gruppen_result = $this->_ci->GruppeModel->load();
|
|
||||||
|
|
||||||
$gruppen_array = array();
|
|
||||||
|
|
||||||
if (isError($gruppen_result))
|
if (isError($gruppen_result))
|
||||||
$this->terminateWithError(getError($gruppen_result), self::ERROR_TYPE_GENERAL);
|
$this->terminateWithError(getError($gruppen_result), self::ERROR_TYPE_GENERAL);
|
||||||
|
|
||||||
|
$gruppen_array = array();
|
||||||
|
|
||||||
if (hasData($gruppen_result))
|
if (hasData($gruppen_result))
|
||||||
$gruppen_array = getData($gruppen_result);
|
$gruppen_array = getData($gruppen_result);
|
||||||
|
|
||||||
$this->_ci->LehrverbandModel->addSelect('CONCAT(UPPER(CONCAT(typ, kurzbz)), \'\', semester, verband, COALESCE(gruppe,\'\')) as gruppe_kurzbz,
|
$lehrverband_result = $this->_ci->LehrverbandModel->search($query_words);
|
||||||
studiengang_kz,
|
|
||||||
semester,
|
|
||||||
tbl_lehrverband.bezeichnung,
|
|
||||||
gid,
|
|
||||||
\'true\' as lehrverband');
|
|
||||||
$this->_ci->LehrverbandModel->addJoin('public.tbl_studiengang', 'studiengang_kz');
|
|
||||||
$this->_ci->LehrverbandModel->addOrder('verband');
|
|
||||||
$this->_ci->LehrverbandModel->addOrder('gruppe');
|
|
||||||
$this->_ci->LehrverbandModel->db->where(array('tbl_lehrverband.aktiv' => true));
|
|
||||||
|
|
||||||
$this->_ci->LehrverbandModel->db->group_start();
|
|
||||||
foreach ($query_words as $word)
|
|
||||||
{
|
|
||||||
$this->_ci->LehrverbandModel->db->group_start();
|
|
||||||
$this->_ci->LehrverbandModel->db->where('CONCAT(CONCAT(typ, kurzbz), \'\', semester, verband, COALESCE(gruppe,\'\')) ILIKE', "%" . $word . "%");
|
|
||||||
$this->_ci->LehrverbandModel->db->or_where('tbl_lehrverband.bezeichnung ILIKE', "%" . $word . "%");
|
|
||||||
$this->_ci->LehrverbandModel->db->group_end();
|
|
||||||
}
|
|
||||||
$this->_ci->LehrverbandModel->db->group_end();
|
|
||||||
$lehrverband_result = $this->_ci->LehrverbandModel->load();
|
|
||||||
|
|
||||||
$lehrverband_array = array();
|
$lehrverband_array = array();
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,25 @@ class StgTree extends FHCAPI_Controller
|
|||||||
return $this->_outputAuthError([$method => ['admin:r', 'assistenz:r']]);
|
return $this->_outputAuthError([$method => ['admin:r', 'assistenz:r']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->getStudiengang($method);
|
$count = count($params);
|
||||||
|
if (!$count)
|
||||||
|
return $this->getStudiengang($method);
|
||||||
|
|
||||||
|
if ($count == 1) {
|
||||||
|
if (is_numeric($params[0]))
|
||||||
|
return $this->getSemester($method, $params[0]);
|
||||||
|
else
|
||||||
|
return $this->getStudiengang($method, $params[0]);
|
||||||
|
}
|
||||||
|
if ($count == 2) {
|
||||||
|
if (is_numeric($params[0]))
|
||||||
|
return $this->getVerband($method, $params[0], $params[1]);
|
||||||
|
else
|
||||||
|
return $this->getSemester($method, $params[1], $params[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
show_404();
|
show_404();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,16 +82,27 @@ class StgTree extends FHCAPI_Controller
|
|||||||
$this->terminateWithSuccess($list);
|
$this->terminateWithSuccess($list);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getStudiengang($studiengang_kz)
|
protected function getStudiengang($studiengang_kz, $org_form = null)
|
||||||
{
|
{
|
||||||
$link = $studiengang_kz . '/';
|
$link = $studiengang_kz . '/';
|
||||||
|
if ($org_form !== null)
|
||||||
|
$link .= $org_form . '/';
|
||||||
|
|
||||||
$this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz');
|
$this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz');
|
||||||
|
|
||||||
$this->StudiengangModel->addDistinct();
|
$this->StudiengangModel->addDistinct();
|
||||||
$this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", semester) AS link", false);
|
$this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", semester) AS link", false);
|
||||||
$this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester ORDER BY verband, gruppe LIMIT 1)) AS name", false);
|
$this->StudiengangModel->addSelect("CONCAT(
|
||||||
$this->StudiengangModel->addSelect("TRUE AS leaf", false);
|
UPPER(CONCAT(typ, kurzbz)),
|
||||||
|
'-',
|
||||||
|
semester,
|
||||||
|
(
|
||||||
|
SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END
|
||||||
|
FROM public.tbl_lehrverband
|
||||||
|
WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester
|
||||||
|
ORDER BY verband, gruppe LIMIT 1
|
||||||
|
)
|
||||||
|
) AS name", false);
|
||||||
|
|
||||||
$this->StudiengangModel->addSelect('semester');
|
$this->StudiengangModel->addSelect('semester');
|
||||||
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
|
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
|
||||||
@@ -97,6 +126,8 @@ class StgTree extends FHCAPI_Controller
|
|||||||
$this->StudienordnungModel->addDistinct();
|
$this->StudienordnungModel->addDistinct();
|
||||||
$this->StudienordnungModel->addSelect("CONCAT(studiengang_kz, '/', p.orgform_kurzbz) AS link");
|
$this->StudienordnungModel->addSelect("CONCAT(studiengang_kz, '/', p.orgform_kurzbz) AS link");
|
||||||
$this->StudienordnungModel->addSelect("p.orgform_kurzbz AS name");
|
$this->StudienordnungModel->addSelect("p.orgform_kurzbz AS name");
|
||||||
|
$this->StudienordnungModel->addSelect("p.orgform_kurzbz AS orgform_kurzbz");
|
||||||
|
$this->StudienordnungModel->addSelect("studiengang_kz AS stg_kz");
|
||||||
$this->StudienordnungModel->addSelect("TRUE as leaf", false);
|
$this->StudienordnungModel->addSelect("TRUE as leaf", false);
|
||||||
|
|
||||||
$this->StudienordnungModel->addJoin('lehre.tbl_studienplan p', 'studienordnung_id');
|
$this->StudienordnungModel->addJoin('lehre.tbl_studienplan p', 'studienordnung_id');
|
||||||
@@ -111,6 +142,115 @@ class StgTree extends FHCAPI_Controller
|
|||||||
$list = array_merge($list, $result);
|
$list = array_merge($list, $result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$this->terminateWithSuccess($list);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSemester($studiengang_kz, $semester, $org_form = null)
|
||||||
|
{
|
||||||
|
$link = $studiengang_kz . '/';
|
||||||
|
if ($org_form !== null)
|
||||||
|
$link .= $org_form . '/';
|
||||||
|
$link .= $semester . '/';
|
||||||
|
|
||||||
|
|
||||||
|
$this->load->model('organisation/Gruppe_model', 'GruppeModel');
|
||||||
|
|
||||||
|
$this->GruppeModel->addDistinct();
|
||||||
|
$this->GruppeModel->addSelect("CONCAT(" . $this->GruppeModel->escape($link . 'grp/') . ", gruppe_kurzbz) AS link", false);
|
||||||
|
$this->GruppeModel->addSelect("CONCAT(gruppe_kurzbz, ' (', bezeichnung, ')') AS name", false);
|
||||||
|
$this->GruppeModel->addSelect("TRUE AS leaf", false);
|
||||||
|
|
||||||
|
$this->GruppeModel->addSelect('sort');
|
||||||
|
$this->GruppeModel->addSelect('gruppe_kurzbz');
|
||||||
|
$this->GruppeModel->addSelect($this->GruppeModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
|
||||||
|
|
||||||
|
$this->GruppeModel->addOrder('sort');
|
||||||
|
$this->GruppeModel->addOrder('gruppe_kurzbz');
|
||||||
|
|
||||||
|
$where = [
|
||||||
|
'studiengang_kz' => $studiengang_kz,
|
||||||
|
'semester' => $semester,
|
||||||
|
'lehre' => true,
|
||||||
|
'sichtbar' => true,
|
||||||
|
'aktiv' => true,
|
||||||
|
'direktinskription' => false
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($org_form !== null)
|
||||||
|
$where['orgform_kurzbz'] = $org_form;
|
||||||
|
|
||||||
|
$result = $this->GruppeModel->loadWhere($where);
|
||||||
|
|
||||||
|
$list = $this->getDataOrTerminateWithError($result);
|
||||||
|
|
||||||
|
$this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz');
|
||||||
|
|
||||||
|
$this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", verband) AS link", false);
|
||||||
|
$this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, verband, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester AND verband=v.verband ORDER BY gruppe LIMIT 1)) AS name", false);
|
||||||
|
$this->StudiengangModel->addSelect("CASE WHEN MAX(gruppe)='' OR MAX(gruppe)=' ' THEN TRUE ELSE FALSE END AS leaf");
|
||||||
|
|
||||||
|
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($semester) . ' AS semester');
|
||||||
|
$this->StudiengangModel->addSelect('verband');
|
||||||
|
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
|
||||||
|
|
||||||
|
$this->StudiengangModel->addOrder('verband');
|
||||||
|
|
||||||
|
$this->StudiengangModel->addGroupBy('link, name, verband');
|
||||||
|
|
||||||
|
$where = [
|
||||||
|
'v.studiengang_kz' => $studiengang_kz,
|
||||||
|
'v.semester' => $semester,
|
||||||
|
'v.verband !=' => '',
|
||||||
|
'v.aktiv' => true
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($org_form !== null && $semester) // NOTE(chris): on semester 0 show all?
|
||||||
|
$where['v.orgform_kurzbz'] = $org_form;
|
||||||
|
|
||||||
|
$result = $this->StudiengangModel->loadWhere($where);
|
||||||
|
$result = $this->getDataOrTerminateWithError($result);
|
||||||
|
|
||||||
|
$list = array_merge($list, $result);
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($list);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getVerband($studiengang_kz, $semester, $verband, $org_form = null)
|
||||||
|
{
|
||||||
|
$link = $studiengang_kz . '/';
|
||||||
|
if ($org_form !== null)
|
||||||
|
$link .= $org_form . '/';
|
||||||
|
$link .= $semester . '/'. $verband . '/';
|
||||||
|
|
||||||
|
|
||||||
|
$this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz');
|
||||||
|
|
||||||
|
$this->StudiengangModel->addDistinct();
|
||||||
|
$this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", gruppe) AS link", false);
|
||||||
|
$this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, verband, gruppe, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester AND verband=v.verband AND gruppe=v.gruppe ORDER BY gruppe LIMIT 1)) AS name", false);
|
||||||
|
$this->StudiengangModel->addSelect("TRUE AS leaf", false);
|
||||||
|
|
||||||
|
$this->StudiengangModel->addSelect('v.semester');
|
||||||
|
$this->StudiengangModel->addSelect('v.verband');
|
||||||
|
$this->StudiengangModel->addSelect('gruppe');
|
||||||
|
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
|
||||||
|
|
||||||
|
$this->StudiengangModel->addOrder('gruppe');
|
||||||
|
|
||||||
|
$where = [
|
||||||
|
'v.studiengang_kz' => $studiengang_kz,
|
||||||
|
'v.semester' => $semester,
|
||||||
|
'v.verband' => $verband,
|
||||||
|
'v.gruppe !=' => '',
|
||||||
|
'v.aktiv' => true
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($org_form !== null && $semester)
|
||||||
|
$where['v.orgform_kurzbz'] = $org_form;
|
||||||
|
|
||||||
|
$result = $this->StudiengangModel->loadWhere($where);
|
||||||
|
|
||||||
|
$list = $this->getDataOrTerminateWithError($result);
|
||||||
|
|
||||||
$this->terminateWithSuccess($list);
|
$this->terminateWithSuccess($list);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined('BASEPATH'))
|
||||||
|
exit('No direct script access allowed');
|
||||||
|
|
||||||
|
|
||||||
|
class Config extends FHCAPI_Controller
|
||||||
|
{
|
||||||
|
private $_ci;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct([
|
||||||
|
'get' => ['admin:r', 'assistenz:r'],
|
||||||
|
'getHeader' => ['admin:r', 'assistenz:r'],
|
||||||
|
'set' => ['admin:r', 'assistenz:r'],
|
||||||
|
'updateCollision' => ['admin:r', 'assistenz:r'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Load Phrases
|
||||||
|
$this->loadPhrases([
|
||||||
|
'ui',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->_ci = &get_instance();
|
||||||
|
$this->_ci->load->model('ressource/Kalenderstatus_model', 'KalenderStatusModel');
|
||||||
|
$this->_ci->load->model('system/Variable_model', 'VariableModel');
|
||||||
|
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get()
|
||||||
|
{
|
||||||
|
$config = [];
|
||||||
|
|
||||||
|
$result = $this->_ci->VariableModel->getVariables(getAuthUID(), ['ignore_kollision', 'kollision_student', 'ignore_reservierung', 'ignore_zeitsperre']);
|
||||||
|
|
||||||
|
$data = $this->getDataOrTerminateWithError($result);
|
||||||
|
$config['ignore_kollision'] = [
|
||||||
|
"type" => "checkbox",
|
||||||
|
"label" => $this->p->t('ui', 'ignore_kollision'),
|
||||||
|
"value" => ($data['ignore_kollision'] ?? 'false') === 'true'
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
$config['kollision_student'] = [
|
||||||
|
"type" => "checkbox",
|
||||||
|
"label" => $this->p->t('ui', 'kollision_student'),
|
||||||
|
"value" => ($data['kollision_student'] ?? 'false') === 'true'
|
||||||
|
];
|
||||||
|
|
||||||
|
$config['ignore_reservierung'] = [
|
||||||
|
"type" => "checkbox",
|
||||||
|
"label" => $this->p->t('ui', 'ignore_reservierung'),
|
||||||
|
"value" => ($data['ignore_reservierung'] ?? 'false') === 'true'
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
$config['ignore_zeitsperre'] = [
|
||||||
|
"type" => "checkbox",
|
||||||
|
"label" => $this->p->t('ui', 'ignore_zeitsperre'),
|
||||||
|
"value" => ($data['ignore_zeitsperre'] ?? 'false') === 'true'
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($config);
|
||||||
|
}
|
||||||
|
public function getHeader()
|
||||||
|
{
|
||||||
|
$language = getUserLanguage() == 'German' ? 0 : 1;
|
||||||
|
|
||||||
|
$this->_ci->KalenderStatusModel->addSelect('*, array_to_json(bezeichnung_mehrsprachig::varchar[])->>' . $language .' AS status');
|
||||||
|
$this->_ci->KalenderStatusModel->addOrder('sort');
|
||||||
|
$this->_ci->KalenderStatusModel->db->where_not_in('status_kurzbz', array('archived', 'deleted'));
|
||||||
|
$visible_status = $this->_ci->KalenderStatusModel->load();
|
||||||
|
|
||||||
|
$visible_status = getData($visible_status);
|
||||||
|
|
||||||
|
$config['visible_status']['all'] = 'Alle';
|
||||||
|
|
||||||
|
foreach ($visible_status as $status)
|
||||||
|
{
|
||||||
|
$config['visible_status'][$status->status_kurzbz] = $status->status;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set()
|
||||||
|
{
|
||||||
|
$this->_ci->VariableModel->setVariable(
|
||||||
|
getAuthUID(),
|
||||||
|
'ignore_kollision',
|
||||||
|
$this->input->post('ignore_kollision') === true ? 'true' : 'false'
|
||||||
|
);
|
||||||
|
$this->_ci->VariableModel->setVariable(
|
||||||
|
getAuthUID(),
|
||||||
|
'kollision_student',
|
||||||
|
$this->input->post('kollision_student') === true ? 'true' : 'false'
|
||||||
|
);
|
||||||
|
$this->_ci->VariableModel->setVariable(
|
||||||
|
getAuthUID(),
|
||||||
|
'ignore_reservierung',
|
||||||
|
$this->input->post('ignore_reservierung') === true ? 'true' : 'false'
|
||||||
|
);
|
||||||
|
$this->_ci->VariableModel->setVariable(
|
||||||
|
getAuthUID(),
|
||||||
|
'ignore_zeitsperre',
|
||||||
|
$this->input->post('ignore_zeitsperre') === true ? 'true' : 'false'
|
||||||
|
);
|
||||||
|
$this->terminateWithSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateCollision()
|
||||||
|
{
|
||||||
|
$original_ignore = $this->_ci->variablelib->getVar('ignore_kollision');
|
||||||
|
$this->_ci->VariableModel->setVariable(
|
||||||
|
getAuthUID(),
|
||||||
|
'ignore_kollision',
|
||||||
|
$original_ignore === 'true' ? 'false' : 'true'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||||
|
|
||||||
|
class Coursepicker extends FHCAPI_Controller
|
||||||
|
{
|
||||||
|
private $_ci;
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct([
|
||||||
|
'getByStg' => self::PERM_LOGGED
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->_ci = &get_instance();
|
||||||
|
$this->load->library('form_validation');
|
||||||
|
|
||||||
|
$this->_ci->load->model('education/lehreinheit_model', 'LehreinheitModel');
|
||||||
|
$this->_ci->load->model('education/Lehreinheitmitarbeiter_model', 'LehreinheitmitarbeiterModel');
|
||||||
|
|
||||||
|
|
||||||
|
$this->loadPhrases(['ui']);
|
||||||
|
}
|
||||||
|
public function getByStg()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_POST);
|
||||||
|
$this->_ci->form_validation->set_rules('studiengaenge[]',"studiengaenge","required");
|
||||||
|
$this->_ci->form_validation->set_rules('studiensemester_kurzbz',"studiensemester_kurzbz","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$studiengaenge = $this->input->post('studiengaenge');
|
||||||
|
$studiensemester_kurzbz = $this->input->post('studiensemester_kurzbz');
|
||||||
|
|
||||||
|
$this->_ci->LehreinheitModel->addSelect('
|
||||||
|
tbl_lehreinheit.lehreinheit_id,
|
||||||
|
tbl_lehreinheit.lvnr,
|
||||||
|
tbl_lehreinheit.lehrfach_id,
|
||||||
|
lehrfach.kurzbz AS lehrfach,
|
||||||
|
lehrfach.bezeichnung AS lehrfach_bez,
|
||||||
|
lehrfach.farbe AS lehrfach_farbe,
|
||||||
|
tbl_lehreinheit.lehrform_kurzbz AS lehrform,
|
||||||
|
lema.mitarbeiter_uid AS lektor_uid,
|
||||||
|
ma.kurzbz AS lektor,
|
||||||
|
tbl_person.vorname,
|
||||||
|
tbl_person.nachname,
|
||||||
|
tbl_studiengang.studiengang_kz,
|
||||||
|
upper(tbl_studiengang.typ::character varying::text || tbl_studiengang.kurzbz::text) AS studiengang,
|
||||||
|
lvb.semester,
|
||||||
|
lvb.verband,
|
||||||
|
lvb.gruppe,
|
||||||
|
lvb.gruppe_kurzbz,
|
||||||
|
tbl_lehreinheit.raumtyp,
|
||||||
|
tbl_lehreinheit.raumtypalternativ,
|
||||||
|
tbl_lehreinheit.stundenblockung,
|
||||||
|
tbl_lehreinheit.wochenrythmus,
|
||||||
|
lema.semesterstunden,
|
||||||
|
lema.planstunden,
|
||||||
|
tbl_lehreinheit.start_kw,
|
||||||
|
tbl_lehreinheit.anmerkung,
|
||||||
|
tbl_lehreinheit.studiensemester_kurzbz
|
||||||
|
');
|
||||||
|
$this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehreinheitmitarbeiter lema', 'tbl_lehreinheit.lehreinheit_id = lema.lehreinheit_id');
|
||||||
|
$this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehreinheitgruppe lvb', 'tbl_lehreinheit.lehreinheit_id = lvb.lehreinheit_id');
|
||||||
|
$this->_ci->LehreinheitModel->addJoin('public.tbl_studiengang', 'lvb.studiengang_kz = tbl_studiengang.studiengang_kz');
|
||||||
|
$this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrveranstaltung', 'tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id');
|
||||||
|
$this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrveranstaltung lehrfach', 'tbl_lehreinheit.lehrfach_id = lehrfach.lehrveranstaltung_id');
|
||||||
|
$this->_ci->LehreinheitModel->addJoin('public.tbl_mitarbeiter ma', 'lema.mitarbeiter_uid = ma.mitarbeiter_uid');
|
||||||
|
$this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrform', 'tbl_lehrform.lehrform_kurzbz = tbl_lehreinheit.lehrform_kurzbz');
|
||||||
|
$this->_ci->LehreinheitModel->addJoin('public.tbl_benutzer', 'ma.mitarbeiter_uid = tbl_benutzer.uid');
|
||||||
|
$this->_ci->LehreinheitModel->addJoin('public.tbl_person', 'tbl_benutzer.person_id = tbl_person.person_id');
|
||||||
|
|
||||||
|
$this->_ci->LehreinheitModel->db->group_start();
|
||||||
|
$first = true;
|
||||||
|
foreach ($studiengaenge as $studiengang)
|
||||||
|
{
|
||||||
|
if ($first)
|
||||||
|
$this->_ci->LehreinheitModel->db->group_start();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->_ci->LehreinheitModel->db->or_group_start();
|
||||||
|
}
|
||||||
|
$first = false;
|
||||||
|
|
||||||
|
$this->_ci->LehreinheitModel->db->where('tbl_lehrveranstaltung.studiengang_kz', $studiengang['studiengang_kz']);
|
||||||
|
|
||||||
|
if (isset($studiengang['semester']))
|
||||||
|
$this->_ci->LehreinheitModel->db->where('tbl_lehrveranstaltung.semester', $studiengang['semester']);
|
||||||
|
|
||||||
|
if (isset($studiengang['orgform_kurzbz']))
|
||||||
|
$this->_ci->LehreinheitModel->db->where('tbl_lehrveranstaltung.orgform_kurzbz', $studiengang['orgform_kurzbz']);
|
||||||
|
|
||||||
|
$this->_ci->LehreinheitModel->db->group_end();
|
||||||
|
}
|
||||||
|
$this->_ci->LehreinheitModel->db->group_end();
|
||||||
|
|
||||||
|
$result = $this->_ci->LehreinheitModel->loadWhere(array(
|
||||||
|
'tbl_lehrform.verplanen' => true,
|
||||||
|
'tbl_lehreinheit.studiensemester_kurzbz' => $studiensemester_kurzbz
|
||||||
|
));
|
||||||
|
|
||||||
|
$result = hasData($result) ? getData($result) : array();
|
||||||
|
|
||||||
|
$lehreinheit_ids = array_values(array_unique(array_column($result, 'lehreinheit_id')));
|
||||||
|
|
||||||
|
$offen_map = array();
|
||||||
|
if (!empty($lehreinheit_ids))
|
||||||
|
{
|
||||||
|
$offen_result = $this->_ci->LehreinheitModel->getOffeneStunden($lehreinheit_ids);
|
||||||
|
if (hasData($offen_result))
|
||||||
|
{
|
||||||
|
foreach (getData($offen_result) as $row)
|
||||||
|
{
|
||||||
|
$offen_map[$row->lehreinheit_id . '_' . $row->mitarbeiter_uid] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$grouped = array();
|
||||||
|
|
||||||
|
foreach ($result as $row)
|
||||||
|
{
|
||||||
|
$lehreinheit_id = $row->lehreinheit_id;
|
||||||
|
if (!isset($grouped[$lehreinheit_id]))
|
||||||
|
{
|
||||||
|
$grouped[$lehreinheit_id] = (object)array(
|
||||||
|
'lehrfach_id' => $row->lehrfach_id,
|
||||||
|
'lehrfach_bez' => $row->lehrfach_bez,
|
||||||
|
'lehrfach_farbe' => $row->lehrfach_farbe,
|
||||||
|
'studiengang_kz' => $row->studiengang_kz,
|
||||||
|
'studiengang' => $row->studiengang,
|
||||||
|
'semester' => $row->semester,
|
||||||
|
'verband' => $row->verband,
|
||||||
|
'gruppe' => $row->gruppe,
|
||||||
|
'gruppe_kurzbz' => $row->gruppe_kurzbz,
|
||||||
|
'raumtyp' => $row->raumtyp,
|
||||||
|
'raumtypalternativ' => $row->raumtypalternativ,
|
||||||
|
'anmerkung' => $row->anmerkung,
|
||||||
|
'studiensemester_kurzbz' => $row->studiensemester_kurzbz,
|
||||||
|
'fachbereich_kurzbz' => isset($row->fachbereich_kurzbz) ? $row->fachbereich_kurzbz : null,
|
||||||
|
'lektoren' => array(),
|
||||||
|
'lehreinheit_id' => $lehreinheit_id,
|
||||||
|
'lvnr' => array(),
|
||||||
|
'lehrfach' => array(),
|
||||||
|
'lehrform' => array(),
|
||||||
|
'stundenblockung' => array(),
|
||||||
|
'wochenrythmus' => array(),
|
||||||
|
'planstunden' => array(),
|
||||||
|
'start_kw' => array(),
|
||||||
|
'verplant' => array(),
|
||||||
|
'offenestunden' => array(),
|
||||||
|
'lehrverband' => array(),
|
||||||
|
'lem' => array(),
|
||||||
|
'verplant_gesamt' => 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$group = $grouped[$lehreinheit_id];
|
||||||
|
|
||||||
|
$group->lektoren[$row->lektor_uid] = (object)array(
|
||||||
|
'uid' => $row->lektor_uid,
|
||||||
|
'kurzbz' => trim($row->lektor),
|
||||||
|
'name' => $row->vorname . ' ' . $row->nachname,
|
||||||
|
);
|
||||||
|
|
||||||
|
$group->lvnr[] = $row->lvnr;
|
||||||
|
$group->lehrfach[] = $row->lehrfach;
|
||||||
|
$group->lehrform[] = $row->lehrform;
|
||||||
|
$group->stundenblockung[] = $row->stundenblockung;
|
||||||
|
$group->wochenrythmus[] = $row->wochenrythmus;
|
||||||
|
$group->planstunden[] = $row->planstunden;
|
||||||
|
$group->start_kw[] = $row->start_kw;
|
||||||
|
|
||||||
|
$offen_key = $lehreinheit_id . '_' . $row->lektor_uid;
|
||||||
|
$offen = isset($offen_map[$offen_key]) ? $offen_map[$offen_key] : null;
|
||||||
|
$verplant_wert = $offen ? $offen->verplant : 0;
|
||||||
|
$offenestunden_wert = $offen ? $offen->offenestunden : 0;
|
||||||
|
|
||||||
|
$group->verplant[] = $verplant_wert;
|
||||||
|
$group->offenestunden[] = $offenestunden_wert;
|
||||||
|
$group->verplant_gesamt += $verplant_wert;
|
||||||
|
|
||||||
|
$lvb = $row->studiengang . '-' . $row->semester;
|
||||||
|
|
||||||
|
if ($row->verband != '' && $row->verband != ' ' && $row->verband != '0' && $row->verband != null)
|
||||||
|
$lvb .= $row->verband;
|
||||||
|
|
||||||
|
if ($row->gruppe != '' && $row->gruppe != ' ' && $row->gruppe != '0' && $row->gruppe != null)
|
||||||
|
$lvb .= $row->gruppe;
|
||||||
|
|
||||||
|
$group->lehrverband[] = ($row->gruppe_kurzbz != '' && $row->gruppe_kurzbz != null) ? $row->gruppe_kurzbz : $lvb;
|
||||||
|
|
||||||
|
$group->lem[] = array(
|
||||||
|
'lehreinheit_id' => $row->lehreinheit_id,
|
||||||
|
'mitarbeiter_uid' => $row->lektor_uid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($grouped as $group)
|
||||||
|
{
|
||||||
|
$group->lektoren = array_values($group->lektoren);
|
||||||
|
$group->lehrverband = array_values(array_unique($group->lehrverband));
|
||||||
|
$group->lehrfach = $this->_formatArr($group->lehrfach);
|
||||||
|
$group->lehrform = $this->_formatArr($group->lehrform);
|
||||||
|
$group->stundenblockung = $this->_formatArr($group->stundenblockung);
|
||||||
|
$group->wochenrythmus = $this->_formatArr($group->wochenrythmus);
|
||||||
|
$group->planstunden = $this->_formatArr($group->planstunden);
|
||||||
|
$group->start_kw = $this->_formatArr($group->start_kw);
|
||||||
|
$group->verplant = $this->_formatArr($group->verplant);
|
||||||
|
$group->offenestunden = $this->_formatArr($group->offenestunden);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(array_values($grouped));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _formatArr($arr)
|
||||||
|
{
|
||||||
|
$values = array_values(array_unique($arr));
|
||||||
|
$formatted = implode(' ', $values);
|
||||||
|
|
||||||
|
if (count($values) > 1)
|
||||||
|
$formatted .= ' ?';
|
||||||
|
|
||||||
|
return $formatted;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||||
|
|
||||||
|
class Favorites extends FHCAPI_Controller
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct([
|
||||||
|
'index' => self::PERM_LOGGED,
|
||||||
|
'set' => self::PERM_LOGGED
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Load models
|
||||||
|
$this->load->model('system/Variable_model', 'VariableModel');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$result = $this->VariableModel->getVariables(getAuthUID(), ['tempus_favorites']);
|
||||||
|
|
||||||
|
$data = $this->getDataOrTerminateWithError($result);
|
||||||
|
|
||||||
|
if (!$data)
|
||||||
|
$this->terminateWithSuccess(null);
|
||||||
|
else
|
||||||
|
$this->terminateWithSuccess(isset($data['tempus_favorites']) ? $data['tempus_favorites'] : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set()
|
||||||
|
{
|
||||||
|
$this->load->library('form_validation');
|
||||||
|
|
||||||
|
$this->form_validation->set_rules('favorites', 'Favorites', 'required');
|
||||||
|
|
||||||
|
if (!$this->form_validation->run())
|
||||||
|
$this->terminateWithValidationErrors($this->form_validation->error_array());
|
||||||
|
|
||||||
|
$favorites = $this->input->post('favorites');
|
||||||
|
|
||||||
|
$result = $this->VariableModel->setVariable(getAuthUID(), 'tempus_favorites', $favorites);
|
||||||
|
|
||||||
|
$this->getDataOrTerminateWithError($result);
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||||
|
|
||||||
|
class Info extends FHCAPI_Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
private $_ci;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object initialization
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct([
|
||||||
|
'getStudiengaenge' => self::PERM_LOGGED,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->_ci =& get_instance();
|
||||||
|
|
||||||
|
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||||
|
|
||||||
|
$this->loadPhrases([
|
||||||
|
'ui'
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStudiengaenge()
|
||||||
|
{
|
||||||
|
$this->_ci->StudiengangModel->addDistinct();
|
||||||
|
$this->_ci->StudiengangModel->addSelect('studiengang_kz');
|
||||||
|
$this->_ci->StudiengangModel->addSelect("CONCAT(kurzbzlang, ' (', UPPER(CONCAT(typ, kurzbz)), ') - ', tbl_studiengang.bezeichnung) AS name");
|
||||||
|
|
||||||
|
$result = $this->getDataOrTerminateWithError($this->_ci->StudiengangModel->load());
|
||||||
|
$this->terminateWithSuccess($result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,438 @@
|
|||||||
|
<?php
|
||||||
|
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||||
|
|
||||||
|
class Kalender extends FHCAPI_Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
private $_ci;
|
||||||
|
const ALLOWED_PLAN_FILTER = ['ort', 'uid', 'stg'];
|
||||||
|
const ALLOWED_ROOM_FILTER = ['lehreinheit_id', 'kalender_id'];
|
||||||
|
|
||||||
|
const ALLOWED_TO_UPDATE = ['start_time', 'end_time', 'ort_kurzbz'];
|
||||||
|
/**
|
||||||
|
* Object initialization
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct([
|
||||||
|
'getStunden' => self::PERM_LOGGED,
|
||||||
|
'getCalendarHours' => self::PERM_LOGGED,
|
||||||
|
'getPlan' => self::PERM_LOGGED,
|
||||||
|
'getPlanByOrt' => self::PERM_LOGGED,
|
||||||
|
'getRaumvorschlag' => self::PERM_LOGGED,
|
||||||
|
'getHistory' => 'lehre/lvplan:rw',
|
||||||
|
'deleteEntry' => 'lehre/lvplan:rw',
|
||||||
|
'syncToLecturer' => 'lehre/lvplan:rw',
|
||||||
|
'syncToStudent' => 'lehre/lvplan:rw',
|
||||||
|
'getPlanLecturer' =>'lehre/lvplan:rw',
|
||||||
|
'getPlanStudent' => 'lehre/lvplan:rw',
|
||||||
|
'getZeitwuensche' => self::PERM_LOGGED,
|
||||||
|
'getZeitsperren' => self::PERM_LOGGED,
|
||||||
|
'updateKalenderEvent' => 'lehre/lvplan:rw',
|
||||||
|
'addKalenderEvent' => 'lehre/lvplan:rw',
|
||||||
|
'calculateMultiWeekPlan' => 'lehre/lvplan:rw',
|
||||||
|
'confirmMultiWeekPlan' => 'lehre/lvplan:rw',
|
||||||
|
'addToKalenderEvent' => 'lehre/lvplan:rw',
|
||||||
|
'sync' => 'lehre/lvplan:rw',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->_ci =& get_instance();
|
||||||
|
|
||||||
|
$this->_ci->load->library('LogLib');
|
||||||
|
$this->_ci->load->library('form_validation');
|
||||||
|
$this->_ci->load->library('KalenderLib');
|
||||||
|
$this->_ci->load->library('KalenderSyncLib');
|
||||||
|
$this->_ci->load->library('RaumvorschlagLib');
|
||||||
|
$this->loadPhrases([
|
||||||
|
'ui'
|
||||||
|
]);
|
||||||
|
$this->_ci->load->config('tempus');
|
||||||
|
|
||||||
|
|
||||||
|
$this->_ci->loglib->setConfigs(array(
|
||||||
|
'classIndex' => 5,
|
||||||
|
'functionIndex' => 5,
|
||||||
|
'lineIndex' => 4,
|
||||||
|
'dbLogType' => 'API', // required
|
||||||
|
'dbExecuteUser' => 'RESTful API'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Public methods
|
||||||
|
/**
|
||||||
|
* fetches Stunden layout from database
|
||||||
|
* @access public
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function getStunden()
|
||||||
|
{
|
||||||
|
$this->load->model('ressource/Stunde_model', 'StundeModel');
|
||||||
|
|
||||||
|
$this->_ci->StundeModel->addOrder('stunde', 'ASC');
|
||||||
|
$stunden = $this->_ci->StundeModel->load();
|
||||||
|
|
||||||
|
$stunden = $this->getDataOrTerminateWithError($stunden);
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($stunden);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCalendarHours()
|
||||||
|
{
|
||||||
|
$calender_start = $this->_ci->config->item('calendar_start') ?? 7;
|
||||||
|
$calender_end = $this->_ci->config->item('calendar_end') ?? 23;
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(array(
|
||||||
|
'start' => $calender_start,
|
||||||
|
'end' => $calender_end
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlan()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_POST);
|
||||||
|
$this->_ci->form_validation->set_rules('start_date',"start_date","required");
|
||||||
|
$this->_ci->form_validation->set_rules('end_date',"end_date","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$start_date = $this->_ci->input->post('start_date', TRUE);
|
||||||
|
$end_date = $this->_ci->input->post('end_date', TRUE);
|
||||||
|
|
||||||
|
$filter = $this->_checkFilter(self::ALLOWED_PLAN_FILTER);
|
||||||
|
|
||||||
|
$stundenplan_data = $this->_ci->kalenderlib->getPlanForPlanner(
|
||||||
|
$start_date,
|
||||||
|
$end_date,
|
||||||
|
isset($filter->ort) ? $filter->ort : null,
|
||||||
|
isset($filter->uid) ? $filter->uid : null,
|
||||||
|
isset($filter->stg) ? $filter->stg : null
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($stundenplan_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlanStudent()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_GET);
|
||||||
|
$this->_ci->form_validation->set_rules('start_date',"start_date","required");
|
||||||
|
$this->_ci->form_validation->set_rules('end_date',"end_date","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$start_date = $this->_ci->input->get('start_date', TRUE);
|
||||||
|
$end_date = $this->_ci->input->get('end_date', TRUE);
|
||||||
|
|
||||||
|
$stundenplan_data = $this->_ci->kalenderlib->getPlanForStudent(
|
||||||
|
$start_date,
|
||||||
|
$end_date
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($stundenplan_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlanLecturer()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_GET);
|
||||||
|
$this->_ci->form_validation->set_rules('start_date',"start_date","required");
|
||||||
|
$this->_ci->form_validation->set_rules('end_date',"end_date","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$start_date = $this->_ci->input->get('start_date', TRUE);
|
||||||
|
$end_date = $this->_ci->input->get('end_date', TRUE);
|
||||||
|
|
||||||
|
$stundenplan_data = $this->_ci->kalenderlib->getPlanForLecturer(
|
||||||
|
$start_date,
|
||||||
|
$end_date
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($stundenplan_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlanByOrt($start_date = null, $end_date = null, $ort = null)
|
||||||
|
{
|
||||||
|
if (!isset($start_date) || !isset($end_date) || !isset($ort))
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_GET);
|
||||||
|
$this->_ci->form_validation->set_rules('start_date',"start_date","required");
|
||||||
|
$this->_ci->form_validation->set_rules('end_date',"end_date","required");
|
||||||
|
$this->_ci->form_validation->set_rules('ort',"ort","required");
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$start_date = $this->_ci->input->get('start_date', TRUE);
|
||||||
|
$end_date = $this->_ci->input->get('end_date', TRUE);
|
||||||
|
$ort = $this->_ci->input->get('ort', TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($this->_ci->kalenderlib->getPlanByOrt($start_date, $end_date, $ort));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getZeitsperren()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_GET);
|
||||||
|
$this->_ci->form_validation->set_rules('start_date',"start_date","required");
|
||||||
|
$this->_ci->form_validation->set_rules('end_date',"end_date","required");
|
||||||
|
$this->_ci->form_validation->set_rules('emp',"emp","required");
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$start_date = $this->_ci->input->get('start_date', TRUE);
|
||||||
|
$end_date = $this->_ci->input->get('end_date', TRUE);
|
||||||
|
$emp = $this->_ci->input->get('emp', TRUE);
|
||||||
|
|
||||||
|
$stundenplan_data = $this->_ci->kalenderlib->getZeitsperren($start_date, $end_date, $emp);
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($stundenplan_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getZeitwuensche()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_GET);
|
||||||
|
$this->_ci->form_validation->set_rules('start_date',"start_date","required");
|
||||||
|
$this->_ci->form_validation->set_rules('end_date',"end_date","required");
|
||||||
|
$this->_ci->form_validation->set_rules('emp',"emp","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$start_date = $this->_ci->input->get('start_date', TRUE);
|
||||||
|
$end_date = $this->_ci->input->get('end_date', TRUE);
|
||||||
|
$emp = $this->_ci->input->get('emp', TRUE);
|
||||||
|
|
||||||
|
$stundenplan_data = $this->_ci->kalenderlib->getZeitwuensche($start_date, $end_date, $emp);
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($stundenplan_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateKalenderEvent()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_POST);
|
||||||
|
$this->_ci->form_validation->set_rules('kalender_id',"kalender_id","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$updateFields = $this->_checkUpdate($this->_ci->input->post('updatedInfos', TRUE));
|
||||||
|
$kalender_id = $this->_ci->input->post('kalender_id', TRUE);
|
||||||
|
|
||||||
|
$result = $this->_ci->kalenderlib->updateKalenderEvent($kalender_id, $updateFields->ort_kurzbz ?? null, $updateFields->start_time ?? null, $updateFields->end_time ?? null);
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
$this->terminateWithError(getError($result), $result->code);
|
||||||
|
|
||||||
|
$this->terminateWithSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRaumvorschlag()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_POST);
|
||||||
|
|
||||||
|
$filter = $this->_checkFilter(self::ALLOWED_ROOM_FILTER);
|
||||||
|
$this->terminateWithSuccess($this->_ci->raumvorschlaglib->getVorschlaege($filter->kalender_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHistory()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_GET);
|
||||||
|
$this->_ci->form_validation->set_rules('kalender_id',"kalender_id","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$kalender_id = $this->_ci->input->get('kalender_id', TRUE);
|
||||||
|
|
||||||
|
$result = $this->_ci->kalenderlib->getHistory($kalender_id);
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
$this->terminateWithError(getError($result));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(getData($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteEntry()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_POST);
|
||||||
|
$this->_ci->form_validation->set_rules('kalender_id', "kalender_id", "required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$kalender_id = $this->_ci->input->post('kalender_id', TRUE);
|
||||||
|
|
||||||
|
$result = $this->_ci->kalenderlib->deleteEntry($kalender_id);
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
$this->terminateWithError(getError($result));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(getData($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sync()
|
||||||
|
{
|
||||||
|
$result = $this->_ci->kalendersynclib->sync();
|
||||||
|
$this->terminateWithSuccess(getData($result));
|
||||||
|
}
|
||||||
|
public function syncToLecturer()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_POST);
|
||||||
|
$this->_ci->form_validation->set_rules('kalender_id', "kalender_id", "required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$kalender_id = $this->_ci->input->post('kalender_id', TRUE);
|
||||||
|
|
||||||
|
$result = $this->_ci->kalenderlib->updateStatus($kalender_id, 'sync_preview');
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
$this->terminateWithError(getError($result));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(getData($result));
|
||||||
|
}
|
||||||
|
public function syncToStudent()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_POST);
|
||||||
|
$this->_ci->form_validation->set_rules('kalender_id', "kalender_id", "required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$kalender_id = $this->_ci->input->post('kalender_id', TRUE);
|
||||||
|
|
||||||
|
$result = $this->_ci->kalenderlib->updateStatus($kalender_id, 'sync_live');
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
$this->terminateWithError(getError($result));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(getData($result));
|
||||||
|
}
|
||||||
|
public function addKalenderEvent()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_POST);
|
||||||
|
$this->_ci->form_validation->set_rules('lehreinheit_id',"lehreinheit_id","required");
|
||||||
|
$this->_ci->form_validation->set_rules('start_date',"start_date","required");
|
||||||
|
$this->_ci->form_validation->set_rules('end_date',"end_date","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$lehreinheit_id = $this->_ci->input->post('lehreinheit_id', TRUE);
|
||||||
|
$ort_kurzbz = $this->_ci->input->post('ort_kurzbz', TRUE);
|
||||||
|
$start_date = $this->_ci->input->post('start_date', TRUE);
|
||||||
|
$end_date = $this->_ci->input->post('end_date', TRUE);
|
||||||
|
$result = $this->_ci->kalenderlib->addKalenderEvent($start_date, $end_date, $lehreinheit_id, $ort_kurzbz);
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
$this->terminateWithError(getError($result));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(getData($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function calculateMultiWeekPlan()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_POST);
|
||||||
|
$this->_ci->form_validation->set_rules('lehreinheit_id',"lehreinheit_id","required");
|
||||||
|
$this->_ci->form_validation->set_rules('start_date',"start_date","required");
|
||||||
|
$this->_ci->form_validation->set_rules('end_date',"end_date","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$lehreinheit_id = $this->_ci->input->post('lehreinheit_id', TRUE);
|
||||||
|
$ort_kurzbz = $this->_ci->input->post('ort_kurzbz', TRUE);
|
||||||
|
$start_date = $this->_ci->input->post('start_date', TRUE);
|
||||||
|
$end_date = $this->_ci->input->post('end_date', TRUE);
|
||||||
|
|
||||||
|
$result = $this->_ci->kalenderlib->calculateMultiWeekPlan($start_date, $end_date, $lehreinheit_id, $ort_kurzbz);
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
$this->terminateWithError(getError($result));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(getData($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function confirmMultiWeekPlan()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_POST);
|
||||||
|
$this->_ci->form_validation->set_rules('plan[]',"plan","required");
|
||||||
|
$this->_ci->form_validation->set_rules('lehreinheit_id',"lehreinheit_id","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$plan = $this->_ci->input->post('plan', TRUE);
|
||||||
|
$lehreinheit_id = $this->_ci->input->post('lehreinheit_id', TRUE);
|
||||||
|
|
||||||
|
$result = $this->_ci->kalenderlib->confirmMultiWeekPlan($plan, $lehreinheit_id);
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
$this->terminateWithError(getError($result));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(getData($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addToKalenderEvent()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_POST);
|
||||||
|
$this->_ci->form_validation->set_rules('target_kalender_id',"target_kalender_id","required");
|
||||||
|
$this->_ci->form_validation->set_rules('lehreinheit_id',"lehreinheit_id","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$target_kalender_id = $this->_ci->input->post('target_kalender_id', TRUE);
|
||||||
|
$lehreinheit_id = $this->_ci->input->post('lehreinheit_id', TRUE);
|
||||||
|
|
||||||
|
$result = $this->_ci->kalenderlib->addToKalenderEvent($target_kalender_id, $lehreinheit_id);
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
$this->terminateWithError(getError($result));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(getData($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function _checkFilter($filters)
|
||||||
|
{
|
||||||
|
$filter_valid = true;
|
||||||
|
$filter_object = new stdClass();
|
||||||
|
foreach ($filters as $filter)
|
||||||
|
{
|
||||||
|
if ($this->_ci->input->post($filter))
|
||||||
|
{
|
||||||
|
$filter_valid = true;
|
||||||
|
$filter_object->$filter = $this->_ci->input->post($filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$filter_valid)
|
||||||
|
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
|
||||||
|
|
||||||
|
return $filter_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _checkUpdate($updateInfos)
|
||||||
|
{
|
||||||
|
$update_valid = false;
|
||||||
|
$update_object = new stdClass();
|
||||||
|
foreach (self::ALLOWED_TO_UPDATE as $filter)
|
||||||
|
{
|
||||||
|
if (isset($updateInfos[$filter]))
|
||||||
|
{
|
||||||
|
$update_valid = true;
|
||||||
|
$update_object->$filter = $updateInfos[$filter];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$update_valid)
|
||||||
|
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
|
||||||
|
|
||||||
|
|
||||||
|
return $update_object;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,298 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined('BASEPATH'))
|
||||||
|
exit('No direct script access allowed');
|
||||||
|
|
||||||
|
|
||||||
|
class KalenderSync extends FHCAPI_Controller
|
||||||
|
{
|
||||||
|
private $_ci;
|
||||||
|
|
||||||
|
const ALLOWED_STATUS = ['preview', 'live'];
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct([
|
||||||
|
'getSyncs' => ['admin:r', 'assistenz:r'],
|
||||||
|
'loadSync' => ['admin:r', 'assistenz:r'],
|
||||||
|
'add' => ['admin:r', 'assistenz:r'],
|
||||||
|
'start' => ['admin:r', 'assistenz:r'],
|
||||||
|
'delete' => ['admin:r', 'assistenz:r'],
|
||||||
|
'updateSync' => ['admin:r', 'assistenz:r'],
|
||||||
|
'getStudienplan' => ['admin:r', 'assistenz:r'],
|
||||||
|
'getSyncStatus' => ['admin:r', 'assistenz:r'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->loadPhrases([
|
||||||
|
'lehre',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->_ci = &get_instance();
|
||||||
|
$this->_ci->load->library('form_validation');
|
||||||
|
$this->_ci->load->model('organisation/Studienplan_model', 'StudienplanModel');
|
||||||
|
$this->_ci->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||||
|
$this->_ci->load->model('ressource/Kalendersyncstatus_model', 'KalendersyncstatusModel');
|
||||||
|
$this->_ci->load->model('ressource/Kalenderstatus_model', 'KalenderStatusModel');
|
||||||
|
$this->_ci->load->library('KalenderSyncLib');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSyncs()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_GET);
|
||||||
|
$this->_ci->form_validation->set_rules('studiensemester_kurzbz',"studiensemester_kurzbz","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
$studiensemester_kurzbz = $this->_ci->input->get('studiensemester_kurzbz', TRUE);
|
||||||
|
|
||||||
|
$language = getUserLanguage() == 'German' ? 0 : 1;
|
||||||
|
|
||||||
|
$this->_ci->KalenderStatusModel->addSelect('tbl_kalender_syncstatus.*, array_to_json(bezeichnung_mehrsprachig::varchar[])->>' . $language .' AS sync_status_kurzbz, tbl_organisationseinheit.bezeichnung as oebezeichnung, tbl_studienplan.bezeichnung as studienplanbezeichnung');
|
||||||
|
$this->_ci->KalenderStatusModel->addJoin('lehre.tbl_kalender_status', 'tbl_kalender_syncstatus.sync_status_kurzbz = tbl_kalender_status.status_kurzbz');
|
||||||
|
$this->_ci->KalenderStatusModel->addJoin('public.tbl_organisationseinheit', 'tbl_kalender_syncstatus.oe_kurzbz = tbl_organisationseinheit.oe_kurzbz');
|
||||||
|
$this->_ci->KalenderStatusModel->addJoin('lehre.tbl_studienplan', 'tbl_kalender_syncstatus.studienplan_id = tbl_studienplan.studienplan_id', 'LEFT');
|
||||||
|
|
||||||
|
$data = $this->_ci->KalendersyncstatusModel->loadWhere(array('studiensemester_kurzbz' => $studiensemester_kurzbz));
|
||||||
|
|
||||||
|
if (isError($data))
|
||||||
|
$this->terminateWithError(getError($data));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(getData($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
$this->load->library('form_validation');
|
||||||
|
|
||||||
|
$this->form_validation->set_rules('kalender_syncstatus_id', 'kalender_syncstatus_id', 'required');
|
||||||
|
|
||||||
|
if (!$this->form_validation->run())
|
||||||
|
$this->terminateWithValidationErrors($this->form_validation->error_array());
|
||||||
|
|
||||||
|
$kalender_syncstatus_id = $this->input->post('kalender_syncstatus_id');
|
||||||
|
|
||||||
|
$result = $this->_ci->KalendersyncstatusModel->load($kalender_syncstatus_id);
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
$this->terminateWithError(getError($result));
|
||||||
|
|
||||||
|
if (!hasData($result))
|
||||||
|
$this->terminateWithError($this->p->t('lehre', 'errorDeleting'));
|
||||||
|
|
||||||
|
$del_result = $this->_ci->KalendersyncstatusModel->delete(getData($result)[0]->kalender_syncstatus_id);
|
||||||
|
|
||||||
|
if (isError($del_result))
|
||||||
|
$this->terminateWithError(getError($del_result));
|
||||||
|
$this->terminateWithSuccess($del_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadSync()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_GET);
|
||||||
|
$this->_ci->form_validation->set_rules('kalender_syncstatus_id',"kalender_syncstatus_id","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
$kalender_syncstatus_id = $this->_ci->input->get('kalender_syncstatus_id', TRUE);
|
||||||
|
$data = $this->_ci->KalendersyncstatusModel->loadWhere(array('kalender_syncstatus_id' => $kalender_syncstatus_id));
|
||||||
|
if (isError($data))
|
||||||
|
$this->terminateWithError(getError($data));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(getData($data)[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSyncStatus()
|
||||||
|
{
|
||||||
|
$language = getUserLanguage() == 'German' ? 0 : 1;
|
||||||
|
|
||||||
|
$this->_ci->KalenderStatusModel->addSelect('*, array_to_json(bezeichnung_mehrsprachig::varchar[])->>' . $language .' AS bezeichnung, status_kurzbz');
|
||||||
|
$this->_ci->KalenderStatusModel->db->where_in('status_kurzbz', self::ALLOWED_STATUS);
|
||||||
|
$data = $this->_ci->KalenderStatusModel->load();
|
||||||
|
|
||||||
|
if (isError($data))
|
||||||
|
$this->terminateWithError(getError($data));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(getData($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function add()
|
||||||
|
{
|
||||||
|
$formData = $this->input->post('formData');
|
||||||
|
|
||||||
|
$this->_ci->form_validation->set_data($formData);
|
||||||
|
|
||||||
|
$this->_ci->form_validation->set_rules('oe_kurzbz',"oe_kurzbz","required");
|
||||||
|
$this->_ci->form_validation->set_rules('studiensemester_kurzbz',"studiensemester_kurzbz","required");
|
||||||
|
$this->_ci->form_validation->set_rules('sync_status_kurzbz',"sync_status_kurzbz","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$ausbildungssemester = isEmptyString($formData['ausbildungssemester']) ? null : $formData['ausbildungssemester'];
|
||||||
|
$studienplan_id = isEmptyString($formData['studienplan_id']) ? null : $formData['studienplan_id'];
|
||||||
|
|
||||||
|
$existing = $this->_checkIfExist(
|
||||||
|
$formData['oe_kurzbz'],
|
||||||
|
$formData['studiensemester_kurzbz'],
|
||||||
|
$ausbildungssemester,
|
||||||
|
$studienplan_id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isError($existing))
|
||||||
|
$this->terminateWithError(getError($existing));
|
||||||
|
|
||||||
|
if (hasData($existing))
|
||||||
|
{
|
||||||
|
$this->terminateWithError('Es existiert bereits ein Eintrag.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->_ci->KalendersyncstatusModel->insert(
|
||||||
|
array(
|
||||||
|
'oe_kurzbz' => $formData['oe_kurzbz'],
|
||||||
|
'studiensemester_kurzbz' => $formData['studiensemester_kurzbz'],
|
||||||
|
'datum_bis' => $formData['datum_bis'],
|
||||||
|
'studienplan_id' => $studienplan_id,
|
||||||
|
'ausbildungssemester' => $ausbildungssemester,
|
||||||
|
'sync_status_kurzbz' => $formData['sync_status_kurzbz'],
|
||||||
|
'mail' => $formData['mail'],
|
||||||
|
'insertvon' => getAuthUID()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function start()
|
||||||
|
{
|
||||||
|
$formData = $this->input->post('formData');
|
||||||
|
|
||||||
|
$this->_ci->form_validation->set_data($formData);
|
||||||
|
|
||||||
|
$this->_ci->form_validation->set_rules('oe_kurzbz',"oe_kurzbz","required");
|
||||||
|
$this->_ci->form_validation->set_rules('studiensemester_kurzbz',"studiensemester_kurzbz","required");
|
||||||
|
$this->_ci->form_validation->set_rules('sync_status_kurzbz',"sync_status_kurzbz","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$result = $this->_ci->kalendersynclib->runManual($formData['oe_kurzbz'], $formData['studiensemester_kurzbz'], $formData['ausbildungssemester'], $formData['studienplan_id'], $formData['mail'], $formData['datum_bis'], $formData['sync_status_kurzbz']);
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
$this->terminateWithError(getError($result));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($result);
|
||||||
|
}
|
||||||
|
public function updateSync()
|
||||||
|
{
|
||||||
|
$formData = $this->input->post('formData');
|
||||||
|
|
||||||
|
$this->_ci->form_validation->set_data($formData);
|
||||||
|
|
||||||
|
$this->_ci->form_validation->set_rules('kalender_syncstatus_id',"kalender_syncstatus_id","required");
|
||||||
|
$this->_ci->form_validation->set_rules('oe_kurzbz',"oe_kurzbz","required");
|
||||||
|
$this->_ci->form_validation->set_rules('studiensemester_kurzbz',"studiensemester_kurzbz","required");
|
||||||
|
$this->_ci->form_validation->set_rules('sync_status_kurzbz',"sync_status_kurzbz","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$ausbildungssemester = isEmptyString($formData['ausbildungssemester']) ? null : $formData['ausbildungssemester'];
|
||||||
|
$studienplan_id = isEmptyString($formData['studienplan_id']) ? null : $formData['studienplan_id'];
|
||||||
|
|
||||||
|
$existing = $this->_checkIfExist(
|
||||||
|
$formData['oe_kurzbz'],
|
||||||
|
$formData['studiensemester_kurzbz'],
|
||||||
|
$ausbildungssemester,
|
||||||
|
$studienplan_id,
|
||||||
|
$formData['kalender_syncstatus_id']
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isError($existing))
|
||||||
|
$this->terminateWithError(getError($existing));
|
||||||
|
|
||||||
|
if (hasData($existing))
|
||||||
|
{
|
||||||
|
$this->terminateWithError('Es existiert bereits ein Eintrag.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_ci->KalendersyncstatusModel->update(
|
||||||
|
array('kalender_syncstatus_id' => $formData['kalender_syncstatus_id']),
|
||||||
|
array(
|
||||||
|
'oe_kurzbz' => $formData['oe_kurzbz'],
|
||||||
|
'studiensemester_kurzbz' => $formData['studiensemester_kurzbz'],
|
||||||
|
'datum_bis' => $formData['datum_bis'],
|
||||||
|
'studienplan_id' => $studienplan_id,
|
||||||
|
'ausbildungssemester' => $ausbildungssemester,
|
||||||
|
'sync_status_kurzbz' => $formData['sync_status_kurzbz'],
|
||||||
|
'mail' => $formData['mail'],
|
||||||
|
'updatevon' => getAuthUID(),
|
||||||
|
'updateamum' => date('c'),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
public function getStudienplan()
|
||||||
|
{
|
||||||
|
$this->_ci->form_validation->set_data($_GET);
|
||||||
|
$this->_ci->form_validation->set_rules('oe_kurzbz',"oe_kurzbz","required");
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$oe_kurzbz = $this->_ci->input->get('oe_kurzbz', TRUE);
|
||||||
|
$studiensemester_kurzbz = $this->_ci->input->get('studiensemester_kurzbz', TRUE);
|
||||||
|
$ausbildungssemester = $this->_ci->input->get('ausbildungssemester', TRUE);
|
||||||
|
|
||||||
|
$this->_ci->StudiengangModel->addJoin('tbl_organisationseinheit', 'oe_kurzbz');
|
||||||
|
$is_studiengang = $this->_ci->StudiengangModel->loadWhere(array('oe_kurzbz' => $oe_kurzbz));
|
||||||
|
|
||||||
|
if (hasData($is_studiengang) && !isEmptyString($studiensemester_kurzbz))
|
||||||
|
{
|
||||||
|
$studiengang = getData($is_studiengang)[0];
|
||||||
|
|
||||||
|
$studienplaene = $this->_ci->StudienplanModel->getStudienplaeneBySemester(
|
||||||
|
$studiengang->studiengang_kz,
|
||||||
|
$studiensemester_kurzbz,
|
||||||
|
isEmptyString($ausbildungssemester) ? null : $ausbildungssemester
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
$data = $this->getDataOrTerminateWithError($studienplaene);
|
||||||
|
|
||||||
|
$unique = array();
|
||||||
|
foreach ($data as $row)
|
||||||
|
{
|
||||||
|
$unique[$row->studienplan_id] = (object) array(
|
||||||
|
'studienplan_id' => $row->studienplan_id,
|
||||||
|
'studienordnung_id' => $row->studienordnung_id,
|
||||||
|
'bezeichnung' => $row->bezeichnung,
|
||||||
|
'orgform_kurzbz' => $row->orgform_kurzbz
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(array_values($unique));
|
||||||
|
}
|
||||||
|
$this->terminateWithSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _checkIfExist($oe_kurzbz, $studiensemester_kurzbz, $ausbildungssemester, $studienplan_id, $kalender_syncstatus_id = null)
|
||||||
|
{
|
||||||
|
$this->_ci->KalendersyncstatusModel->db->where('oe_kurzbz', $oe_kurzbz);
|
||||||
|
$this->_ci->KalendersyncstatusModel->db->where('studiensemester_kurzbz', $studiensemester_kurzbz);
|
||||||
|
|
||||||
|
if (is_null($ausbildungssemester))
|
||||||
|
$this->_ci->KalendersyncstatusModel->db->where('ausbildungssemester IS NULL', null, false);
|
||||||
|
else
|
||||||
|
$this->_ci->KalendersyncstatusModel->db->where('ausbildungssemester', $ausbildungssemester);
|
||||||
|
|
||||||
|
if (is_null($studienplan_id))
|
||||||
|
$this->_ci->KalendersyncstatusModel->db->where('studienplan_id IS NULL', null, false);
|
||||||
|
else
|
||||||
|
$this->_ci->KalendersyncstatusModel->db->where('studienplan_id', $studienplan_id);
|
||||||
|
|
||||||
|
if (!is_null($kalender_syncstatus_id))
|
||||||
|
$this->_ci->KalendersyncstatusModel->db->where('kalender_syncstatus_id !=', $kalender_syncstatus_id);
|
||||||
|
return $this->_ci->KalendersyncstatusModel->load();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,231 @@
|
|||||||
|
<?php
|
||||||
|
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||||
|
|
||||||
|
class Reservierung extends FHCAPI_Controller
|
||||||
|
{
|
||||||
|
private $_ci;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object initialization
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct([
|
||||||
|
'addReservierung' => 'lehre/lvplan:rw',
|
||||||
|
'getRollen' => 'lehre/lvplan:rw',
|
||||||
|
'getInformation' => 'lehre/lvplan:rw',
|
||||||
|
'getLektor' => 'lehre/lvplan:rw',
|
||||||
|
'searchGroup' => 'lehre/lvplan:rw',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->_ci =& get_instance();
|
||||||
|
|
||||||
|
$this->_ci->load->library('LogLib');
|
||||||
|
$this->_ci->load->library('form_validation');
|
||||||
|
$this->_ci->load->library('KalenderLib');
|
||||||
|
|
||||||
|
$this->_ci->load->model('ressource/Ort_model', 'OrtModel');
|
||||||
|
$this->_ci->load->model('ressource/Kalender_Event_Rolle_model', 'KalenderEventRolleModel');
|
||||||
|
$this->_ci->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||||
|
$this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
|
||||||
|
$this->_ci->load->model('organisation/gruppe_model', 'GruppeModel');
|
||||||
|
|
||||||
|
|
||||||
|
$this->loadPhrases([
|
||||||
|
'ui'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Public methods
|
||||||
|
|
||||||
|
public function getInformation()
|
||||||
|
{
|
||||||
|
$return_array = array('berechtigt' => false, 'studiengaenge' => []);
|
||||||
|
|
||||||
|
$this->_ci->OrtModel->db->join("
|
||||||
|
(select ort,standort_id,strasse, plz
|
||||||
|
FROM public.tbl_standort
|
||||||
|
LEFT JOIN public.tbl_adresse USING(adresse_id)
|
||||||
|
) standort", "standort_id", "LEFT", false);
|
||||||
|
|
||||||
|
$raeume = $this->_ci->OrtModel->loadWhere(array('aktiv' => true, 'reservieren' => true));
|
||||||
|
$return_array['raeume'] = hasData($raeume) ? getData($raeume) : [];
|
||||||
|
|
||||||
|
if (!$this->_ci->permissionlib->isBerechtigt('lehre/reservierung'))
|
||||||
|
$this->terminateWithSuccess($return_array);
|
||||||
|
|
||||||
|
$stg_berechtigungen = $this->_ci->permissionlib->getSTG_isEntitledFor('lehre/reservierung');
|
||||||
|
if (isEmptyArray($stg_berechtigungen))
|
||||||
|
$this->terminateWithSuccess($return_array);
|
||||||
|
|
||||||
|
$this->_ci->StudiengangModel->addSelect('studiengang_kz, UPPER(CONCAT(typ, kurzbz)) as kuerzel, kurzbzlang');
|
||||||
|
$this->_ci->StudiengangModel->addOrder('typ, kurzbz');
|
||||||
|
$this->_ci->StudiengangModel->db->where_in('studiengang_kz', $stg_berechtigungen);
|
||||||
|
$studiengaenge = $this->_ci->StudiengangModel->loadWhere(array('aktiv' => true));
|
||||||
|
|
||||||
|
if (isError($studiengaenge))
|
||||||
|
$this->terminateWithError(getError($studiengaenge));
|
||||||
|
$language = getUserLanguage() == 'German' ? 0 : 1;
|
||||||
|
$this->_ci->KalenderEventRolleModel->addOrder('sort');
|
||||||
|
$this->_ci->KalenderEventRolleModel->addSelect('rolle_kurzbz, array_to_json(bezeichnung_mehrsprachig::varchar[])->>'. $language. ' as bezeichnung');
|
||||||
|
|
||||||
|
$rollen = $this->_ci->KalenderEventRolleModel->load();
|
||||||
|
|
||||||
|
$this->_ci->StudiensemesterModel->addOrder('start', 'DESC');
|
||||||
|
$studiensemester = $this->_ci->StudiensemesterModel->load();
|
||||||
|
|
||||||
|
$return_array['studiengaenge'] = hasData($studiengaenge) ? getData($studiengaenge) : [];
|
||||||
|
$return_array['berechtigt'] = true;
|
||||||
|
$return_array['rollen'] = hasData($rollen) ? getData($rollen) : [];
|
||||||
|
$return_array['studiensemester'] = hasData($studiensemester) ? getData($studiensemester) : [];
|
||||||
|
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($return_array);
|
||||||
|
}
|
||||||
|
public function getRaeume()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->_ci->OrtModel->db->join("
|
||||||
|
(select ort,standort_id,strasse, plz
|
||||||
|
FROM public.tbl_standort
|
||||||
|
LEFT JOIN public.tbl_adresse USING(adresse_id)
|
||||||
|
) standort", "standort_id", "LEFT", false);
|
||||||
|
|
||||||
|
$result = $this->_ci->OrtModel->loadWhere(array('aktiv' => true, 'reservieren' => true));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(hasData($result) ? getData($result) : []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchGroup()
|
||||||
|
{
|
||||||
|
$query = $this->input->get('query');
|
||||||
|
if (is_null($query))
|
||||||
|
$this->terminateWithError($this->_ci->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
|
||||||
|
|
||||||
|
$stg_berechtigungen = $this->_ci->permissionlib->getSTG_isEntitledFor('lehre/reservierung');
|
||||||
|
|
||||||
|
if (isEmptyArray($stg_berechtigungen))
|
||||||
|
$this->terminateWithSuccess([]);
|
||||||
|
|
||||||
|
|
||||||
|
$query_words = explode(' ', urldecode($query));
|
||||||
|
|
||||||
|
$gruppen_result = $this->_ci->GruppeModel->search($query_words);
|
||||||
|
|
||||||
|
if (isError($gruppen_result))
|
||||||
|
$this->terminateWithError(getError($gruppen_result), self::ERROR_TYPE_GENERAL);
|
||||||
|
|
||||||
|
$gruppen_array = array();
|
||||||
|
|
||||||
|
if (hasData($gruppen_result))
|
||||||
|
$gruppen_array = getData($gruppen_result);
|
||||||
|
|
||||||
|
$lehrverband_result = $this->_ci->LehrverbandModel->search($query_words);
|
||||||
|
|
||||||
|
$lehrverband_array = array();
|
||||||
|
|
||||||
|
if (isError($lehrverband_result))
|
||||||
|
$this->terminateWithError(getError($lehrverband_result), self::ERROR_TYPE_GENERAL);
|
||||||
|
|
||||||
|
if (hasData($lehrverband_result))
|
||||||
|
$lehrverband_array = getData($lehrverband_result);
|
||||||
|
|
||||||
|
$all_gruppen = array_merge($gruppen_array, $lehrverband_array);
|
||||||
|
|
||||||
|
$gefilterte_gruppen = array_filter($all_gruppen, function($gruppe) use ($stg_berechtigungen)
|
||||||
|
{
|
||||||
|
return in_array($gruppe->studiengang_kz, $stg_berechtigungen);
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($gefilterte_gruppen);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRollen()
|
||||||
|
{
|
||||||
|
$language = getUserLanguage() == 'German' ? 0 : 1;
|
||||||
|
|
||||||
|
$this->_ci->KalenderEventRolleModel->addOrder('sort');
|
||||||
|
$this->_ci->KalenderEventRolleModel->addSelect('rolle_kurzbz, array_to_json(bezeichnung_mehrsprachig::varchar[])->>'. $language. ' as bezeichnung');
|
||||||
|
|
||||||
|
$result = $this->_ci->KalenderEventRolleModel->load();
|
||||||
|
|
||||||
|
$this->terminateWithSuccess(hasData($result) ? getData($result) : []);
|
||||||
|
}
|
||||||
|
public function addReservierung()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->_ci->form_validation->set_data($_POST);
|
||||||
|
$this->_ci->form_validation->set_rules('titel',"titel","required");
|
||||||
|
$this->_ci->form_validation->set_rules('beschreibung',"beschreibung","required");
|
||||||
|
$this->_ci->form_validation->set_rules('ort_kurzbz[]',"ort_kurzbz","required");
|
||||||
|
$this->_ci->form_validation->set_rules('start_date',"start_date","required");
|
||||||
|
$this->_ci->form_validation->set_rules('end_date',"end_date","required");
|
||||||
|
|
||||||
|
|
||||||
|
if($this->_ci->form_validation->run() === FALSE)
|
||||||
|
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
|
||||||
|
|
||||||
|
$titel = $this->_ci->input->post('titel', TRUE);
|
||||||
|
$beschreibung = $this->_ci->input->post('beschreibung', TRUE);
|
||||||
|
$ort_kurzbz = $this->_ci->input->post('ort_kurzbz', TRUE);
|
||||||
|
$start_date = $this->_ci->input->post('start_date', TRUE);
|
||||||
|
$end_date = $this->_ci->input->post('end_date', TRUE);
|
||||||
|
$teilnehmer = $this->_ci->input->post('teilnehmer', TRUE);
|
||||||
|
$specialGroups = $this->_ci->input->post('specialGroups', TRUE);
|
||||||
|
$specialFinalGroups = $this->_ci->input->post('specialFinalGroups', TRUE);
|
||||||
|
$groups = $this->_ci->input->post('groups', TRUE);
|
||||||
|
|
||||||
|
if ($this->_ci->permissionlib->isBerechtigt('lehre/reservierung'))
|
||||||
|
{
|
||||||
|
if (empty($teilnehmer) || !is_array($teilnehmer))
|
||||||
|
{
|
||||||
|
$teilnehmer[] = array('uid' => getAuthUID(), 'rolle' => 'organisator');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$teilnehmer[] = array('uid' => getAuthUID(), 'rolle' => 'organisator');
|
||||||
|
|
||||||
|
|
||||||
|
$result = $this->_ci->kalenderlib->addReservierung($titel, $beschreibung, $ort_kurzbz, $start_date, $end_date, $teilnehmer, $specialFinalGroups, $specialGroups, $groups);
|
||||||
|
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
$this->terminateWithError(getError($result));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getLektor()
|
||||||
|
{
|
||||||
|
$query = $this->input->get('query');
|
||||||
|
|
||||||
|
if (is_null($query))
|
||||||
|
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
|
||||||
|
|
||||||
|
$query_words = explode(' ', $query);
|
||||||
|
|
||||||
|
$this->_ci->MitarbeiterModel->addSelect('uid, person_id, vorname, nachname');
|
||||||
|
$this->_ci->MitarbeiterModel->addJoin('public.tbl_benutzer', 'uid = mitarbeiter_uid');
|
||||||
|
$this->_ci->MitarbeiterModel->addJoin('public.tbl_person', 'person_id');
|
||||||
|
|
||||||
|
$this->_ci->MitarbeiterModel->db->where('public.tbl_benutzer.aktiv', true);
|
||||||
|
|
||||||
|
$this->_ci->MitarbeiterModel->db->group_start();
|
||||||
|
foreach ($query_words as $word)
|
||||||
|
{
|
||||||
|
$this->_ci->MitarbeiterModel->db->group_start();
|
||||||
|
$this->_ci->MitarbeiterModel->db->where('tbl_person.vorname ILIKE', "%" . $word . "%");
|
||||||
|
$this->_ci->MitarbeiterModel->db->or_where('tbl_person.nachname ILIKE', "%" . $word . "%");
|
||||||
|
$this->_ci->MitarbeiterModel->db->or_where('uid ILIKE', "%" . $word . "%");
|
||||||
|
$this->_ci->MitarbeiterModel->db->group_end();
|
||||||
|
}
|
||||||
|
$this->_ci->MitarbeiterModel->db->group_end();
|
||||||
|
$this->_ci->MitarbeiterModel->addOrder('nachname');
|
||||||
|
$this->_ci->MitarbeiterModel->addOrder('vorname');
|
||||||
|
$result = $this->_ci->MitarbeiterModel->load();
|
||||||
|
$this->terminateWithSuccess(hasData($result) ? getData($result) : array());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined("BASEPATH")) exit("No direct script access allowed");
|
||||||
|
|
||||||
|
class TempusJob extends JOB_Controller
|
||||||
|
{
|
||||||
|
private $_ci;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->_ci =& get_instance();
|
||||||
|
|
||||||
|
$this->_ci->load->helper('hlp_sancho_helper');
|
||||||
|
$this->_ci->load->library('KalenderLib');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function sync()
|
||||||
|
{
|
||||||
|
$this->_ci->logInfo('Start job FHC-Core->Tempus->sync');
|
||||||
|
$this->_ci->kalenderlib->sync();
|
||||||
|
$this->_ci->logInfo('End job FHC-Core->Tempus->sync');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,495 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Job zur einmaligen Migration des Stundenplans
|
||||||
|
*
|
||||||
|
* Aufruf
|
||||||
|
* php index.ci.php system/MigrateKalender
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||||
|
|
||||||
|
class MigrateKalender extends CLI_Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||||
|
$this->load->model('ressource/Kalender_Lehreinheit_model', 'KalenderLehreinheitModel');
|
||||||
|
$this->load->model('ressource/Kalender_Ort_model', 'KalenderOrtModel');
|
||||||
|
$this->load->model('ressource/Stundenplandev_Kalender_model', 'SyncModel');
|
||||||
|
$this->load->model('ressource/Reservierung_Kalender_model', 'SyncReservierungModel');
|
||||||
|
$this->load->model('ressource/Kalender_Event_Teilnehmer_model', 'KalenderEventTeilnehmerModel');
|
||||||
|
$this->load->model('ressource/Kalender_Event_model', 'KalenderEventModel');
|
||||||
|
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
|
||||||
|
$this->load->model('person/Benutzer_model', 'BenutzerModel');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Everything has a beginning
|
||||||
|
*/
|
||||||
|
public function migrateStundenplan($von, $bis = null, $studiengang_kz = null)
|
||||||
|
{
|
||||||
|
if (is_null($von))
|
||||||
|
return error('Parameter "von" ist erforderlich');
|
||||||
|
|
||||||
|
if (is_null($bis))
|
||||||
|
$bis = '2100-12-31';
|
||||||
|
|
||||||
|
$db = new DB_Model();
|
||||||
|
|
||||||
|
$stpldevsql = '
|
||||||
|
WITH eindeutige_stunden AS (
|
||||||
|
SELECT DISTINCT unr, datum, stunde
|
||||||
|
FROM lehre.tbl_stundenplandev
|
||||||
|
WHERE datum >= ? AND datum <= ?';
|
||||||
|
|
||||||
|
$params = [$von, $bis];
|
||||||
|
|
||||||
|
if (!is_null($studiengang_kz))
|
||||||
|
{
|
||||||
|
$stpldevsql .= ' AND studiengang_kz = ?';
|
||||||
|
|
||||||
|
$params[] = $studiengang_kz;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stpldevsql .= '),
|
||||||
|
|
||||||
|
block_keys AS (
|
||||||
|
SELECT
|
||||||
|
unr,
|
||||||
|
datum,
|
||||||
|
stunde,
|
||||||
|
stunde - ROW_NUMBER() OVER (PARTITION BY unr, datum ORDER BY stunde) AS block_nr
|
||||||
|
FROM eindeutige_stunden
|
||||||
|
),
|
||||||
|
blocks AS (
|
||||||
|
SELECT
|
||||||
|
bk.unr,
|
||||||
|
bk.datum,
|
||||||
|
bk.block_nr,
|
||||||
|
MIN(bk.stunde) AS stunde_von,
|
||||||
|
MAX(bk.stunde) AS stunde_bis,
|
||||||
|
array_agg(DISTINCT sp.lehreinheit_id) AS lehreinheit_id,
|
||||||
|
array_agg(DISTINCT sp.ort_kurzbz) AS ort_kurzbz,
|
||||||
|
array_agg(sp.stundenplandev_id ORDER BY bk.stunde) AS stundenplandev_ids,
|
||||||
|
MIN(sp.insertamum) AS insertamum,
|
||||||
|
(array_agg(sp.insertvon ORDER BY sp.insertamum ASC))[1] AS insertvon,
|
||||||
|
MAX(sp.updateamum) AS updateamum,
|
||||||
|
(array_agg(sp.updatevon ORDER BY sp.updateamum DESC))[1] AS updatevon
|
||||||
|
FROM block_keys bk JOIN lehre.tbl_stundenplandev sp ON sp.unr = bk.unr AND sp.datum = bk.datum AND sp.stunde = bk.stunde
|
||||||
|
WHERE sp.datum >= ? AND sp.datum <= ?
|
||||||
|
GROUP BY bk.unr, bk.datum, bk.block_nr
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
b.stundenplandev_ids,
|
||||||
|
b.unr,
|
||||||
|
b.datum,
|
||||||
|
b.block_nr,
|
||||||
|
b.lehreinheit_id,
|
||||||
|
b.ort_kurzbz,
|
||||||
|
b.datum + stundevon.beginn AS von,
|
||||||
|
b.datum + stundebis.ende AS bis,
|
||||||
|
b.insertamum,
|
||||||
|
b.insertvon,
|
||||||
|
b.updateamum,
|
||||||
|
b.updatevon
|
||||||
|
FROM blocks b
|
||||||
|
JOIN lehre.tbl_stunde stundevon ON stundevon.stunde = b.stunde_von
|
||||||
|
JOIN lehre.tbl_stunde stundebis ON stundebis.stunde = b.stunde_bis
|
||||||
|
ORDER BY b.datum, b.unr, b.block_nr;';
|
||||||
|
|
||||||
|
array_push($params, $von, $bis);
|
||||||
|
$stpldev = $db->execReadOnlyQuery($stpldevsql, $params);
|
||||||
|
|
||||||
|
if (hasData($stpldev))
|
||||||
|
{
|
||||||
|
// Pruefen ob der Eintrag schon in Sync Tabelle vorhanden ist
|
||||||
|
// Wenn neuere Änderungen vorhanden dann Update
|
||||||
|
// Wenn keine Änderungen seit leztem Sync dann Ueberspringen
|
||||||
|
// Wenn noch nicht vorhanden neu anlegen
|
||||||
|
// Danach ggf pruefen welceh Eintraege in der zwischenzeit geloescht wurden und
|
||||||
|
// in der neuen Tabelle auch archivieren oder loeschen
|
||||||
|
|
||||||
|
$data = getData($stpldev);
|
||||||
|
foreach($data as $block)
|
||||||
|
{
|
||||||
|
$ids = is_array($block->stundenplandev_ids) ? $block->stundenplandev_ids : explode(',', $block->stundenplandev_ids);
|
||||||
|
/*$ids = array_map('intval', $ids);*/
|
||||||
|
|
||||||
|
$this->SyncModel->db->where_in('stundenplandev_id', $ids);
|
||||||
|
$sync_result = $this->SyncModel->load();
|
||||||
|
|
||||||
|
if (!hasData($sync_result))
|
||||||
|
{
|
||||||
|
$kalender_id = $this->_insertKalender($block, 'lehreinheit');
|
||||||
|
if ($kalender_id)
|
||||||
|
{
|
||||||
|
$this->_insertSync($block->stundenplandev_ids, $kalender_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$syncData = getData($sync_result);
|
||||||
|
$kalender_id = $syncData[0]->kalender_id;
|
||||||
|
$last_sync = $syncData[0]->lastupdate;
|
||||||
|
$synced_ids = array_column($syncData, 'stundenplandev_id');
|
||||||
|
|
||||||
|
if ($block->updateamum > $last_sync)
|
||||||
|
{
|
||||||
|
$this->_updateKalender($kalender_id, $block);
|
||||||
|
$this->_updateSync($synced_ids, $kalender_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fehlende = array_diff($block->stundenplandev_ids, $synced_ids);
|
||||||
|
if (!empty($fehlende))
|
||||||
|
{
|
||||||
|
$this->_insertSync($fehlende, $kalender_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function migrateReservierung($von, $bis = null, $ort_kurzbz = null)
|
||||||
|
{
|
||||||
|
if (is_null($von))
|
||||||
|
return error('Parameter "von" ist erforderlich');
|
||||||
|
|
||||||
|
if (is_null($bis))
|
||||||
|
$bis = '2100-12-31';
|
||||||
|
|
||||||
|
$db = new DB_Model();
|
||||||
|
|
||||||
|
$qry = "WITH eindeutige_stunden AS (
|
||||||
|
SELECT DISTINCT titel, beschreibung, datum, stunde
|
||||||
|
FROM campus.tbl_reservierung
|
||||||
|
WHERE datum >= ? AND datum <= ?";
|
||||||
|
|
||||||
|
$params = array($von, $bis);
|
||||||
|
|
||||||
|
if (!is_null($ort_kurzbz))
|
||||||
|
{
|
||||||
|
$qry .= " AND ort_kurzbz = ?";
|
||||||
|
$params[] = $ort_kurzbz;
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry .= "),
|
||||||
|
block_keys AS (
|
||||||
|
SELECT
|
||||||
|
titel, beschreibung, datum, stunde,
|
||||||
|
stunde - ROW_NUMBER() OVER (PARTITION BY titel, beschreibung, datum ORDER BY stunde) AS block_nr
|
||||||
|
FROM eindeutige_stunden
|
||||||
|
),
|
||||||
|
blocks AS (
|
||||||
|
SELECT
|
||||||
|
bk.titel,
|
||||||
|
bk.beschreibung,
|
||||||
|
bk.datum,
|
||||||
|
bk.block_nr,
|
||||||
|
MIN(bk.stunde) AS stunde_von,
|
||||||
|
MAX(bk.stunde) AS stunde_bis,
|
||||||
|
array_agg(DISTINCT r.reservierung_id::text) AS reservierung_ids,
|
||||||
|
array_agg(DISTINCT r.uid) AS uids,
|
||||||
|
array_agg(DISTINCT r.gruppe_kurzbz) AS gruppen_kurzbz,
|
||||||
|
array_agg(DISTINCT ROW(r.semester, r.verband, r.gruppe, r.studiengang_kz)::text) AS svg_kombis,
|
||||||
|
array_agg(DISTINCT r.ort_kurzbz) AS ort_kurzbz,
|
||||||
|
MIN(r.studiengang_kz) AS studiengang_kz,
|
||||||
|
MIN(r.reservierung_id) AS reservierung_id,
|
||||||
|
MAX(r.insertamum) AS insertamum,
|
||||||
|
(array_agg(r.insertvon ORDER BY r.insertamum ASC))[1] AS insertvon
|
||||||
|
FROM block_keys bk
|
||||||
|
JOIN campus.tbl_reservierung r
|
||||||
|
ON r.titel = bk.titel AND r.beschreibung = bk.beschreibung AND r.datum = bk.datum AND r.stunde = bk.stunde
|
||||||
|
WHERE r.datum >= ? AND r.datum <= ?
|
||||||
|
GROUP BY bk.titel, bk.beschreibung, bk.datum, bk.block_nr
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
b.*,
|
||||||
|
(b.datum + s_von.beginn) AS von,
|
||||||
|
(b.datum + s_bis.ende) AS bis
|
||||||
|
FROM blocks b
|
||||||
|
JOIN lehre.tbl_stunde s_von ON s_von.stunde = b.stunde_von
|
||||||
|
JOIN lehre.tbl_stunde s_bis ON s_bis.stunde = b.stunde_bis
|
||||||
|
ORDER BY b.reservierung_id DESC;";
|
||||||
|
|
||||||
|
array_push($params, $von, $bis);
|
||||||
|
$reservierung_data = $db->execReadOnlyQuery($qry, $params);
|
||||||
|
|
||||||
|
|
||||||
|
if (hasData($reservierung_data))
|
||||||
|
{
|
||||||
|
$data = getData($reservierung_data);
|
||||||
|
|
||||||
|
foreach($data as $block)
|
||||||
|
{
|
||||||
|
|
||||||
|
$ids = is_array($block->reservierung_ids) ? $block->reservierung_ids : explode(',', $block->reservierung_ids);
|
||||||
|
|
||||||
|
$this->SyncReservierungModel->db->where_in('reservierung_id', $ids);
|
||||||
|
$sync_result = $this->SyncReservierungModel->load();
|
||||||
|
|
||||||
|
if (!hasData($sync_result))
|
||||||
|
{
|
||||||
|
$kalender_id = $this->_insertKalender($block, 'reservierung');
|
||||||
|
if ($kalender_id)
|
||||||
|
{
|
||||||
|
$this->_insertReservierungSync($block->reservierung_ids, $kalender_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$syncData = getData($sync_result);
|
||||||
|
$kalender_id = $syncData[0]->kalender_id;
|
||||||
|
$last_sync = $syncData[0]->lastupdate;
|
||||||
|
$synced_ids = array_column($syncData, 'reservierung_id');
|
||||||
|
|
||||||
|
if ($block->insertamum > $last_sync)
|
||||||
|
{
|
||||||
|
$this->_updateKalender($kalender_id, $block);
|
||||||
|
$this->_updateReservierungSync($synced_ids, $kalender_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fehlende = array_diff($block->reservierung_ids, $synced_ids);
|
||||||
|
if (!empty($fehlende))
|
||||||
|
{
|
||||||
|
$this->_insertReservierungSync($fehlende, $kalender_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function _insertKalender($block, $typ)
|
||||||
|
{
|
||||||
|
$result = $this->KalenderModel->insert(
|
||||||
|
array (
|
||||||
|
'von' => $block->von,
|
||||||
|
'bis' => $block->bis,
|
||||||
|
'typ' => $typ,
|
||||||
|
'status_kurzbz'=> 'live',
|
||||||
|
'insertamum' => $block->insertamum,
|
||||||
|
'insertvon' => $block->insertvon,
|
||||||
|
'updateamum' => $block->updateamum ?? null,
|
||||||
|
'updatevon' => $block->updatevon ?? null
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if(!isSuccess($result))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$kalender_id = getData($result);
|
||||||
|
|
||||||
|
if ($typ === 'lehreinheit')
|
||||||
|
{
|
||||||
|
$lehreinheit_ids = is_array($block->lehreinheit_id) ? $block->lehreinheit_id : explode(',', $block->lehreinheit_id);
|
||||||
|
foreach ($lehreinheit_ids as $lehreinheit_id)
|
||||||
|
{
|
||||||
|
$leResult = $this->KalenderLehreinheitModel->insert(
|
||||||
|
array (
|
||||||
|
'kalender_id' => $kalender_id,
|
||||||
|
'lehreinheit_id'=> $lehreinheit_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isSuccess($leResult))
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ($typ === 'reservierung')
|
||||||
|
{
|
||||||
|
$eventResult = $this->KalenderEventModel->insert(array(
|
||||||
|
'kalender_id' => $kalender_id,
|
||||||
|
'titel' => $block->titel,
|
||||||
|
'beschreibung' => $block->beschreibung
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!isSuccess($eventResult))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if ($block->insertvon)
|
||||||
|
{
|
||||||
|
$user = $this->BenutzerModel->load(array('uid' => $block->insertvon));
|
||||||
|
|
||||||
|
if (hasData($user))
|
||||||
|
{
|
||||||
|
$this->KalenderEventTeilnehmerModel->insert(array(
|
||||||
|
'kalender_id' => $kalender_id,
|
||||||
|
'uid' => getData($user)[0]->uid,
|
||||||
|
'rolle_kurzbz' => 'organisator'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$uids = is_array($block->uids) ? $block->uids : explode(',', $block->uids);
|
||||||
|
foreach ($uids as $uid)
|
||||||
|
{
|
||||||
|
$this->KalenderEventTeilnehmerModel->insert(array(
|
||||||
|
'kalender_id' => $kalender_id,
|
||||||
|
'uid' => $uid,
|
||||||
|
'rolle_kurzbz' => 'teilnehmer'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$semester_range = $this->StudiensemesterModel->getByDateRange($block->von, $block->bis);
|
||||||
|
|
||||||
|
if (isError($semester_range))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$studiensemester_kurzbz = getData($semester_range)[0]->studiensemester_kurzbz ?? null;
|
||||||
|
|
||||||
|
$gruppen = is_array($block->gruppen_kurzbz) ? $block->gruppen_kurzbz : explode(',', $block->gruppen_kurzbz ?? '');
|
||||||
|
|
||||||
|
foreach ($gruppen as $gruppe_kurzbz)
|
||||||
|
{
|
||||||
|
$gruppe_kurzbz = trim($gruppe_kurzbz);
|
||||||
|
if (!empty($gruppe_kurzbz))
|
||||||
|
{
|
||||||
|
$this->KalenderEventTeilnehmerModel->insert(array(
|
||||||
|
'kalender_id' => $kalender_id,
|
||||||
|
'gruppe_kurzbz' => $gruppe_kurzbz,
|
||||||
|
'studiengang_kz' => $block->studiengang_kz,
|
||||||
|
'studiensemester_kurzbz' => $studiensemester_kurzbz,
|
||||||
|
'rolle_kurzbz' => 'teilnehmer'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($block->svg_kombis as $kombi_str)
|
||||||
|
{
|
||||||
|
$kombi_str = trim($kombi_str, '()');
|
||||||
|
list($sem, $verb, $grp, $stg_kz) = explode(',', $kombi_str);
|
||||||
|
|
||||||
|
$sem = trim($sem) === '' ? null : trim($sem);
|
||||||
|
$verb = trim($verb) === '' ? null : trim($verb);
|
||||||
|
$grp = trim($grp) === '' ? null : trim($grp);
|
||||||
|
$stg_kz = trim($stg_kz) === '' ? null : trim($stg_kz);
|
||||||
|
|
||||||
|
if (is_null($sem) && is_null($verb) && is_null($grp) && is_null($stg_kz))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$this->KalenderEventTeilnehmerModel->insert(array(
|
||||||
|
'kalender_id' => $kalender_id,
|
||||||
|
'studiengang_kz' => $stg_kz,
|
||||||
|
'semester' => $sem,
|
||||||
|
'verband' => $verb,
|
||||||
|
'gruppe' => $grp,
|
||||||
|
'studiensemester_kurzbz' => $studiensemester_kurzbz,
|
||||||
|
'rolle_kurzbz' => 'teilnehmer'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$orte = is_array($block->ort_kurzbz) ? $block->ort_kurzbz : explode(',', $block->ort_kurzbz ?? '');
|
||||||
|
|
||||||
|
foreach ($orte as $ort)
|
||||||
|
{
|
||||||
|
$ort = trim($ort);
|
||||||
|
if (!empty($ort))
|
||||||
|
{
|
||||||
|
$this->KalenderOrtModel->insert(
|
||||||
|
array (
|
||||||
|
'kalender_id' => $kalender_id,
|
||||||
|
'ort_kurzbz' => $ort
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $kalender_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _insertSync($ids, $kalender_id)
|
||||||
|
{
|
||||||
|
foreach($ids as $id)
|
||||||
|
{
|
||||||
|
$this->SyncModel->insert(
|
||||||
|
array (
|
||||||
|
'stundenplandev_id' => $id,
|
||||||
|
'kalender_id' => $kalender_id,
|
||||||
|
'lastupdate' => date('Y-m-d H:i:s')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function _insertReservierungSync($ids, $kalender_id)
|
||||||
|
{
|
||||||
|
|
||||||
|
foreach($ids as $id)
|
||||||
|
{
|
||||||
|
$this->SyncReservierungModel->insert(
|
||||||
|
array (
|
||||||
|
'reservierung_id' => $id,
|
||||||
|
'kalender_id' => $kalender_id,
|
||||||
|
'lastupdate' => date('Y-m-d H:i:s')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _updateKalender($kalender_id, $block)
|
||||||
|
{
|
||||||
|
$this->KalenderModel->update(
|
||||||
|
array (
|
||||||
|
'kalender_id' => $kalender_id
|
||||||
|
),
|
||||||
|
array (
|
||||||
|
'von' => $block->von,
|
||||||
|
'bis' => $block->bis,
|
||||||
|
'updateamum'=> $block->updateamum,
|
||||||
|
'updatevon' => $block->updatevon
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$orte = is_array($block->ort_kurzbz) ? $block->ort_kurzbz : explode(',', $block->ort_kurzbz ?? '');
|
||||||
|
$orte = array_filter(array_map('trim', $orte));
|
||||||
|
|
||||||
|
$this->KalenderOrtModel->delete(array('kalender_id' => $kalender_id));
|
||||||
|
|
||||||
|
foreach ($orte as $ort)
|
||||||
|
{
|
||||||
|
$this->KalenderOrtModel->insert(
|
||||||
|
array (
|
||||||
|
'kalender_id' => $kalender_id,
|
||||||
|
'ort_kurzbz' => $ort
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _updateSync($ids, $kalender_id)
|
||||||
|
{
|
||||||
|
foreach($ids as $id)
|
||||||
|
{
|
||||||
|
$this->SyncModel->update(
|
||||||
|
array (
|
||||||
|
'stundenplandev_id' => $id,
|
||||||
|
'kalender_id' => $kalender_id
|
||||||
|
),
|
||||||
|
array (
|
||||||
|
'lastupdate' => date('Y-m-d H:i:s')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _updateReservierungSync($ids, $kalender_id)
|
||||||
|
{
|
||||||
|
foreach($ids as $id)
|
||||||
|
{
|
||||||
|
$this->SyncReservierungModel->update(
|
||||||
|
array (
|
||||||
|
'reservierung_id' => $id,
|
||||||
|
'kalender_id' => $kalender_id
|
||||||
|
),
|
||||||
|
array (
|
||||||
|
'lastupdate' => date('Y-m-d H:i:s')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
interface ICollisionCheck
|
||||||
|
{
|
||||||
|
public function getName();
|
||||||
|
|
||||||
|
public function check($data);
|
||||||
|
|
||||||
|
public function checkAll($kalender_ids);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
defined('BASEPATH') || exit('No direct script access allowed');
|
||||||
|
|
||||||
|
use CI3_Events as Events;
|
||||||
|
|
||||||
|
class CollisionChecker
|
||||||
|
{
|
||||||
|
private $_checks = [];
|
||||||
|
|
||||||
|
private $_ci;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->_ci =& get_instance();
|
||||||
|
|
||||||
|
$this->_ci->load->library('collision/checks/RoomCollisionCheck');
|
||||||
|
$this->_ci->load->library('collision/checks/LectureCollisionCheck');
|
||||||
|
$this->_ci->load->library('collision/checks/VerbandCollisionCheck');
|
||||||
|
$this->_ci->load->library('collision/checks/StudentCollisionCheck');
|
||||||
|
$this->register($this->_ci->roomcollisioncheck);
|
||||||
|
$this->register($this->_ci->lecturecollisioncheck);
|
||||||
|
$this->register($this->_ci->verbandcollisioncheck);
|
||||||
|
$this->register($this->_ci->studentcollisioncheck);
|
||||||
|
Events::trigger('collision_register', $this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register(ICollisionCheck $check)
|
||||||
|
{
|
||||||
|
$this->_checks[$check->getName()] = $check;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run($data)
|
||||||
|
{
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
|
foreach ($this->_checks as $check)
|
||||||
|
{
|
||||||
|
$result = $check->check($data);
|
||||||
|
|
||||||
|
if (!empty($result))
|
||||||
|
{
|
||||||
|
$errors = array_merge($errors, $result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function runAll($kalender_ids)
|
||||||
|
{
|
||||||
|
$results = array_fill_keys($kalender_ids, []);
|
||||||
|
|
||||||
|
foreach ($this->_checks as $check)
|
||||||
|
{
|
||||||
|
$batchResult = $check->checkAll($kalender_ids);
|
||||||
|
foreach ($batchResult as $kalender_id => $errors)
|
||||||
|
{
|
||||||
|
$results[$kalender_id] = array_merge($results[$kalender_id], $errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,171 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (! defined("BASEPATH")) exit("No direct script access allowed");
|
||||||
|
|
||||||
|
class KalenderNotificationLib
|
||||||
|
{
|
||||||
|
private $_ci;
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->_ci =& get_instance();
|
||||||
|
$this->_ci->load->library('MailLib');
|
||||||
|
$this->_ci->load->config('tempus');
|
||||||
|
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sendMails($mail_infos)
|
||||||
|
{
|
||||||
|
if (!$this->_ci->config->item('send_update_mails'))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
$lektor_added = array();
|
||||||
|
$lektor_changed = array();
|
||||||
|
$lektor_deleted = array();
|
||||||
|
|
||||||
|
$student_added = array();
|
||||||
|
$student_changed = array();
|
||||||
|
$student_deleted = array();
|
||||||
|
|
||||||
|
foreach ($mail_infos as $info)
|
||||||
|
{
|
||||||
|
$entry = $info['entry'];
|
||||||
|
$new_status = $info['new_status'];
|
||||||
|
$notify = $info['notify'];
|
||||||
|
|
||||||
|
$old_entry = null;
|
||||||
|
if ($entry->vorgaenger_kalender_id)
|
||||||
|
{
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'tbl_kalender.kalender_id = tbl_kalender_ort.kalender_id', 'LEFT');
|
||||||
|
$vorgaenger = $this->_ci->KalenderModel->load(array('tbl_kalender.kalender_id' => $entry->vorgaenger_kalender_id));
|
||||||
|
if (hasData($vorgaenger))
|
||||||
|
$old_entry = getData($vorgaenger)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($new_status === 'deleted')
|
||||||
|
$row = $this->_buildMailDeleted($entry);
|
||||||
|
else if ($old_entry)
|
||||||
|
$row = $this->_buildMailChanged($old_entry, $entry);
|
||||||
|
else
|
||||||
|
$row = $this->_buildMailNew($entry);
|
||||||
|
|
||||||
|
if (in_array('lektor', $notify))
|
||||||
|
{
|
||||||
|
if ($new_status === 'deleted')
|
||||||
|
$lektor_deleted[] = $row;
|
||||||
|
else if ($old_entry)
|
||||||
|
$lektor_changed[] = $row;
|
||||||
|
else
|
||||||
|
$lektor_added[] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array('student', $notify))
|
||||||
|
{
|
||||||
|
if ($new_status === 'deleted')
|
||||||
|
$student_deleted[] = $row;
|
||||||
|
else if ($old_entry)
|
||||||
|
$student_changed[] = $row;
|
||||||
|
else
|
||||||
|
$student_added[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$lektor_entries = '';
|
||||||
|
$student_entries = '';
|
||||||
|
|
||||||
|
if (!empty($lektor_added))
|
||||||
|
$lektor_entries .= $this->_addToList($lektor_added, 'hinzugefügt') . '<hr/>';
|
||||||
|
|
||||||
|
if (!empty($lektor_changed))
|
||||||
|
$lektor_entries .= $this->_addToList($lektor_changed, 'geändert') . '<hr/>';
|
||||||
|
|
||||||
|
if (!empty($lektor_deleted))
|
||||||
|
$lektor_entries .= $this->_addToList($lektor_deleted, 'gelöscht') . '<hr/>';
|
||||||
|
|
||||||
|
if (!empty($student_added))
|
||||||
|
$student_entries .= $this->_addToList($student_added, 'hinzugefügt') . '<hr/>';
|
||||||
|
|
||||||
|
if (!empty($student_changed))
|
||||||
|
$student_entries .= $this->_addToList($student_changed, 'geändert') . '<hr/>';
|
||||||
|
|
||||||
|
if (!empty($student_deleted))
|
||||||
|
$student_entries .= $this->_addToList($student_deleted, 'gelöscht') . '<hr/>';
|
||||||
|
|
||||||
|
//TODO (david) richtigen Empfänger
|
||||||
|
if (!empty($lektor_entries))
|
||||||
|
$this->_ci->maillib->send('', 'ma0048@technikum-wien.at', 'Lektor Tempus Update', $lektor_entries);
|
||||||
|
if (!empty($student_entries))
|
||||||
|
$this->_ci->maillib->send('', 'ma0048@technikum-wien.at', 'Student Tempus Update', $student_entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _addToList($entries, $status)
|
||||||
|
{
|
||||||
|
return 'Folgende Einträge wurden <b>'. $status .'</b>: <ul>' . implode('', $entries) . '</ul>';
|
||||||
|
|
||||||
|
}
|
||||||
|
private function _buildMailNew($entry)
|
||||||
|
{
|
||||||
|
$von = date('d.m.Y H:i', strtotime($entry->von));
|
||||||
|
$bis = date('H:i', strtotime($entry->bis));
|
||||||
|
|
||||||
|
return '<li>
|
||||||
|
<b>Kalender ID ' . ($entry->kalender_id ?? '-') . '</b>
|
||||||
|
<ul>
|
||||||
|
<li><b>Uhrzeit:</b> ' . $von . ' - ' . $bis . '</li>
|
||||||
|
<li><b>Ort:</b> ' . ($entry->ort_kurzbz ?? '-') . '</li>
|
||||||
|
</ul>
|
||||||
|
</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _buildMailChanged($old_entry, $new_entry)
|
||||||
|
{
|
||||||
|
$old_von = date('d.m.Y H:i', strtotime($old_entry->von));
|
||||||
|
$old_bis = date('H:i', strtotime($old_entry->bis));
|
||||||
|
$new_von = date('d.m.Y H:i', strtotime($new_entry->von));
|
||||||
|
$new_bis = date('H:i', strtotime($new_entry->bis));
|
||||||
|
|
||||||
|
$old_ort = $old_entry->ort_kurzbz ?? '-';
|
||||||
|
$new_ort = $new_entry->ort_kurzbz ?? '-';
|
||||||
|
|
||||||
|
$uhrzeit_changed = ($old_von . $old_bis) !== ($new_von . $new_bis);
|
||||||
|
$ort_changed = $old_ort !== $new_ort;
|
||||||
|
|
||||||
|
$changes = '';
|
||||||
|
|
||||||
|
if ($uhrzeit_changed)
|
||||||
|
{
|
||||||
|
$changes .= '<li>
|
||||||
|
<b>Uhrzeit:</b>
|
||||||
|
<s style="color:red;">' . $old_von . ' - ' . $old_bis . '</s>
|
||||||
|
<span style="color:green;">' . $new_von . ' - ' . $new_bis . '</span>
|
||||||
|
</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ort_changed)
|
||||||
|
{
|
||||||
|
$changes .= '<li>
|
||||||
|
<b>Ort:</b>
|
||||||
|
<s style="color:red;">' . $old_ort . '</s>
|
||||||
|
<span style="color:green;">' . $new_ort . '</span>
|
||||||
|
</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '<li>
|
||||||
|
<b>Kalender ID ' . ($new_entry->kalender_id ?? '-') . '</b>
|
||||||
|
<ul>' . $changes . '</ul>
|
||||||
|
</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _buildMailDeleted($entry)
|
||||||
|
{
|
||||||
|
$von = date('d.m.Y H:i', strtotime($entry->von));
|
||||||
|
$bis = date('H:i', strtotime($entry->bis));
|
||||||
|
|
||||||
|
return '<li style="color:red;">
|
||||||
|
<b><s>Kalender ID ' . ($entry->kalender_id ?? '-') . '</s></b>
|
||||||
|
<ul>
|
||||||
|
<li><s>' . $von . ' - ' . $bis . '</s></li>
|
||||||
|
<li><s>' . ($entry->ort_kurzbz ?? '-') . '</s></li>
|
||||||
|
</ul>
|
||||||
|
</li>';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,336 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (! defined("BASEPATH")) exit("No direct script access allowed");
|
||||||
|
|
||||||
|
class KalenderSyncLib
|
||||||
|
{
|
||||||
|
private $_ci;
|
||||||
|
|
||||||
|
private $status_map = array(
|
||||||
|
'preview' => array('planning', 'sync_preview'),
|
||||||
|
'live' => array('planning', 'sync_preview', 'preview', 'sync_live')
|
||||||
|
);
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->_ci =& get_instance();
|
||||||
|
|
||||||
|
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||||
|
$this->_ci->load->model('ressource/Kalendersyncstatus_model', 'KalendersyncstatusModel');
|
||||||
|
$this->_ci->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run($studiensemester_kurzbz)
|
||||||
|
{
|
||||||
|
$this->_ci->KalendersyncstatusModel->db->where('studiensemester_kurzbz', $studiensemester_kurzbz);
|
||||||
|
|
||||||
|
$configs = $this->_ci->KalendersyncstatusModel->load();
|
||||||
|
|
||||||
|
if (isError($configs))
|
||||||
|
return $configs;
|
||||||
|
|
||||||
|
if (!hasData($configs))
|
||||||
|
return success([]);
|
||||||
|
|
||||||
|
$results = [];
|
||||||
|
$mail_infos_gesamt = [];
|
||||||
|
|
||||||
|
foreach (getData($configs) as $config)
|
||||||
|
{
|
||||||
|
|
||||||
|
$ziel_status = isEmptyString($config->sync_status_kurzbz) ? 'live' : $config->sync_status_kurzbz;
|
||||||
|
|
||||||
|
$relevante_stati = array_merge($this->status_map[$ziel_status], array('todelete'));
|
||||||
|
|
||||||
|
$kalender_ids_result = $this->getKalenderIds(
|
||||||
|
$config->oe_kurzbz,
|
||||||
|
$config->studiensemester_kurzbz,
|
||||||
|
$relevante_stati,
|
||||||
|
$config->ausbildungssemester,
|
||||||
|
$config->studienplan_id,
|
||||||
|
$config->datum_bis
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isError($kalender_ids_result))
|
||||||
|
return $kalender_ids_result;
|
||||||
|
|
||||||
|
$kalender_ids = getData($kalender_ids_result);
|
||||||
|
|
||||||
|
$sync_result = $this->_syncKalenderIds($kalender_ids, $ziel_status);
|
||||||
|
|
||||||
|
if (isError($sync_result))
|
||||||
|
return $sync_result;
|
||||||
|
|
||||||
|
$sync_data = getData($sync_result);
|
||||||
|
|
||||||
|
if ((bool) $config->mail)
|
||||||
|
$mail_infos_gesamt = array_merge($mail_infos_gesamt, $sync_data['mail_infos']);
|
||||||
|
|
||||||
|
$results[] = [
|
||||||
|
'kalender_syncstatus_id' => $config->kalender_syncstatus_id,
|
||||||
|
'oe_kurzbz' => $config->oe_kurzbz,
|
||||||
|
'ausbildungssemester' => $config->ausbildungssemester,
|
||||||
|
'studienplan_id' => $config->studienplan_id,
|
||||||
|
'ziel_status' => $ziel_status,
|
||||||
|
'mail' => (bool) $config->mail,
|
||||||
|
'synced' => $sync_data['synced']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($mail_infos_gesamt))
|
||||||
|
{
|
||||||
|
$this->_ci->load->library('KalenderNotificationLib');
|
||||||
|
$this->_ci->kalendernotificationlib->sendMails($mail_infos_gesamt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success($results);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function runManual($oe_kurzbz, $studiensemester_kurzbz, $ausbildungssemester = null, $studienplan_id = null, $mail = true, $datum_bis = null, $ziel_status = 'live')
|
||||||
|
{
|
||||||
|
$relevante_stati = array_merge($this->status_map[$ziel_status], array('todelete'));
|
||||||
|
|
||||||
|
$kalender_ids_result = $this->getKalenderIds(
|
||||||
|
$oe_kurzbz,
|
||||||
|
$studiensemester_kurzbz,
|
||||||
|
$relevante_stati,
|
||||||
|
$ausbildungssemester,
|
||||||
|
$studienplan_id,
|
||||||
|
$datum_bis
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isError($kalender_ids_result))
|
||||||
|
return $kalender_ids_result;
|
||||||
|
|
||||||
|
$kalender_ids = getData($kalender_ids_result);
|
||||||
|
|
||||||
|
$sync_result = $this->_syncKalenderIds($kalender_ids, $ziel_status);
|
||||||
|
|
||||||
|
if (isError($sync_result))
|
||||||
|
return $sync_result;
|
||||||
|
|
||||||
|
$sync_data = getData($sync_result);
|
||||||
|
|
||||||
|
if ($mail && !empty($sync_data['mail_infos']))
|
||||||
|
{
|
||||||
|
$this->_ci->load->library('KalenderNotificationLib');
|
||||||
|
$this->_ci->kalendernotificationlib->sendMails($sync_data['mail_infos']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success(['synced' => $sync_data['synced']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sync()
|
||||||
|
{
|
||||||
|
$this->_ci->KalenderModel->addSelect('tbl_kalender.*, tbl_kalender_ort.ort_kurzbz, tbl_kalender_ort.location');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'tbl_kalender.kalender_id = tbl_kalender_ort.kalender_id', 'LEFT');
|
||||||
|
$this->_ci->KalenderModel->db->where_in('status_kurzbz', array('sync_live', 'sync_preview', 'todelete'));
|
||||||
|
$this->_ci->KalenderModel->addOrder('tbl_kalender.kalender_id', 'DESC');
|
||||||
|
$to_update = $this->_ci->KalenderModel->load();
|
||||||
|
|
||||||
|
if (isError($to_update))
|
||||||
|
return $to_update;
|
||||||
|
|
||||||
|
if (!hasData($to_update))
|
||||||
|
return success(['synced' => 0]);
|
||||||
|
|
||||||
|
$mail_infos = [];
|
||||||
|
$synced = 0;
|
||||||
|
|
||||||
|
foreach (getData($to_update) as $entry)
|
||||||
|
{
|
||||||
|
if ($entry->status_kurzbz === 'todelete')
|
||||||
|
{
|
||||||
|
$this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'deleted'));
|
||||||
|
$mail_infos[] = array('entry' => $entry, 'new_status' => 'deleted', 'notify' => array('lektor', 'student'));
|
||||||
|
$synced++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($entry->status_kurzbz === 'sync_preview')
|
||||||
|
{
|
||||||
|
$this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'preview'));
|
||||||
|
$this->_archiveVorgaenger($entry->vorgaenger_kalender_id, true);
|
||||||
|
$mail_infos[] = array('entry' => $entry, 'new_status' => 'preview', 'notify' => array('lektor'));
|
||||||
|
$synced++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($entry->status_kurzbz === 'sync_live')
|
||||||
|
{
|
||||||
|
$this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'live'));
|
||||||
|
$this->_archiveVorgaenger($entry->vorgaenger_kalender_id, false);
|
||||||
|
$mail_infos[] = array('entry' => $entry, 'new_status' => 'live', 'notify' => array('lektor', 'student'));
|
||||||
|
$synced++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_ci->load->library('KalenderNotificationLib');
|
||||||
|
$this->_ci->kalendernotificationlib->sendMails($mail_infos);
|
||||||
|
|
||||||
|
return success(['synced' => $synced]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKalenderIds($oe_kurzbz, $studiensemester_kurzbz, $status_kurzbz_list, $ausbildungssemester = null, $studienplan_id = null, $datum_bis = null)
|
||||||
|
{
|
||||||
|
if (empty($status_kurzbz_list))
|
||||||
|
return success([]);
|
||||||
|
|
||||||
|
$studiengang_result = $this->_getStudiengang($oe_kurzbz);
|
||||||
|
|
||||||
|
if (isError($studiengang_result))
|
||||||
|
return $studiengang_result;
|
||||||
|
|
||||||
|
$studiengang_kz = getData($studiengang_result);
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->addDistinct('tbl_kalender.kalender_id');
|
||||||
|
$this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit', 'tbl_kalender.kalender_id = tbl_kalender_lehreinheit.kalender_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheit', 'tbl_kalender_lehreinheit.lehreinheit_id = tbl_lehreinheit.lehreinheit_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehrveranstaltung', 'tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id');
|
||||||
|
|
||||||
|
//TODO (david) oe_kurzbz rekursiv?
|
||||||
|
if (!is_null($studiengang_kz))
|
||||||
|
$this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.studiengang_kz', $studiengang_kz);
|
||||||
|
else
|
||||||
|
$this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.oe_kurzbz', $oe_kurzbz);
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->db->where('tbl_lehreinheit.studiensemester_kurzbz', $studiensemester_kurzbz);
|
||||||
|
$this->_ci->KalenderModel->db->where_in('tbl_kalender.status_kurzbz', $status_kurzbz_list);
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->db->where('NOT EXISTS (
|
||||||
|
SELECT 1 FROM lehre.tbl_kalender nachfolger
|
||||||
|
WHERE nachfolger.vorgaenger_kalender_id = tbl_kalender.kalender_id)', null, false);
|
||||||
|
|
||||||
|
if (!is_null($studienplan_id))
|
||||||
|
{
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_studienplan_lehrveranstaltung', 'tbl_lehrveranstaltung.lehrveranstaltung_id = tbl_studienplan_lehrveranstaltung.lehrveranstaltung_id');
|
||||||
|
$this->_ci->KalenderModel->db->where('tbl_studienplan_lehrveranstaltung.studienplan_id', $studienplan_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($ausbildungssemester))
|
||||||
|
$this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.semester', $ausbildungssemester);
|
||||||
|
|
||||||
|
if (!is_null($datum_bis))
|
||||||
|
{
|
||||||
|
$end_date = date('Y-m-d', strtotime($datum_bis . ' +1 day'));
|
||||||
|
$this->_ci->KalenderModel->db->where('tbl_kalender.von <', $end_date);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->_ci->KalenderModel->load();
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
if (!hasData($result))
|
||||||
|
return success([]);
|
||||||
|
|
||||||
|
return success(array_column(getData($result), 'kalender_id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function _archiveVorgaenger($vorgaenger_kalender_id, $stop_at_live = false)
|
||||||
|
{
|
||||||
|
if (!$vorgaenger_kalender_id) return;
|
||||||
|
|
||||||
|
$current_id = $vorgaenger_kalender_id;
|
||||||
|
|
||||||
|
while ($current_id)
|
||||||
|
{
|
||||||
|
$vorgaenger = $this->_ci->KalenderModel->load(array('kalender_id' => $current_id));
|
||||||
|
if (!hasData($vorgaenger)) return;
|
||||||
|
|
||||||
|
$vorgaenger = getData($vorgaenger)[0];
|
||||||
|
|
||||||
|
if ($stop_at_live && $vorgaenger->status_kurzbz === 'live')
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (in_array($vorgaenger->status_kurzbz, array('preview', 'live')))
|
||||||
|
{
|
||||||
|
$this->_ci->KalenderModel->update(
|
||||||
|
array('kalender_id' => $current_id),
|
||||||
|
array('status_kurzbz' => 'archived')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$current_id = $vorgaenger->vorgaenger_kalender_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _syncKalenderIds($kalender_ids, $ziel_status)
|
||||||
|
{
|
||||||
|
if (empty($kalender_ids))
|
||||||
|
return success(['synced' => 0, 'mail_infos' => []]);
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->addSelect('tbl_kalender.*, tbl_kalender_ort.ort_kurzbz, tbl_kalender_ort.location');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'tbl_kalender.kalender_id = tbl_kalender_ort.kalender_id', 'LEFT');
|
||||||
|
$this->_ci->KalenderModel->db->where_in('tbl_kalender.kalender_id', $kalender_ids);
|
||||||
|
$this->_ci->KalenderModel->addOrder('tbl_kalender.kalender_id', 'DESC');
|
||||||
|
$to_update = $this->_ci->KalenderModel->load();
|
||||||
|
|
||||||
|
if (isError($to_update))
|
||||||
|
return $to_update;
|
||||||
|
|
||||||
|
if (!hasData($to_update))
|
||||||
|
return success(['synced' => 0, 'mail_infos' => []]);
|
||||||
|
|
||||||
|
$mail_infos = [];
|
||||||
|
$synced = 0;
|
||||||
|
|
||||||
|
foreach (getData($to_update) as $entry)
|
||||||
|
{
|
||||||
|
if ($entry->status_kurzbz === 'todelete')
|
||||||
|
{
|
||||||
|
$this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'deleted'));
|
||||||
|
$mail_infos[] = array('entry' => $entry, 'new_status' => 'deleted', 'notify' => array('lektor', 'student'));
|
||||||
|
$synced++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ziel_status === 'preview' && in_array($entry->status_kurzbz, array('planning', 'sync_preview')))
|
||||||
|
{
|
||||||
|
$this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'preview'));
|
||||||
|
$this->_archiveVorgaenger($entry->vorgaenger_kalender_id, true);
|
||||||
|
$mail_infos[] = array('entry' => $entry, 'new_status' => 'preview', 'notify' => array('lektor'));
|
||||||
|
$synced++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ziel_status === 'live' && in_array($entry->status_kurzbz, array('planning', 'sync_preview', 'sync_live')))
|
||||||
|
{
|
||||||
|
$this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'live'));
|
||||||
|
$this->_archiveVorgaenger($entry->vorgaenger_kalender_id, false);
|
||||||
|
$mail_infos[] = array('entry' => $entry, 'new_status' => 'live', 'notify' => array('lektor', 'student'));
|
||||||
|
$synced++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ziel_status === 'live' && $entry->status_kurzbz === 'preview')
|
||||||
|
{
|
||||||
|
$current = getData($this->_ci->KalenderModel->load(array('kalender_id' => $entry->kalender_id)))[0];
|
||||||
|
if ($current->status_kurzbz === 'archived')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'live'));
|
||||||
|
$this->_archiveVorgaenger($entry->vorgaenger_kalender_id, false);
|
||||||
|
$mail_infos[] = array('entry' => $entry, 'new_status' => 'live', 'notify' => array('lektor', 'student'));
|
||||||
|
$synced++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success(['synced' => $synced, 'mail_infos' => $mail_infos]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function _getStudiengang($oe_kurzbz)
|
||||||
|
{
|
||||||
|
$this->_ci->StudiengangModel->addJoin('tbl_organisationseinheit', 'oe_kurzbz');
|
||||||
|
$result = $this->_ci->StudiengangModel->loadWhere(array('oe_kurzbz' => $oe_kurzbz));
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
if (!hasData($result))
|
||||||
|
return success(null);
|
||||||
|
|
||||||
|
return success(getData($result)[0]->studiengang_kz);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,275 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (! defined("BASEPATH")) exit("No direct script access allowed");
|
||||||
|
|
||||||
|
use CI3_Events as Events;
|
||||||
|
|
||||||
|
class RaumvorschlagLib
|
||||||
|
{
|
||||||
|
private $_ci;
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->_ci =& get_instance();
|
||||||
|
|
||||||
|
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||||
|
$this->_ci->load->model('ressource/Kalender_Lehreinheit_model', 'KalenderLehreinheitModel');
|
||||||
|
$this->_ci->load->model('ressource/Kalender_Event_model', 'KalenderEventModel');
|
||||||
|
$this->_ci->load->model('ressource/Kalender_Event_Teilnehmer_model', 'KalenderEventTeilnehmerModel');
|
||||||
|
$this->_ci->load->model('ressource/Kalender_Ort_model', 'KalenderOrtModel');
|
||||||
|
$this->_ci->load->model('education/Lehreinheit_model', 'LehreinheitModel');
|
||||||
|
$this->_ci->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
|
||||||
|
$this->_ci->load->model('education/LehreinheitMitarbeiter_model', 'LehreinheitMitarbeiterModel');
|
||||||
|
$this->_ci->load->model('ressource/Ort_model', 'OrtModel');
|
||||||
|
$this->_ci->load->model('organisation/gruppe_model', 'GruppeModel');
|
||||||
|
$this->_ci->load->model('organisation/Lehrverband_model', 'LehrverbandModel');
|
||||||
|
|
||||||
|
$this->_ci->load->model('education/Lehreinheitgruppe_model', 'LehreinheitgruppeModel');
|
||||||
|
$this->_ci->load->model('education/Lehreinheitgruppe_model', 'LehreinheitgruppeModel');
|
||||||
|
|
||||||
|
$this->_ci->load->library('CollisionChecker');
|
||||||
|
$this->_ci->load->library('KalenderLib');
|
||||||
|
$this->_ci->load->library('PhrasesLib', array('ui'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getVorschlaege($kalender_id)
|
||||||
|
{
|
||||||
|
$event = $this->_ci->kalenderlib->getByKalenderId($kalender_id);
|
||||||
|
$event = $event[0];
|
||||||
|
|
||||||
|
$lektor_uids = array_column($event->lektor, 'mitarbeiter_uid');
|
||||||
|
$gruppen_kurzbz = array_values(array_filter(array_column($event->gruppe, 'gruppe_kurzbz')));
|
||||||
|
|
||||||
|
$lehrverband_gruppen = array_values(array_filter($event->gruppe, function($gruppe)
|
||||||
|
{
|
||||||
|
return empty($gruppe['gruppe_kurzbz']);
|
||||||
|
}));
|
||||||
|
|
||||||
|
$tages_events = $this->_ci->kalenderlib->getForRaumvorschlag(
|
||||||
|
$event->datum,
|
||||||
|
$event->datum,
|
||||||
|
$lektor_uids,
|
||||||
|
$gruppen_kurzbz,
|
||||||
|
$lehrverband_gruppen
|
||||||
|
);
|
||||||
|
|
||||||
|
$lektor_davor = $this->_getEventDavor($tages_events, $event->isostart, $lektor_uids, 'lektor');
|
||||||
|
$gruppen_davor = $this->_getEventDavor($tages_events, $event->isostart, $gruppen_kurzbz, 'gruppe');
|
||||||
|
|
||||||
|
$lektor_davor_ort = $lektor_davor ? $this->_getOrtDetails($lektor_davor->ort_kurzbz) : null;
|
||||||
|
$gruppen_davor_ort = $gruppen_davor ? $this->_getOrtDetails($gruppen_davor->ort_kurzbz) : null;
|
||||||
|
|
||||||
|
$kandidaten = $this->_getRaumkandidaten($event);
|
||||||
|
if (empty($kandidaten)) return [];
|
||||||
|
|
||||||
|
|
||||||
|
$ratings = [];
|
||||||
|
foreach ($kandidaten as $raum)
|
||||||
|
{
|
||||||
|
$rating = ['ort_kurzbz' => $raum->ort_kurzbz, 'score' => 100, 'details' => []];
|
||||||
|
$this->_rateLektor($rating, $raum, $lektor_davor_ort);
|
||||||
|
$this->_rateGruppen($rating, $raum, $gruppen_davor_ort);
|
||||||
|
|
||||||
|
Events::trigger('room_rating',
|
||||||
|
function & () use (&$rating) {
|
||||||
|
return $rating;
|
||||||
|
},
|
||||||
|
$raum,
|
||||||
|
$event
|
||||||
|
);
|
||||||
|
$ratings[] = $rating;
|
||||||
|
}
|
||||||
|
|
||||||
|
usort($ratings, function($a, $b)
|
||||||
|
{
|
||||||
|
return $b['score'] - $a['score'];
|
||||||
|
});
|
||||||
|
|
||||||
|
return $ratings;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _getOrtDetails($ort_kurzbz_array)
|
||||||
|
{
|
||||||
|
$ort_kurzbz_array = (array) $ort_kurzbz_array;
|
||||||
|
if (empty($ort_kurzbz_array)) return [];
|
||||||
|
$this->_ci->OrtModel->addSelect('ort_kurzbz, stockwerk, standort_id');
|
||||||
|
$this->_ci->OrtModel->db->where_in('ort_kurzbz', $ort_kurzbz_array);
|
||||||
|
$result = $this->_ci->OrtModel->load();
|
||||||
|
|
||||||
|
return hasData($result) ? getData($result) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _rateLektor(&$rating, $raum, $lektor_davor_orte)
|
||||||
|
{
|
||||||
|
if (empty($lektor_davor_orte)) return;
|
||||||
|
|
||||||
|
foreach ($lektor_davor_orte as $ort)
|
||||||
|
{
|
||||||
|
if ($ort->ort_kurzbz === $raum->ort_kurzbz)
|
||||||
|
{
|
||||||
|
$rating['score'] += 20;
|
||||||
|
$rating['details'][] = '+20 ' . $this->_ci->phraseslib->t('ui', 'lecturer_already_here');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$gleiches_gebaeude = array_filter($lektor_davor_orte, function($ort) use ($raum)
|
||||||
|
{
|
||||||
|
return $ort->standort_id === $raum->standort_id;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (empty($gleiches_gebaeude))
|
||||||
|
{
|
||||||
|
$rating['score'] -= 20;
|
||||||
|
$rating['details'][] = '-20 '. $this->_ci->phraseslib->t('ui', 'lecturer_building_change');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$diffs = array_map(function($ort) use ($raum)
|
||||||
|
{
|
||||||
|
return abs($ort->stockwerk - $raum->stockwerk);
|
||||||
|
}, $gleiches_gebaeude);
|
||||||
|
|
||||||
|
$diff = min($diffs);
|
||||||
|
|
||||||
|
if ($diff > 0)
|
||||||
|
{
|
||||||
|
$rating['score'] -= $diff * 5;
|
||||||
|
$rating['details'][] = '-' . ($diff * 5) . ' ' . $this->_ci->phraseslib->t('ui', 'lecturer_floor_change');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _rateGruppen(&$rating, $raum, $gruppen_davor_orte)
|
||||||
|
{
|
||||||
|
if (empty($gruppen_davor_orte)) return;
|
||||||
|
|
||||||
|
foreach ($gruppen_davor_orte as $ort)
|
||||||
|
{
|
||||||
|
if ($ort->ort_kurzbz === $raum->ort_kurzbz)
|
||||||
|
{
|
||||||
|
$rating['score'] += 10;
|
||||||
|
$rating['details'][] = '+10 ' . $this->_ci->phraseslib->t('ui', 'student_already_here');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$gleiches_gebaeude = array_filter($gruppen_davor_orte, function($ort) use ($raum)
|
||||||
|
{
|
||||||
|
return $ort->standort_id === $raum->standort_id;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (empty($gleiches_gebaeude))
|
||||||
|
{
|
||||||
|
$rating['score'] -= 20;
|
||||||
|
$rating['details'][] = '-20 '. $this->_ci->phraseslib->t('ui', 'student_building_change');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$diffs = array_map(function($ort) use ($raum)
|
||||||
|
{
|
||||||
|
return abs($ort->stockwerk - $raum->stockwerk);
|
||||||
|
}, $gleiches_gebaeude);
|
||||||
|
|
||||||
|
$diff = min($diffs);
|
||||||
|
|
||||||
|
if ($diff > 0)
|
||||||
|
{
|
||||||
|
$rating['score'] -= $diff * 5;
|
||||||
|
$rating['details'][] = '-' . ($diff * 5) . ' '. $this->_ci->phraseslib->t('ui', 'student_floor_change');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _getEventDavor($events, $von, $uids, $type)
|
||||||
|
{
|
||||||
|
$kandidat = null;
|
||||||
|
|
||||||
|
foreach ($events as $event)
|
||||||
|
{
|
||||||
|
if ($event->isoend > $von)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//Wenn zwischen zwei Events eine 30+ Minuten Pause liegt, wird das Event davor nicht berücksichtigt
|
||||||
|
if ((strtotime($von) - strtotime($event->isoend)) > 30 * 60)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (empty($event->ort_kurzbz))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ($type === 'lektor')
|
||||||
|
$event_uids = array_column($event->lektor, 'mitarbeiter_uid');
|
||||||
|
else
|
||||||
|
$event_uids = array_column($event->gruppe, 'gruppe_kurzbz');
|
||||||
|
|
||||||
|
if (empty(array_intersect($event_uids, $uids)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ($kandidat === null || $event->isoend > $kandidat->isoend)
|
||||||
|
$kandidat = $event;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $kandidat;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _getRaumkandidaten($event)
|
||||||
|
{
|
||||||
|
$lehreinheit = $this->_ci->LehreinheitModel->load($event->lehreinheit_id[0]);
|
||||||
|
if (!hasData($lehreinheit)) return [];
|
||||||
|
$lehreinheit = getData($lehreinheit)[0];
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->addSelect('tbl_kalender_ort.ort_kurzbz');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'tbl_kalender.kalender_id = tbl_kalender_ort.kalender_id');
|
||||||
|
$this->_ci->KalenderModel->db->where('tbl_kalender.von <', $event->isoend);
|
||||||
|
$this->_ci->KalenderModel->db->where('tbl_kalender.bis >', $event->isostart);
|
||||||
|
$this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', ['deleted']);
|
||||||
|
$this->_ci->KalenderModel->db->where('tbl_kalender_ort.ort_kurzbz IS NOT NULL', null, false);
|
||||||
|
$belegte = $this->_ci->KalenderModel->load();
|
||||||
|
|
||||||
|
$belegte_orte = hasData($belegte) ? array_column(getData($belegte), 'ort_kurzbz') : [];
|
||||||
|
|
||||||
|
if (empty($lehreinheit->raumtyp))
|
||||||
|
{
|
||||||
|
$raeume = $this->_getFreieRaeume(null, $belegte_orte);
|
||||||
|
return hasData($raeume) ? getData($raeume) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$vorschlaege = [];
|
||||||
|
|
||||||
|
$raeume = $this->_getFreieRaeume($lehreinheit->raumtyp, $belegte_orte);
|
||||||
|
if (hasData($raeume))
|
||||||
|
$vorschlaege = getData($raeume);
|
||||||
|
|
||||||
|
if (count($vorschlaege) < 5 && !empty($lehreinheit->raumtypalternativ))
|
||||||
|
{
|
||||||
|
$bereits_gefunden = array_merge($belegte_orte, array_column($vorschlaege, 'ort_kurzbz'));
|
||||||
|
$alternativ = $this->_getFreieRaeume($lehreinheit->raumtypalternativ, $bereits_gefunden);
|
||||||
|
|
||||||
|
if (!isError($alternativ) && hasData($alternativ))
|
||||||
|
$vorschlaege = array_merge($vorschlaege, getData($alternativ));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $vorschlaege;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _getFreieRaeume($raumtyp, $belegte_orte)
|
||||||
|
{
|
||||||
|
$this->_ci->OrtModel->addSelect('ort_kurzbz, stockwerk, standort_id');
|
||||||
|
$this->_ci->OrtModel->addJoin('public.tbl_ortraumtyp', 'ort_kurzbz');
|
||||||
|
$this->_ci->OrtModel->db->where('raumtyp_kurzbz', $raumtyp);
|
||||||
|
$this->_ci->OrtModel->db->where('aktiv', true);
|
||||||
|
$this->_ci->OrtModel->db->where("ort_kurzbz NOT LIKE '\_%'", null, false);
|
||||||
|
|
||||||
|
if (!empty($belegte_orte))
|
||||||
|
$this->_ci->OrtModel->db->where_not_in('ort_kurzbz', $belegte_orte);
|
||||||
|
$this->_ci->OrtModel->addOrder('hierarchie, ort_kurzbz');
|
||||||
|
|
||||||
|
return $this->_ci->OrtModel->load();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class LectureCollisionCheck implements ICollisionCheck
|
||||||
|
{
|
||||||
|
|
||||||
|
private $_ci;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->_ci =& get_instance();
|
||||||
|
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||||
|
$this->_ci->load->model('ressource/zeitsperre_model', 'ZeitsperreModel');
|
||||||
|
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
|
||||||
|
$this->_ci->load->library('PhrasesLib', array('ui'));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'lecture';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function check($data)
|
||||||
|
{
|
||||||
|
if (!isset($data->von, $data->bis, $data->kalender_id)) return [];
|
||||||
|
|
||||||
|
if ($this->_ci->variablelib->getVar('ignore_kollision') === 'true') return [];
|
||||||
|
|
||||||
|
$uids = $this->_getUids($data->kalender_id);
|
||||||
|
|
||||||
|
if (empty($uids)) return [];
|
||||||
|
|
||||||
|
$collisions = [];
|
||||||
|
|
||||||
|
$collisions = array_merge($collisions, $this->_checkLehreinheit($uids, $data));
|
||||||
|
$collisions = array_merge($collisions, $this->_checkReservierung($uids, $data));
|
||||||
|
$collisions = array_merge($collisions, $this->_checkZeitsperre($uids, $data));
|
||||||
|
|
||||||
|
return $collisions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkAll($kalender_ids)
|
||||||
|
{
|
||||||
|
if (empty($kalender_ids)) return [];
|
||||||
|
|
||||||
|
$kollisionsfreie_user = unserialize(KOLLISIONSFREIE_USER);
|
||||||
|
$grouped = [];
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->addSelect('DISTINCT ON (tbl_kalender.kalender_id) tbl_kalender.kalender_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit current_kalender_le', 'current_kalender_le.kalender_id = tbl_kalender.kalender_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheit current_lehreinheit', 'current_lehreinheit.lehreinheit_id = current_kalender_le.lehreinheit_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheitmitarbeiter current_lehreinheit_ma', 'current_lehreinheit_ma.lehreinheit_id = current_lehreinheit.lehreinheit_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheitmitarbeiter other_lehreinheithreinheit_ma', 'other_lehreinheithreinheit_ma.mitarbeiter_uid = current_lehreinheit_ma.mitarbeiter_uid');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheit other_lehreinheit', 'other_lehreinheit.lehreinheit_id = other_lehreinheithreinheit_ma.lehreinheit_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit other_kalender_le', 'other_kalender_le.lehreinheit_id = other_lehreinheit.lehreinheit_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender other_kalender', 'other_kalender.kalender_id = other_kalender_le.kalender_id');
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->db->where('other_kalender.kalender_id != tbl_kalender.kalender_id', null, false);
|
||||||
|
$this->_ci->KalenderModel->db->where('other_kalender.von < tbl_kalender.bis', null, false);
|
||||||
|
$this->_ci->KalenderModel->db->where('other_kalender.bis > tbl_kalender.von', null, false);
|
||||||
|
$this->_ci->KalenderModel->db->where_not_in('other_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete']);
|
||||||
|
$this->_ci->KalenderModel->db->where_not_in('current_lehreinheit_ma.mitarbeiter_uid', $kollisionsfreie_user);
|
||||||
|
$this->_ci->KalenderModel->db->where_in('tbl_kalender.kalender_id', $kalender_ids);
|
||||||
|
$this->_ci->KalenderModel->db->where(
|
||||||
|
'other_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)',
|
||||||
|
null, false
|
||||||
|
);
|
||||||
|
$result = $this->_ci->KalenderModel->load();
|
||||||
|
if (!isError($result) && hasData($result))
|
||||||
|
{
|
||||||
|
foreach (getData($result) as $row)
|
||||||
|
{
|
||||||
|
$grouped[$row->kalender_id][] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->addSelect('DISTINCT ON (tbl_kalender.kalender_id) tbl_kalender.kalender_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit current_kalender_le', 'current_kalender_le.kalender_id = tbl_kalender.kalender_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheit current_lehreinheit', 'current_lehreinheit.lehreinheit_id = current_kalender_le.lehreinheit_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheitmitarbeiter current_lehreinheit_ma', 'current_lehreinheit_ma.lehreinheit_id = current_lehreinheit.lehreinheit_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_event_teilnehmer other_t', 'other_t.uid = current_lehreinheit_ma.mitarbeiter_uid');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_event other_e', 'other_e.kalender_id = other_t.kalender_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender other_kalender', 'other_kalender.kalender_id = other_e.kalender_id');
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->db->where('other_kalender.kalender_id != tbl_kalender.kalender_id', null, false);
|
||||||
|
$this->_ci->KalenderModel->db->where('other_kalender.von < tbl_kalender.bis', null, false);
|
||||||
|
$this->_ci->KalenderModel->db->where('other_kalender.bis > tbl_kalender.von', null, false);
|
||||||
|
$this->_ci->KalenderModel->db->where_not_in('other_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete']);
|
||||||
|
$this->_ci->KalenderModel->db->where_not_in('current_lehreinheit_ma.mitarbeiter_uid', $kollisionsfreie_user);
|
||||||
|
$this->_ci->KalenderModel->db->where_in('tbl_kalender.kalender_id', $kalender_ids);
|
||||||
|
$this->_ci->KalenderModel->db->where(
|
||||||
|
'other_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)',
|
||||||
|
null, false
|
||||||
|
);
|
||||||
|
$result = $this->_ci->KalenderModel->load();
|
||||||
|
if (!isError($result) && hasData($result))
|
||||||
|
{
|
||||||
|
foreach (getData($result) as $row)
|
||||||
|
{
|
||||||
|
$grouped[$row->kalender_id][] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->addSelect('DISTINCT ON (tbl_kalender.kalender_id) tbl_kalender.kalender_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit current_kalender_le', 'current_kalender_le.kalender_id = tbl_kalender.kalender_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheit current_lehreinheit', 'current_lehreinheit.lehreinheit_id = current_kalender_le.lehreinheit_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheitmitarbeiter current_lehreinheit_ma', 'current_lehreinheit_ma.lehreinheit_id = current_lehreinheit.lehreinheit_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('campus.tbl_zeitsperre z',
|
||||||
|
"z.mitarbeiter_uid = current_lehreinheit_ma.mitarbeiter_uid
|
||||||
|
AND z.zeitsperretyp_kurzbz != 'ZVerfueg'");
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_stunde vonstunde_z', 'vonstunde_z.stunde = z.vonstunde', 'LEFT');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_stunde bisstunde_z', 'bisstunde_z.stunde = z.bisstunde', 'LEFT');
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->db->where('(z.vondatum + COALESCE(vonstunde_z.beginn, \'00:00\'))::timestamp < tbl_kalender.bis', null, false);
|
||||||
|
$this->_ci->KalenderModel->db->where('(z.bisdatum + COALESCE(bisstunde_z.ende, \'23:59\'))::timestamp > tbl_kalender.von', null, false);
|
||||||
|
$this->_ci->KalenderModel->db->where_not_in('current_lehreinheit_ma.mitarbeiter_uid', $kollisionsfreie_user);
|
||||||
|
$this->_ci->KalenderModel->db->where_in('tbl_kalender.kalender_id', $kalender_ids);
|
||||||
|
$result = $this->_ci->KalenderModel->load();
|
||||||
|
|
||||||
|
|
||||||
|
if (!isError($result) && hasData($result))
|
||||||
|
{
|
||||||
|
foreach (getData($result) as $row)
|
||||||
|
{
|
||||||
|
$grouped[$row->kalender_id][] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $grouped;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _getUids($kalender_id)
|
||||||
|
{
|
||||||
|
$kollisionsfreie_user = unserialize(KOLLISIONSFREIE_USER);
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->addDistinct('mitarbeiter_uid, tbl_kalender_event_teilnehmer.uid');
|
||||||
|
$this->_ci->KalenderModel->addSelect('mitarbeiter_uid, tbl_kalender_event_teilnehmer.uid');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit', 'kalender_id', 'LEFT');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheit', 'lehreinheit_id', 'LEFT');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheitmitarbeiter', 'lehreinheit_id', 'LEFT');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_event', 'kalender_id', 'LEFT');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_event_teilnehmer', 'tbl_kalender_event.kalender_id = tbl_kalender_event_teilnehmer.kalender_id', 'LEFT');
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->db->group_start();
|
||||||
|
$this->_ci->KalenderModel->db->where_not_in('mitarbeiter_uid', $kollisionsfreie_user);
|
||||||
|
$this->_ci->KalenderModel->db->or_where('mitarbeiter_uid IS NULL', null, false);
|
||||||
|
$this->_ci->KalenderModel->db->group_end();
|
||||||
|
|
||||||
|
$result = $this->_ci->KalenderModel->loadWhere(array(
|
||||||
|
'tbl_kalender.kalender_id' => $kalender_id
|
||||||
|
));
|
||||||
|
|
||||||
|
if (isError($result) || !hasData($result)) return [];
|
||||||
|
|
||||||
|
$data = getData($result);
|
||||||
|
$mitarbeiter_uids = array_filter(array_column($data, 'mitarbeiter_uid'));
|
||||||
|
$event_teilnehmer = array_filter(array_column($data, 'uid'));
|
||||||
|
|
||||||
|
return array_unique(array_merge($mitarbeiter_uids, $event_teilnehmer));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _checkLehreinheit($uids, $data)
|
||||||
|
{
|
||||||
|
$kollisionsfreie_user = unserialize(KOLLISIONSFREIE_USER);
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->addDistinct('mitarbeiter_uid, tbl_kalender.von, tbl_kalender.bis');
|
||||||
|
$this->_ci->KalenderModel->addSelect('mitarbeiter_uid, tbl_kalender.von, tbl_kalender.bis');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit', 'kalender_id', 'LEFT');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheit', 'lehreinheit_id', 'LEFT');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheitmitarbeiter', 'lehreinheit_id', 'LEFT');
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->db->where_in('mitarbeiter_uid', $uids);
|
||||||
|
$this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $data->kalender_id);
|
||||||
|
$this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', array('archived', 'deleted', 'to_delete'));
|
||||||
|
$this->_ci->KalenderModel->db->where_not_in('mitarbeiter_uid', $kollisionsfreie_user);
|
||||||
|
$this->_ci->KalenderModel->db->where(
|
||||||
|
'tbl_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)',
|
||||||
|
null, false
|
||||||
|
);
|
||||||
|
|
||||||
|
$result = $this->_ci->KalenderModel->loadWhere(array(
|
||||||
|
'von <' => $data->bis,
|
||||||
|
'bis >' => $data->von,
|
||||||
|
));
|
||||||
|
|
||||||
|
if (isError($result) || !hasData($result)) return [];
|
||||||
|
|
||||||
|
return array_map(function($row)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'message' => $this->_ci->phraseslib->t('ui', 'ma_le_kollision') . ': ' . $row->mitarbeiter_uid . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')',
|
||||||
|
'errorCode' => 'lector_collision',
|
||||||
|
];
|
||||||
|
}, getData($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _checkReservierung($uids, $data)
|
||||||
|
{
|
||||||
|
if ($this->_ci->variablelib->getVar('ignore_reservierung') === 'true') return [];
|
||||||
|
|
||||||
|
$kollisionsfreie_user = unserialize(KOLLISIONSFREIE_USER);
|
||||||
|
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->addDistinct('tbl_kalender_event_teilnehmer.uid, tbl_kalender.von, tbl_kalender.bis');
|
||||||
|
$this->_ci->KalenderModel->addSelect('tbl_kalender_event_teilnehmer.uid, tbl_kalender.von, tbl_kalender.bis');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_event', 'kalender_id', 'LEFT');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_event_teilnehmer', 'tbl_kalender_event.kalender_id = tbl_kalender_event_teilnehmer.kalender_id', 'LEFT');
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->db->where_in('tbl_kalender_event_teilnehmer.uid', $uids);
|
||||||
|
$this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $data->kalender_id);
|
||||||
|
$this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', array('archived', 'deleted', 'to_delete'));
|
||||||
|
$this->_ci->KalenderModel->db->where_not_in('uid', $kollisionsfreie_user);
|
||||||
|
$this->_ci->KalenderModel->db->where(
|
||||||
|
'tbl_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)',
|
||||||
|
null, false
|
||||||
|
);
|
||||||
|
$result = $this->_ci->KalenderModel->loadWhere(array(
|
||||||
|
'von <' => $data->bis,
|
||||||
|
'bis >' => $data->von,
|
||||||
|
));
|
||||||
|
|
||||||
|
if (isError($result) || !hasData($result)) return [];
|
||||||
|
|
||||||
|
return array_map(function($row)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'message' => $this->_ci->phraseslib->t('ui', 'reservierung_kollision') . ': ' . $row->uid . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')',
|
||||||
|
'errorCode' => 'reservation_collision',
|
||||||
|
];
|
||||||
|
}, getData($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _checkZeitsperre($uids, $data)
|
||||||
|
{
|
||||||
|
if ($this->_ci->variablelib->getVar('ignore_zeitsperre') === 'true') return [];
|
||||||
|
|
||||||
|
$this->_ci->ZeitsperreModel->addSelect('mitarbeiter_uid, vondatum, vonstunde_z.beginn as von_beginn, bisdatum, bisstunde_z.ende as bis_ende');
|
||||||
|
$this->_ci->ZeitsperreModel->addJoin('lehre.tbl_stunde vonstunde_z', 'vonstunde_z.stunde = tbl_zeitsperre.vonstunde', 'LEFT');
|
||||||
|
$this->_ci->ZeitsperreModel->addJoin('lehre.tbl_stunde bisstunde_z', 'bisstunde_z.stunde = tbl_zeitsperre.bisstunde', 'LEFT');
|
||||||
|
$this->_ci->ZeitsperreModel->db->where('zeitsperretyp_kurzbz !=', 'ZVerfueg');
|
||||||
|
$this->_ci->ZeitsperreModel->db->where('(tbl_zeitsperre.vondatum + COALESCE(vonstunde_z.beginn, \'00:00\'))::timestamp <', $data->bis);
|
||||||
|
$this->_ci->ZeitsperreModel->db->where('(tbl_zeitsperre.bisdatum + COALESCE(bisstunde_z.ende, \'23:59\'))::timestamp >', $data->von);
|
||||||
|
|
||||||
|
$this->_ci->ZeitsperreModel->db->where_in('mitarbeiter_uid', $uids);
|
||||||
|
$result = $this->_ci->ZeitsperreModel->load();
|
||||||
|
|
||||||
|
if (isError($result) || !hasData($result)) return [];
|
||||||
|
|
||||||
|
return array_map(function($row)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'message' => $this->_ci->phraseslib->t('ui', 'ma_zeitsperre_kollision') . ': ' . $row->mitarbeiter_uid . ' (' . date('d.m.Y H:i', strtotime($row->vondatum . ' ' . $row->von_beginn)) . ' - ' . date('d.m.Y H:i', strtotime($row->bisdatum . ' ' . $row->bis_ende)) . ')',
|
||||||
|
'errorCode' => 'absences_collision',
|
||||||
|
];
|
||||||
|
}, getData($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class RoomCollisionCheck implements ICollisionCheck
|
||||||
|
{
|
||||||
|
|
||||||
|
private $_ci;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->_ci =& get_instance();
|
||||||
|
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||||
|
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
|
||||||
|
$this->_ci->load->library('PhrasesLib', array('ui'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'room';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function check($data)
|
||||||
|
{
|
||||||
|
if (!isset($data->ort_kurzbz, $data->von, $data->bis, $data->kalender_id) || isEmptyArray($data->ort_kurzbz)) return [];
|
||||||
|
|
||||||
|
if ($this->_ci->variablelib->getVar('ignore_kollision') === 'true') return [];
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->addSelect('kalender_id, ort_kurzbz, von, bis');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'kalender_id');
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $data->kalender_id);
|
||||||
|
$this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)', null, false);
|
||||||
|
$this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete']);
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->db->where_in('ort_kurzbz', $data->ort_kurzbz);
|
||||||
|
$result = $this->_ci->KalenderModel->loadWhere(array(
|
||||||
|
'von <' => $data->bis,
|
||||||
|
'bis >' => $data->von,
|
||||||
|
));
|
||||||
|
|
||||||
|
if (isError($result)) return [];
|
||||||
|
if (!hasData($result)) return [];
|
||||||
|
|
||||||
|
return array_map(function($row)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'errorCode' => 'room_collision',
|
||||||
|
'message' => $this->_ci->phraseslib->t('ui', 'raum_kollision') . ': ' . $row->ort_kurzbz . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')'
|
||||||
|
];
|
||||||
|
}, getData($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkAll($kalender_ids)
|
||||||
|
{
|
||||||
|
if (empty($kalender_ids)) return [];
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->addSelect('DISTINCT ON (tbl_kalender.kalender_id) tbl_kalender.kalender_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort current_ort', 'current_ort.kalender_id = tbl_kalender.kalender_id');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort other_ort', 'other_ort.ort_kurzbz = current_ort.ort_kurzbz');
|
||||||
|
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender other_kalender', 'other_kalender.kalender_id = other_ort.kalender_id');
|
||||||
|
|
||||||
|
$this->_ci->KalenderModel->db->where('other_kalender.kalender_id != tbl_kalender.kalender_id', null, false);
|
||||||
|
$this->_ci->KalenderModel->db->where('other_kalender.von < tbl_kalender.bis', null, false);
|
||||||
|
$this->_ci->KalenderModel->db->where('other_kalender.bis > tbl_kalender.von', null, false);
|
||||||
|
$this->_ci->KalenderModel->db->where_not_in('other_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete']);
|
||||||
|
$this->_ci->KalenderModel->db->where_in('tbl_kalender.kalender_id', $kalender_ids);
|
||||||
|
$this->_ci->KalenderModel->db->where('other_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)', null, false);
|
||||||
|
|
||||||
|
$result = $this->_ci->KalenderModel->load();
|
||||||
|
|
||||||
|
|
||||||
|
if (isError($result) || !hasData($result)) return [];
|
||||||
|
|
||||||
|
$grouped = [];
|
||||||
|
foreach (getData($result) as $row)
|
||||||
|
{
|
||||||
|
$grouped[$row->kalender_id][] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $grouped;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class StudentCollisionCheck implements ICollisionCheck
|
||||||
|
{
|
||||||
|
private $_ci;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->_ci =& get_instance();
|
||||||
|
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||||
|
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
|
||||||
|
$this->_ci->load->library('PhrasesLib', array('ui'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'student';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function check($data)
|
||||||
|
{
|
||||||
|
if (!isset($data->von, $data->bis, $data->kalender_id)) return [];
|
||||||
|
|
||||||
|
if ($this->_ci->variablelib->getVar('ignore_kollision') === 'true') return [];
|
||||||
|
if ($this->_ci->variablelib->getVar('kollision_student') !== 'true') return [];
|
||||||
|
|
||||||
|
$kollisionsfreie_user = unserialize(KOLLISIONSFREIE_USER);
|
||||||
|
$placeholders = implode(',', array_fill(0, count($kollisionsfreie_user), '?'));
|
||||||
|
|
||||||
|
|
||||||
|
$dbModel = new DB_Model();
|
||||||
|
|
||||||
|
$qry1 = "
|
||||||
|
SELECT DISTINCT tbl_benutzergruppe.uid
|
||||||
|
FROM lehre.tbl_kalender
|
||||||
|
JOIN lehre.tbl_kalender_lehreinheit USING(kalender_id)
|
||||||
|
JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_kalender_lehreinheit.lehreinheit_id
|
||||||
|
JOIN lehre.tbl_lehreinheitgruppe ON tbl_lehreinheitgruppe.lehreinheit_id = tbl_lehreinheit.lehreinheit_id
|
||||||
|
JOIN public.tbl_gruppe
|
||||||
|
ON tbl_gruppe.studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz
|
||||||
|
AND tbl_gruppe.semester = tbl_lehreinheitgruppe.semester
|
||||||
|
AND tbl_gruppe.gruppe_kurzbz = tbl_lehreinheitgruppe.gruppe_kurzbz
|
||||||
|
JOIN public.tbl_benutzergruppe ON tbl_benutzergruppe.gruppe_kurzbz = tbl_gruppe.gruppe_kurzbz
|
||||||
|
AND tbl_benutzergruppe.studiensemester_kurzbz = tbl_lehreinheit.studiensemester_kurzbz
|
||||||
|
|
||||||
|
WHERE tbl_kalender.kalender_id = ?
|
||||||
|
AND tbl_benutzergruppe.uid NOT IN ($placeholders)
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT DISTINCT tbl_studentlehrverband.student_uid AS uid
|
||||||
|
FROM lehre.tbl_kalender
|
||||||
|
JOIN lehre.tbl_kalender_lehreinheit USING(kalender_id)
|
||||||
|
JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_kalender_lehreinheit.lehreinheit_id
|
||||||
|
JOIN lehre.tbl_lehreinheitgruppe ON tbl_lehreinheitgruppe.lehreinheit_id = tbl_lehreinheit.lehreinheit_id
|
||||||
|
JOIN public.tbl_studentlehrverband
|
||||||
|
ON tbl_studentlehrverband.studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz
|
||||||
|
AND tbl_studentlehrverband.semester = tbl_lehreinheitgruppe.semester
|
||||||
|
AND tbl_studentlehrverband.studiensemester_kurzbz = tbl_lehreinheit.studiensemester_kurzbz
|
||||||
|
AND (tbl_lehreinheitgruppe.verband = tbl_studentlehrverband.verband OR tbl_lehreinheitgruppe.verband IS NULL OR btrim(tbl_lehreinheitgruppe.verband::text) = '' OR tbl_studentlehrverband.verband IS NULL)
|
||||||
|
AND (tbl_lehreinheitgruppe.gruppe = tbl_studentlehrverband.gruppe OR tbl_lehreinheitgruppe.gruppe IS NULL OR btrim(tbl_lehreinheitgruppe.gruppe::text) = '' OR tbl_studentlehrverband.gruppe IS NULL)
|
||||||
|
WHERE tbl_kalender.kalender_id = ?
|
||||||
|
AND tbl_studentlehrverband.student_uid NOT IN ($placeholders)
|
||||||
|
|
||||||
|
";
|
||||||
|
|
||||||
|
$result1 = $dbModel->execReadOnlyQuery($qry1, array_merge(
|
||||||
|
[$data->kalender_id],
|
||||||
|
$kollisionsfreie_user,
|
||||||
|
[$data->kalender_id],
|
||||||
|
$kollisionsfreie_user
|
||||||
|
));
|
||||||
|
|
||||||
|
if (isError($result1) || !hasData($result1)) return [];
|
||||||
|
|
||||||
|
$curUids = array_flip(array_column(getData($result1), 'uid'));
|
||||||
|
|
||||||
|
$qry2 = "
|
||||||
|
SELECT DISTINCT tbl_kalender.kalender_id, tbl_kalender.von, tbl_kalender.bis, tbl_benutzergruppe.uid
|
||||||
|
FROM lehre.tbl_kalender
|
||||||
|
JOIN lehre.tbl_kalender_lehreinheit ON tbl_kalender_lehreinheit.kalender_id = tbl_kalender.kalender_id
|
||||||
|
JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_kalender_lehreinheit.lehreinheit_id
|
||||||
|
JOIN lehre.tbl_lehreinheitgruppe ON tbl_lehreinheitgruppe.lehreinheit_id = tbl_lehreinheit.lehreinheit_id
|
||||||
|
JOIN public.tbl_gruppe
|
||||||
|
ON tbl_gruppe.studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz
|
||||||
|
AND tbl_gruppe.semester = tbl_lehreinheitgruppe.semester
|
||||||
|
AND tbl_gruppe.gruppe_kurzbz = tbl_lehreinheitgruppe.gruppe_kurzbz
|
||||||
|
JOIN public.tbl_benutzergruppe ON tbl_benutzergruppe.gruppe_kurzbz = tbl_gruppe.gruppe_kurzbz
|
||||||
|
AND tbl_benutzergruppe.studiensemester_kurzbz = tbl_lehreinheit.studiensemester_kurzbz
|
||||||
|
WHERE tbl_kalender.von < ?
|
||||||
|
AND tbl_kalender.bis > ?
|
||||||
|
AND tbl_kalender.kalender_id != ?
|
||||||
|
AND tbl_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete')
|
||||||
|
AND tbl_benutzergruppe.uid NOT IN ($placeholders)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM lehre.tbl_kalender vorgaenger
|
||||||
|
WHERE vorgaenger.vorgaenger_kalender_id = tbl_kalender.kalender_id
|
||||||
|
)
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT DISTINCT tbl_kalender.kalender_id, tbl_kalender.von, tbl_kalender.bis, tbl_studentlehrverband.student_uid AS uid
|
||||||
|
FROM lehre.tbl_kalender
|
||||||
|
JOIN lehre.tbl_kalender_lehreinheit ON tbl_kalender_lehreinheit.kalender_id = tbl_kalender.kalender_id
|
||||||
|
JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_kalender_lehreinheit.lehreinheit_id
|
||||||
|
JOIN lehre.tbl_lehreinheitgruppe ON tbl_lehreinheitgruppe.lehreinheit_id = tbl_lehreinheit.lehreinheit_id
|
||||||
|
JOIN public.tbl_studentlehrverband
|
||||||
|
ON tbl_studentlehrverband.studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz
|
||||||
|
AND tbl_studentlehrverband.semester = tbl_lehreinheitgruppe.semester
|
||||||
|
AND tbl_studentlehrverband.studiensemester_kurzbz = tbl_lehreinheit.studiensemester_kurzbz
|
||||||
|
AND (tbl_lehreinheitgruppe.verband = tbl_studentlehrverband.verband OR tbl_lehreinheitgruppe.verband IS NULL OR btrim(tbl_lehreinheitgruppe.verband::text) = '' OR tbl_studentlehrverband.verband IS NULL)
|
||||||
|
AND (tbl_lehreinheitgruppe.gruppe = tbl_studentlehrverband.gruppe OR tbl_lehreinheitgruppe.gruppe IS NULL OR btrim(tbl_lehreinheitgruppe.gruppe::text) = '' OR tbl_studentlehrverband.gruppe IS NULL)
|
||||||
|
WHERE tbl_kalender.von < ?
|
||||||
|
AND tbl_kalender.bis > ?
|
||||||
|
AND tbl_kalender.kalender_id != ?
|
||||||
|
AND tbl_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete')
|
||||||
|
AND tbl_studentlehrverband.student_uid NOT IN ($placeholders)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM lehre.tbl_kalender vorgaenger
|
||||||
|
WHERE vorgaenger.vorgaenger_kalender_id = tbl_kalender.kalender_id
|
||||||
|
)
|
||||||
|
";
|
||||||
|
|
||||||
|
$result2 = $dbModel->execReadOnlyQuery($qry2, array_merge(
|
||||||
|
[$data->bis, $data->von, $data->kalender_id],
|
||||||
|
$kollisionsfreie_user,
|
||||||
|
[$data->bis, $data->von, $data->kalender_id],
|
||||||
|
$kollisionsfreie_user
|
||||||
|
));
|
||||||
|
|
||||||
|
if (isError($result2) || !hasData($result2)) return [];
|
||||||
|
|
||||||
|
$conflicts = [];
|
||||||
|
foreach (getData($result2) as $row)
|
||||||
|
{
|
||||||
|
if (isset($curUids[$row->uid]))
|
||||||
|
{
|
||||||
|
$conflicts[] = [
|
||||||
|
'message' =>
|
||||||
|
$this->_ci->phraseslib->t('ui', 'student_kollision')
|
||||||
|
. ': ' . $row->uid
|
||||||
|
. ' (' . date('d.m.Y H:i', strtotime($row->von))
|
||||||
|
. ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')',
|
||||||
|
'errorCode' => 'student_collision'
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $conflicts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkAll($kalender_ids)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (empty($kalender_ids)) return [];
|
||||||
|
|
||||||
|
if ($this->_ci->variablelib->getVar('kollision_student') !== 'true') return [];
|
||||||
|
|
||||||
|
$dbModel = new DB_Model();
|
||||||
|
$placeholders = implode(',', array_fill(0, count($kalender_ids), '?'));
|
||||||
|
|
||||||
|
$sql = "
|
||||||
|
SELECT DISTINCT current_kalender.kalender_id, current_benutzergruppe.uid
|
||||||
|
FROM lehre.tbl_kalender current_kalender
|
||||||
|
JOIN lehre.tbl_kalender_lehreinheit current_kalender_le ON current_kalender_le.kalender_id = current_kalender.kalender_id
|
||||||
|
JOIN lehre.tbl_lehreinheit current_lehreinheit ON current_lehreinheit.lehreinheit_id = current_kalender_le.lehreinheit_id
|
||||||
|
JOIN lehre.tbl_lehreinheitgruppe current_lehreinheitgruppe ON current_lehreinheitgruppe.lehreinheit_id = current_lehreinheit.lehreinheit_id
|
||||||
|
JOIN public.tbl_gruppe current_gruppe
|
||||||
|
ON current_gruppe.studiengang_kz = current_lehreinheitgruppe.studiengang_kz
|
||||||
|
AND current_gruppe.semester = current_lehreinheitgruppe.semester
|
||||||
|
AND current_gruppe.gruppe_kurzbz = current_lehreinheitgruppe.gruppe_kurzbz
|
||||||
|
JOIN public.tbl_benutzergruppe current_benutzergruppe ON current_benutzergruppe.gruppe_kurzbz = current_gruppe.gruppe_kurzbz
|
||||||
|
AND current_benutzergruppe.studiensemester_kurzbz = current_lehreinheit.studiensemester_kurzbz
|
||||||
|
JOIN lehre.tbl_kalender other_kalender
|
||||||
|
ON other_kalender.kalender_id != current_kalender.kalender_id
|
||||||
|
AND other_kalender.von < current_kalender.bis
|
||||||
|
AND other_kalender.bis > current_kalender.von
|
||||||
|
AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete')
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM lehre.tbl_kalender vorgaenger
|
||||||
|
WHERE vorgaenger.vorgaenger_kalender_id = other_kalender.kalender_id
|
||||||
|
)
|
||||||
|
JOIN lehre.tbl_kalender_lehreinheit other_kalender_le ON other_kalender_le.kalender_id = other_kalender.kalender_id
|
||||||
|
JOIN lehre.tbl_lehreinheit other_lehreinheit ON other_lehreinheit.lehreinheit_id = other_kalender_le.lehreinheit_id
|
||||||
|
JOIN lehre.tbl_lehreinheitgruppe other_lehreinheitgruppe ON other_lehreinheitgruppe.lehreinheit_id = other_lehreinheit.lehreinheit_id
|
||||||
|
JOIN public.tbl_gruppe other_gruppe
|
||||||
|
ON other_gruppe.studiengang_kz = other_lehreinheitgruppe.studiengang_kz
|
||||||
|
AND other_gruppe.semester = other_lehreinheitgruppe.semester
|
||||||
|
AND other_gruppe.gruppe_kurzbz = other_lehreinheitgruppe.gruppe_kurzbz
|
||||||
|
JOIN public.tbl_benutzergruppe other_benutzergruppe
|
||||||
|
ON other_benutzergruppe.gruppe_kurzbz = other_gruppe.gruppe_kurzbz
|
||||||
|
AND other_benutzergruppe.uid = current_benutzergruppe.uid
|
||||||
|
AND other_benutzergruppe.studiensemester_kurzbz = other_lehreinheit.studiensemester_kurzbz
|
||||||
|
|
||||||
|
|
||||||
|
WHERE current_kalender.kalender_id IN ($placeholders)
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT DISTINCT current_kalender.kalender_id, current_studentlehrverband.student_uid AS uid
|
||||||
|
FROM lehre.tbl_kalender current_kalender
|
||||||
|
JOIN lehre.tbl_kalender_lehreinheit current_kalender_le ON current_kalender_le.kalender_id = current_kalender.kalender_id
|
||||||
|
JOIN lehre.tbl_lehreinheit current_lehreinheit ON current_lehreinheit.lehreinheit_id = current_kalender_le.lehreinheit_id
|
||||||
|
JOIN lehre.tbl_lehreinheitgruppe current_lehreinheitgruppe ON current_lehreinheitgruppe.lehreinheit_id = current_lehreinheit.lehreinheit_id
|
||||||
|
JOIN public.tbl_studentlehrverband current_studentlehrverband
|
||||||
|
ON current_studentlehrverband.studiengang_kz = current_lehreinheitgruppe.studiengang_kz
|
||||||
|
AND current_studentlehrverband.semester = current_lehreinheitgruppe.semester
|
||||||
|
AND current_studentlehrverband.studiensemester_kurzbz = current_lehreinheit.studiensemester_kurzbz
|
||||||
|
AND (current_lehreinheitgruppe.verband = current_studentlehrverband.verband OR current_lehreinheitgruppe.verband IS NULL OR btrim(current_lehreinheitgruppe.verband::text) = '' OR current_studentlehrverband.verband IS NULL)
|
||||||
|
AND (current_lehreinheitgruppe.gruppe = current_studentlehrverband.gruppe OR current_lehreinheitgruppe.gruppe IS NULL OR btrim(current_lehreinheitgruppe.gruppe::text) = '' OR current_studentlehrverband.gruppe IS NULL)
|
||||||
|
|
||||||
|
JOIN lehre.tbl_kalender other_kalender
|
||||||
|
ON other_kalender.kalender_id != current_kalender.kalender_id
|
||||||
|
AND other_kalender.von < current_kalender.bis
|
||||||
|
AND other_kalender.bis > current_kalender.von
|
||||||
|
AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete')
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM lehre.tbl_kalender vorgaenger
|
||||||
|
WHERE vorgaenger.vorgaenger_kalender_id = other_kalender.kalender_id
|
||||||
|
)
|
||||||
|
JOIN lehre.tbl_kalender_lehreinheit other_kalender_le ON other_kalender_le.kalender_id = other_kalender.kalender_id
|
||||||
|
JOIN lehre.tbl_lehreinheit other_lehreinheit ON other_lehreinheit.lehreinheit_id = other_kalender_le.lehreinheit_id
|
||||||
|
JOIN lehre.tbl_lehreinheitgruppe other_lehreinheitgruppe ON other_lehreinheitgruppe.lehreinheit_id = other_lehreinheit.lehreinheit_id
|
||||||
|
JOIN public.tbl_studentlehrverband other_slv
|
||||||
|
ON other_slv.studiengang_kz = other_lehreinheitgruppe.studiengang_kz
|
||||||
|
AND other_slv.semester = other_lehreinheitgruppe.semester
|
||||||
|
AND other_slv.studiensemester_kurzbz = other_lehreinheit.studiensemester_kurzbz
|
||||||
|
AND other_slv.student_uid = current_studentlehrverband.student_uid
|
||||||
|
AND (other_lehreinheitgruppe.verband = other_slv.verband OR other_lehreinheitgruppe.verband IS NULL OR btrim(other_lehreinheitgruppe.verband::text) = '' OR other_slv.verband IS NULL)
|
||||||
|
AND (other_lehreinheitgruppe.gruppe = other_slv.gruppe OR other_lehreinheitgruppe.gruppe IS NULL OR btrim(other_lehreinheitgruppe.gruppe::text) = '' OR other_slv.gruppe IS NULL)
|
||||||
|
|
||||||
|
WHERE current_kalender.kalender_id IN ($placeholders)
|
||||||
|
";
|
||||||
|
|
||||||
|
$result = $dbModel->execReadOnlyQuery($sql, array_merge($kalender_ids, $kalender_ids));
|
||||||
|
|
||||||
|
if (isError($result) || !hasData($result)) return [];
|
||||||
|
|
||||||
|
$grouped = [];
|
||||||
|
foreach (getData($result) as $row)
|
||||||
|
{
|
||||||
|
$grouped[$row->kalender_id][] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $grouped;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,362 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class VerbandCollisionCheck implements ICollisionCheck
|
||||||
|
{
|
||||||
|
|
||||||
|
private $_ci;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->_ci =& get_instance();
|
||||||
|
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||||
|
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
|
||||||
|
$this->_ci->load->library('PhrasesLib', array('ui'));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'verband';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function check($data)
|
||||||
|
{
|
||||||
|
if (!isset($data->von, $data->bis, $data->kalender_id)) return [];
|
||||||
|
|
||||||
|
if ($this->_ci->variablelib->getVar('ignore_kollision') === 'true') return [];
|
||||||
|
|
||||||
|
$kollision_student = $this->_ci->variablelib->getVar('kollision_student') === 'false';
|
||||||
|
$kollision_reservierung = $this->_ci->variablelib->getVar('ignore_reservierung') === 'false';
|
||||||
|
|
||||||
|
if (!$kollision_student && !$kollision_reservierung) return [];
|
||||||
|
|
||||||
|
$dbModel = new DB_Model();
|
||||||
|
$collisions = [];
|
||||||
|
|
||||||
|
if ($kollision_student)
|
||||||
|
{
|
||||||
|
$union_event = "";
|
||||||
|
|
||||||
|
if ($kollision_reservierung)
|
||||||
|
{
|
||||||
|
$union_event = "
|
||||||
|
UNION
|
||||||
|
SELECT tbl_kalender_event_teilnehmer.studiengang_kz, tbl_kalender_event_teilnehmer.semester, tbl_kalender_event_teilnehmer.verband, tbl_kalender_event_teilnehmer.gruppe, tbl_kalender_event_teilnehmer.gruppe_kurzbz, tbl_kalender_event_teilnehmer.kalender_id
|
||||||
|
FROM lehre.tbl_kalender_event_teilnehmer
|
||||||
|
WHERE tbl_kalender_event_teilnehmer.rolle_kurzbz = 'teilnehmer'
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql_gruppen = "
|
||||||
|
SELECT
|
||||||
|
other_kalender.von,
|
||||||
|
other_kalender.bis,
|
||||||
|
COALESCE(
|
||||||
|
other_lehreinheitguppe.gruppe_kurzbz,
|
||||||
|
UPPER(stg.typ::text || stg.kurzbz::text) || '-' || other_lehreinheitguppe.semester ||
|
||||||
|
COALESCE(other_lehreinheitguppe.verband::text, '') ||
|
||||||
|
COALESCE(other_lehreinheitguppe.gruppe::text, '')
|
||||||
|
) AS gruppenname
|
||||||
|
FROM lehre.tbl_kalender current_kalender
|
||||||
|
|
||||||
|
JOIN (
|
||||||
|
SELECT tbl_lehreinheitgruppe.studiengang_kz, tbl_lehreinheitgruppe.semester, tbl_lehreinheitgruppe.verband, tbl_lehreinheitgruppe.gruppe,
|
||||||
|
tbl_lehreinheitgruppe.gruppe_kurzbz, tbl_kalender_lehreinheit.kalender_id
|
||||||
|
FROM lehre.tbl_kalender_lehreinheit
|
||||||
|
JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_kalender_lehreinheit.lehreinheit_id
|
||||||
|
JOIN lehre.tbl_lehreinheitgruppe ON tbl_lehreinheitgruppe.lehreinheit_id = tbl_lehreinheit.lehreinheit_id
|
||||||
|
". $union_event ."
|
||||||
|
) current_lehreinheitguppe ON current_lehreinheitguppe.kalender_id = current_kalender.kalender_id
|
||||||
|
|
||||||
|
LEFT JOIN public.tbl_gruppe current_gruppe
|
||||||
|
ON current_gruppe.gruppe_kurzbz = current_lehreinheitguppe.gruppe_kurzbz
|
||||||
|
|
||||||
|
JOIN lehre.tbl_kalender other_kalender
|
||||||
|
ON other_kalender.kalender_id != current_kalender.kalender_id
|
||||||
|
AND other_kalender.von < ?
|
||||||
|
AND other_kalender.bis > ?
|
||||||
|
|
||||||
|
JOIN (
|
||||||
|
SELECT tbl_lehreinheitgruppe.studiengang_kz, tbl_lehreinheitgruppe.semester, tbl_lehreinheitgruppe.verband, tbl_lehreinheitgruppe.gruppe,
|
||||||
|
tbl_lehreinheitgruppe.gruppe_kurzbz, tbl_kalender_lehreinheit.kalender_id
|
||||||
|
FROM lehre.tbl_kalender_lehreinheit
|
||||||
|
JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_kalender_lehreinheit.lehreinheit_id
|
||||||
|
JOIN lehre.tbl_lehreinheitgruppe ON tbl_lehreinheitgruppe.lehreinheit_id = tbl_lehreinheit.lehreinheit_id
|
||||||
|
". $union_event ."
|
||||||
|
) other_lehreinheitguppe ON other_lehreinheitguppe.kalender_id = other_kalender.kalender_id
|
||||||
|
|
||||||
|
LEFT JOIN public.tbl_gruppe other_gruppe
|
||||||
|
ON other_gruppe.gruppe_kurzbz = other_lehreinheitguppe.gruppe_kurzbz
|
||||||
|
|
||||||
|
LEFT JOIN public.tbl_studiengang stg
|
||||||
|
ON stg.studiengang_kz = other_lehreinheitguppe.studiengang_kz
|
||||||
|
|
||||||
|
WHERE current_kalender.kalender_id = ?
|
||||||
|
AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete')
|
||||||
|
AND current_lehreinheitguppe.studiengang_kz = other_lehreinheitguppe.studiengang_kz
|
||||||
|
AND current_lehreinheitguppe.semester = other_lehreinheitguppe.semester
|
||||||
|
AND (
|
||||||
|
(
|
||||||
|
current_lehreinheitguppe.gruppe_kurzbz IS NULL
|
||||||
|
AND other_lehreinheitguppe.gruppe_kurzbz IS NULL
|
||||||
|
AND (
|
||||||
|
current_lehreinheitguppe.verband IS NULL
|
||||||
|
OR (
|
||||||
|
current_lehreinheitguppe.verband = other_lehreinheitguppe.verband
|
||||||
|
AND (current_lehreinheitguppe.gruppe IS NULL OR other_lehreinheitguppe.gruppe IS NULL OR current_lehreinheitguppe.gruppe = other_lehreinheitguppe.gruppe)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
OR
|
||||||
|
(
|
||||||
|
current_lehreinheitguppe.gruppe_kurzbz IS NOT NULL
|
||||||
|
AND other_lehreinheitguppe.gruppe_kurzbz IS NOT NULL
|
||||||
|
AND current_gruppe.direktinskription IS NOT TRUE
|
||||||
|
AND other_gruppe.direktinskription IS NOT TRUE
|
||||||
|
)
|
||||||
|
OR
|
||||||
|
(
|
||||||
|
(
|
||||||
|
current_lehreinheitguppe.gruppe_kurzbz IS NULL
|
||||||
|
AND other_lehreinheitguppe.gruppe_kurzbz IS NOT NULL
|
||||||
|
AND other_gruppe.direktinskription IS NOT TRUE
|
||||||
|
)
|
||||||
|
OR
|
||||||
|
(
|
||||||
|
current_lehreinheitguppe.gruppe_kurzbz IS NOT NULL
|
||||||
|
AND other_lehreinheitguppe.gruppe_kurzbz IS NULL
|
||||||
|
AND current_gruppe.direktinskription IS NOT TRUE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AND other_kalender.kalender_id NOT IN (
|
||||||
|
SELECT vorgaenger_kalender_id
|
||||||
|
FROM lehre.tbl_kalender
|
||||||
|
WHERE vorgaenger_kalender_id IS NOT NULL
|
||||||
|
)
|
||||||
|
";
|
||||||
|
|
||||||
|
$result = $dbModel->execReadOnlyQuery($sql_gruppen, [
|
||||||
|
$data->bis,
|
||||||
|
$data->von,
|
||||||
|
$data->kalender_id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!isError($result) && hasData($result))
|
||||||
|
{
|
||||||
|
foreach (getData($result) as $row)
|
||||||
|
{
|
||||||
|
$collisions[] = [
|
||||||
|
'message' => $this->_ci->phraseslib->t('ui', 'verband_kollision') . ': ' . $row->gruppenname . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')',
|
||||||
|
'errorCode' => 'verband_collision',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($kollision_reservierung && !$kollision_student)
|
||||||
|
{
|
||||||
|
$sql_reservierung = "
|
||||||
|
SELECT
|
||||||
|
other_kalender.von,
|
||||||
|
other_kalender.bis,
|
||||||
|
COALESCE(
|
||||||
|
other_event_teilnehmer.gruppe_kurzbz,
|
||||||
|
UPPER(stg.typ::text || stg.kurzbz::text) || '-' || other_event_teilnehmer.semester ||
|
||||||
|
COALESCE(other_event_teilnehmer.verband::text, '') ||
|
||||||
|
COALESCE(other_event_teilnehmer.gruppe::text, '')
|
||||||
|
) AS gruppenname
|
||||||
|
FROM lehre.tbl_kalender_event_teilnehmer current_event_teilnehmer
|
||||||
|
LEFT JOIN public.tbl_gruppe current_gruppe
|
||||||
|
ON current_gruppe.gruppe_kurzbz = current_event_teilnehmer.gruppe_kurzbz
|
||||||
|
|
||||||
|
JOIN lehre.tbl_kalender other_kalender
|
||||||
|
ON other_kalender.kalender_id != ?
|
||||||
|
AND other_kalender.von < ?
|
||||||
|
AND other_kalender.bis > ?
|
||||||
|
|
||||||
|
JOIN lehre.tbl_kalender_event_teilnehmer other_event_teilnehmer
|
||||||
|
ON other_event_teilnehmer.kalender_id = other_kalender.kalender_id
|
||||||
|
AND other_event_teilnehmer.rolle_kurzbz = 'teilnehmer'
|
||||||
|
|
||||||
|
LEFT JOIN public.tbl_gruppe other_gruppe
|
||||||
|
ON other_gruppe.gruppe_kurzbz = other_event_teilnehmer.gruppe_kurzbz
|
||||||
|
|
||||||
|
LEFT JOIN public.tbl_studiengang stg
|
||||||
|
ON stg.studiengang_kz = other_event_teilnehmer.studiengang_kz
|
||||||
|
|
||||||
|
WHERE current_event_teilnehmer.kalender_id = ?
|
||||||
|
AND current_event_teilnehmer.rolle_kurzbz = 'teilnehmer'
|
||||||
|
AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete')
|
||||||
|
AND current_event_teilnehmer.studiengang_kz = other_event_teilnehmer.studiengang_kz
|
||||||
|
AND current_event_teilnehmer.semester = other_event_teilnehmer.semester
|
||||||
|
AND (
|
||||||
|
(
|
||||||
|
current_event_teilnehmer.gruppe_kurzbz IS NULL
|
||||||
|
AND other_event_teilnehmer.gruppe_kurzbz IS NULL
|
||||||
|
AND (
|
||||||
|
current_event_teilnehmer.verband IS NULL
|
||||||
|
OR (
|
||||||
|
current_event_teilnehmer.verband = other_event_teilnehmer.verband
|
||||||
|
AND (current_event_teilnehmer.gruppe IS NULL OR other_event_teilnehmer.gruppe IS NULL OR current_event_teilnehmer.gruppe = other_event_teilnehmer.gruppe)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
OR
|
||||||
|
(
|
||||||
|
current_event_teilnehmer.gruppe_kurzbz IS NOT NULL
|
||||||
|
AND other_event_teilnehmer.gruppe_kurzbz IS NOT NULL
|
||||||
|
AND current_gruppe.direktinskription IS NOT TRUE
|
||||||
|
AND other_gruppe.direktinskription IS NOT TRUE
|
||||||
|
)
|
||||||
|
OR
|
||||||
|
(
|
||||||
|
(
|
||||||
|
current_event_teilnehmer.gruppe_kurzbz IS NULL
|
||||||
|
AND other_event_teilnehmer.gruppe_kurzbz IS NOT NULL
|
||||||
|
AND other_gruppe.direktinskription IS NOT TRUE
|
||||||
|
)
|
||||||
|
OR
|
||||||
|
(
|
||||||
|
current_event_teilnehmer.gruppe_kurzbz IS NOT NULL
|
||||||
|
AND other_event_teilnehmer.gruppe_kurzbz IS NULL
|
||||||
|
AND current_gruppe.direktinskription IS NOT TRUE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AND other_kalender.kalender_id NOT IN (
|
||||||
|
SELECT vorgaenger_kalender_id
|
||||||
|
FROM lehre.tbl_kalender
|
||||||
|
WHERE vorgaenger_kalender_id IS NOT NULL
|
||||||
|
)
|
||||||
|
";
|
||||||
|
|
||||||
|
$result = $dbModel->execReadOnlyQuery($sql_reservierung, [
|
||||||
|
$data->kalender_id,
|
||||||
|
$data->bis,
|
||||||
|
$data->von,
|
||||||
|
$data->kalender_id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!isError($result) && hasData($result))
|
||||||
|
{
|
||||||
|
foreach (getData($result) as $row)
|
||||||
|
{
|
||||||
|
$collisions[] = [
|
||||||
|
'errorCode' => 'reservation_collision',
|
||||||
|
'message' => $this->_ci->phraseslib->t('ui', 'reservierung_kollision') . ': ' . $row->gruppenname . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collisions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkAll($kalender_ids)
|
||||||
|
{
|
||||||
|
if (empty($kalender_ids)) return [];
|
||||||
|
|
||||||
|
$dbModel = new DB_Model();
|
||||||
|
|
||||||
|
$placeholders = implode(',', array_fill(0, count($kalender_ids), '?'));
|
||||||
|
|
||||||
|
$sql = "
|
||||||
|
SELECT DISTINCT ON (current_kalender.kalender_id) current_kalender.kalender_id
|
||||||
|
FROM lehre.tbl_kalender current_kalender
|
||||||
|
|
||||||
|
JOIN (
|
||||||
|
SELECT tbl_lehreinheitgruppe.studiengang_kz, tbl_lehreinheitgruppe.semester, tbl_lehreinheitgruppe.verband, tbl_lehreinheitgruppe.gruppe,
|
||||||
|
tbl_lehreinheitgruppe.gruppe_kurzbz, tbl_kalender_lehreinheit.kalender_id
|
||||||
|
FROM lehre.tbl_kalender_lehreinheit
|
||||||
|
JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_kalender_lehreinheit.lehreinheit_id
|
||||||
|
JOIN lehre.tbl_lehreinheitgruppe ON tbl_lehreinheitgruppe.lehreinheit_id = tbl_lehreinheit.lehreinheit_id
|
||||||
|
UNION
|
||||||
|
SELECT tbl_kalender_event_teilnehmer.studiengang_kz, tbl_kalender_event_teilnehmer.semester, tbl_kalender_event_teilnehmer.verband, tbl_kalender_event_teilnehmer.gruppe,
|
||||||
|
tbl_kalender_event_teilnehmer.gruppe_kurzbz, tbl_kalender_event_teilnehmer.kalender_id
|
||||||
|
FROM lehre.tbl_kalender_event_teilnehmer
|
||||||
|
) current_lehreinheitguppe ON current_lehreinheitguppe.kalender_id = current_kalender.kalender_id
|
||||||
|
|
||||||
|
LEFT JOIN public.tbl_gruppe current_gruppe
|
||||||
|
ON current_gruppe.gruppe_kurzbz = current_lehreinheitguppe.gruppe_kurzbz
|
||||||
|
|
||||||
|
JOIN lehre.tbl_kalender other_kalender
|
||||||
|
ON other_kalender.kalender_id != current_kalender.kalender_id
|
||||||
|
AND other_kalender.von < current_kalender.bis
|
||||||
|
AND other_kalender.bis > current_kalender.von
|
||||||
|
|
||||||
|
JOIN (
|
||||||
|
SELECT tbl_lehreinheitgruppe.studiengang_kz, tbl_lehreinheitgruppe.semester, tbl_lehreinheitgruppe.verband, tbl_lehreinheitgruppe.gruppe,
|
||||||
|
tbl_lehreinheitgruppe.gruppe_kurzbz, tbl_kalender_lehreinheit.kalender_id
|
||||||
|
FROM lehre.tbl_kalender_lehreinheit
|
||||||
|
JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_kalender_lehreinheit.lehreinheit_id
|
||||||
|
JOIN lehre.tbl_lehreinheitgruppe ON tbl_lehreinheitgruppe.lehreinheit_id = tbl_lehreinheit.lehreinheit_id
|
||||||
|
UNION
|
||||||
|
SELECT tbl_kalender_event_teilnehmer.studiengang_kz, tbl_kalender_event_teilnehmer.semester, tbl_kalender_event_teilnehmer.verband, tbl_kalender_event_teilnehmer.gruppe,
|
||||||
|
tbl_kalender_event_teilnehmer.gruppe_kurzbz, tbl_kalender_event_teilnehmer.kalender_id
|
||||||
|
FROM lehre.tbl_kalender_event_teilnehmer
|
||||||
|
) other_lehreinheitguppe ON other_lehreinheitguppe.kalender_id = other_kalender.kalender_id
|
||||||
|
|
||||||
|
LEFT JOIN public.tbl_gruppe other_gruppe
|
||||||
|
ON other_gruppe.gruppe_kurzbz = other_lehreinheitguppe.gruppe_kurzbz
|
||||||
|
|
||||||
|
WHERE current_kalender.kalender_id IN ({$placeholders})
|
||||||
|
AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete')
|
||||||
|
AND current_lehreinheitguppe.studiengang_kz = other_lehreinheitguppe.studiengang_kz
|
||||||
|
AND current_lehreinheitguppe.semester = other_lehreinheitguppe.semester
|
||||||
|
AND (
|
||||||
|
(
|
||||||
|
current_lehreinheitguppe.gruppe_kurzbz IS NULL
|
||||||
|
AND other_lehreinheitguppe.gruppe_kurzbz IS NULL
|
||||||
|
AND (
|
||||||
|
current_lehreinheitguppe.verband IS NULL
|
||||||
|
OR (
|
||||||
|
current_lehreinheitguppe.verband = other_lehreinheitguppe.verband
|
||||||
|
AND (current_lehreinheitguppe.gruppe IS NULL OR other_lehreinheitguppe.gruppe IS NULL OR current_lehreinheitguppe.gruppe = other_lehreinheitguppe.gruppe)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
OR
|
||||||
|
(
|
||||||
|
current_lehreinheitguppe.gruppe_kurzbz IS NOT NULL
|
||||||
|
AND other_lehreinheitguppe.gruppe_kurzbz IS NOT NULL
|
||||||
|
AND current_gruppe.direktinskription IS NOT TRUE
|
||||||
|
AND other_gruppe.direktinskription IS NOT TRUE
|
||||||
|
)
|
||||||
|
OR
|
||||||
|
(
|
||||||
|
(
|
||||||
|
current_lehreinheitguppe.gruppe_kurzbz IS NULL
|
||||||
|
AND other_lehreinheitguppe.gruppe_kurzbz IS NOT NULL
|
||||||
|
AND other_gruppe.direktinskription IS NOT TRUE
|
||||||
|
)
|
||||||
|
OR
|
||||||
|
(
|
||||||
|
current_lehreinheitguppe.gruppe_kurzbz IS NOT NULL
|
||||||
|
AND other_lehreinheitguppe.gruppe_kurzbz IS NULL
|
||||||
|
AND current_gruppe.direktinskription IS NOT TRUE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AND other_kalender.kalender_id NOT IN (
|
||||||
|
SELECT vorgaenger_kalender_id
|
||||||
|
FROM lehre.tbl_kalender
|
||||||
|
WHERE vorgaenger_kalender_id IS NOT NULL
|
||||||
|
)
|
||||||
|
|
||||||
|
";
|
||||||
|
|
||||||
|
$result = $dbModel->execReadOnlyQuery($sql, $kalender_ids);
|
||||||
|
|
||||||
|
if (isError($result) || !hasData($result)) return [];
|
||||||
|
|
||||||
|
$grouped = [];
|
||||||
|
foreach (getData($result) as $row)
|
||||||
|
{
|
||||||
|
$grouped[$row->kalender_id][] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $grouped;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -395,6 +395,77 @@ EOSQL;
|
|||||||
return $this->execReadOnlyQuery($qry, $params);
|
return $this->execReadOnlyQuery($qry, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getOffeneStunden($lehreinheit_ids)
|
||||||
|
{
|
||||||
|
if (!is_array($lehreinheit_ids))
|
||||||
|
$lehreinheit_ids = [$lehreinheit_ids];
|
||||||
|
|
||||||
|
if (empty($lehreinheit_ids))
|
||||||
|
return success([]);
|
||||||
|
|
||||||
|
$db = new DB_Model();
|
||||||
|
$placeholders = implode(',', array_fill(0, count($lehreinheit_ids), '?'));
|
||||||
|
|
||||||
|
$qry = "WITH eventdauer AS (
|
||||||
|
SELECT
|
||||||
|
tbl_kalender_lehreinheit.lehreinheit_id,
|
||||||
|
tbl_kalender.von,
|
||||||
|
tbl_kalender.bis,
|
||||||
|
tbl_kalender.kalender_id
|
||||||
|
FROM lehre.tbl_lehreinheit
|
||||||
|
JOIN lehre.tbl_kalender_lehreinheit USING(lehreinheit_id)
|
||||||
|
JOIN lehre.tbl_kalender USING(kalender_id)
|
||||||
|
WHERE tbl_lehreinheit.lehreinheit_id IN ($placeholders)
|
||||||
|
GROUP BY tbl_kalender_lehreinheit.lehreinheit_id, tbl_kalender.von, tbl_kalender.bis, tbl_kalender.kalender_id
|
||||||
|
),
|
||||||
|
raster AS (
|
||||||
|
SELECT
|
||||||
|
eventdauer.lehreinheit_id,
|
||||||
|
eventdauer.kalender_id,
|
||||||
|
eventdauer.von,
|
||||||
|
eventdauer.bis,
|
||||||
|
vonstunde.stunde AS vonstunde_nr,
|
||||||
|
bisstunde.stunde AS bisstunde_nr
|
||||||
|
FROM eventdauer
|
||||||
|
LEFT JOIN lehre.tbl_stunde vonstunde ON eventdauer.von::time = vonstunde.beginn
|
||||||
|
LEFT JOIN lehre.tbl_stunde bisstunde ON eventdauer.bis::time = bisstunde.ende
|
||||||
|
),
|
||||||
|
unterrichtsdauer AS (
|
||||||
|
SELECT
|
||||||
|
raster.lehreinheit_id,
|
||||||
|
raster.kalender_id,
|
||||||
|
sum(EXTRACT(EPOCH FROM (LEAST(stunde.ende, raster.bis::time) - GREATEST(stunde.beginn, raster.von::time))) / 60) AS minuten
|
||||||
|
FROM raster JOIN lehre.tbl_stunde stunde ON stunde.beginn < raster.bis::time AND stunde.ende > raster.von::time
|
||||||
|
WHERE raster.vonstunde_nr IS NULL OR raster.bisstunde_nr IS NULL
|
||||||
|
GROUP BY raster.lehreinheit_id, raster.kalender_id
|
||||||
|
),
|
||||||
|
verplantestunden AS (
|
||||||
|
SELECT raster.lehreinheit_id, raster.kalender_id,
|
||||||
|
CASE
|
||||||
|
WHEN raster.vonstunde_nr IS NOT NULL AND raster.bisstunde_nr IS NOT NULL
|
||||||
|
THEN (raster.bisstunde_nr - raster.vonstunde_nr + 1)
|
||||||
|
ELSE coalesce(unterrichtsdauer.minuten, 0) / 45
|
||||||
|
END AS verplantestunde
|
||||||
|
FROM raster LEFT JOIN unterrichtsdauer ON unterrichtsdauer.lehreinheit_id = raster.lehreinheit_id AND unterrichtsdauer.kalender_id = raster.kalender_id
|
||||||
|
),
|
||||||
|
verplant_gesamt AS (
|
||||||
|
SELECT lehreinheit_id, SUM(verplantestunde) AS verplant
|
||||||
|
FROM verplantestunden
|
||||||
|
GROUP BY lehreinheit_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
lema.lehreinheit_id,
|
||||||
|
lema.mitarbeiter_uid,
|
||||||
|
lema.planstunden,
|
||||||
|
ROUND(coalesce(verplant_gesamt.verplant, 0), 2) AS verplant,
|
||||||
|
ROUND((lema.planstunden - coalesce(verplant_gesamt.verplant, 0)), 2) AS offenestunden
|
||||||
|
FROM lehre.tbl_lehreinheitmitarbeiter lema
|
||||||
|
LEFT JOIN verplant_gesamt ON verplant_gesamt.lehreinheit_id = lema.lehreinheit_id
|
||||||
|
WHERE lema.lehreinheit_id IN ($placeholders)";
|
||||||
|
|
||||||
|
return $db->execReadOnlyQuery($qry, array_merge($lehreinheit_ids, $lehreinheit_ids));
|
||||||
|
}
|
||||||
|
|
||||||
private function getLVTmp($stg_kz = null)
|
private function getLVTmp($stg_kz = null)
|
||||||
{
|
{
|
||||||
$qry = "SELECT DISTINCT ON(lehrveranstaltung_id) *,
|
$qry = "SELECT DISTINCT ON(lehrveranstaltung_id) *,
|
||||||
|
|||||||
@@ -11,4 +11,26 @@ class Gruppe_model extends DB_Model
|
|||||||
$this->dbTable = 'public.tbl_gruppe';
|
$this->dbTable = 'public.tbl_gruppe';
|
||||||
$this->pk = 'gruppe_kurzbz';
|
$this->pk = 'gruppe_kurzbz';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function search($query_words)
|
||||||
|
{
|
||||||
|
$this->addSelect('gruppe_kurzbz,
|
||||||
|
studiengang_kz,
|
||||||
|
semester,
|
||||||
|
bezeichnung,
|
||||||
|
gid,
|
||||||
|
\'false\' as lehrverband');
|
||||||
|
$this->db->where(array('sichtbar' => true, 'aktiv' => true, 'lehre' => true, 'direktinskription' => false, 'semester IS NOT NULL' => null));
|
||||||
|
$this->db->group_start();
|
||||||
|
foreach ($query_words as $word)
|
||||||
|
{
|
||||||
|
$this->db->group_start();
|
||||||
|
$this->db->where('gruppe_kurzbz ILIKE', "%" . $word . "%");
|
||||||
|
$this->db->or_where('bezeichnung ILIKE', "%" . $word . "%");
|
||||||
|
$this->db->group_end();
|
||||||
|
}
|
||||||
|
$this->db->group_end();
|
||||||
|
|
||||||
|
return $this->load();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,4 +41,29 @@ class Lehrverband_model extends DB_Model
|
|||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function search($query_words)
|
||||||
|
{
|
||||||
|
$this->addSelect('CONCAT(UPPER(CONCAT(typ, kurzbz)), \'\', semester, verband, COALESCE(gruppe,\'\')) as gruppe_kurzbz,
|
||||||
|
studiengang_kz,
|
||||||
|
semester,
|
||||||
|
tbl_lehrverband.bezeichnung,
|
||||||
|
gid,
|
||||||
|
\'true\' as lehrverband');
|
||||||
|
$this->addJoin('public.tbl_studiengang', 'studiengang_kz');
|
||||||
|
$this->addOrder('verband');
|
||||||
|
$this->addOrder('gruppe');
|
||||||
|
$this->db->where(array('tbl_lehrverband.aktiv' => true));
|
||||||
|
|
||||||
|
$this->db->group_start();
|
||||||
|
foreach ($query_words as $word)
|
||||||
|
{
|
||||||
|
$this->db->group_start();
|
||||||
|
$this->db->where('CONCAT(CONCAT(typ, kurzbz), \'\', semester, verband, COALESCE(gruppe,\'\')) ILIKE', "%" . $word . "%");
|
||||||
|
$this->db->or_where('tbl_lehrverband.bezeichnung ILIKE', "%" . $word . "%");
|
||||||
|
$this->db->group_end();
|
||||||
|
}
|
||||||
|
$this->db->group_end();
|
||||||
|
return $this->load();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
class Kalender_Event_Rolle_model extends DB_Model
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->dbTable = 'lehre.tbl_kalender_event_rolle';
|
||||||
|
$this->pk = array('rolle_kurzbz');
|
||||||
|
$this->hasSequence = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
class
|
||||||
|
Kalender_Event_Teilnehmer_model extends DB_Model
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->dbTable = 'lehre.tbl_kalender_event_teilnehmer';
|
||||||
|
$this->pk = array('kalender_event_teilnehmer_id');
|
||||||
|
$this->hasSequence = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
class Kalender_Event_model extends DB_Model
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->dbTable = 'lehre.tbl_kalender_event';
|
||||||
|
$this->pk = array('kalender_id');
|
||||||
|
$this->hasSequence = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
class Kalender_Lehreinheit_model extends DB_Model
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->dbTable = 'lehre.tbl_kalender_lehreinheit';
|
||||||
|
$this->pk = array('kalender_id','lehreinheit_id');
|
||||||
|
$this->hasSequence = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
class Kalender_Ort_model extends DB_Model
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->dbTable = 'lehre.tbl_kalender_ort';
|
||||||
|
$this->pk = 'kalender_ort_id';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
class Kalender_model extends DB_Model
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->dbTable = 'lehre.tbl_kalender';
|
||||||
|
$this->pk = 'kalender_id';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
class Kalenderstatus_model extends DB_Model
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->dbTable = 'lehre.tbl_kalender_status';
|
||||||
|
$this->pk = 'status_kurzbz';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
class Kalendersyncstatus_model extends DB_Model
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->dbTable = 'lehre.tbl_kalender_syncstatus';
|
||||||
|
$this->pk = 'kalender_syncstatus_id';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
class Reservierung_Kalender_model extends DB_Model
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->dbTable = 'sync.tbl_reservierung_kalender';
|
||||||
|
$this->pk = 'reservierung_kalender_id';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
class Stundenplandev_Kalender_model extends DB_Model
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->dbTable = 'sync.tbl_stundenplandev_kalender';
|
||||||
|
$this->pk = 'stundenplandev_kalender_id';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
$includesArray = array(
|
||||||
|
'title' => 'Tempus',
|
||||||
|
'axios027' => true,
|
||||||
|
'bootstrap5' => true,
|
||||||
|
'fontawesome6' => true,
|
||||||
|
'vue3' => true,
|
||||||
|
'primevue3' => true,
|
||||||
|
'tabulator5' => true,
|
||||||
|
'filtercomponent' => true,
|
||||||
|
'navigationcomponent' => true,
|
||||||
|
'vuedatepicker11' => true,
|
||||||
|
'phrases' => array(
|
||||||
|
'global',
|
||||||
|
'ui',
|
||||||
|
'lehre'
|
||||||
|
),
|
||||||
|
'customCSSs' => [
|
||||||
|
'public/css/components/vue-datepicker.css',
|
||||||
|
'public/css/components/primevue.css',
|
||||||
|
'public/css/components/calendar.css',
|
||||||
|
'public/css/Tempus.css',
|
||||||
|
'public/css/Studentenverwaltung.css',
|
||||||
|
'public/css/components/function.css'
|
||||||
|
],
|
||||||
|
'customJSs' => [
|
||||||
|
#'vendor/npm-asset/primevue/tree/tree.min.js',
|
||||||
|
#'vendor/npm-asset/primevue/toast/toast.min.js'
|
||||||
|
'vendor/moment/luxonjs/luxon.min.js'
|
||||||
|
],
|
||||||
|
'customJSModules' => [
|
||||||
|
'public/js/apps/Tempus.js'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->load->view('templates/FHC-Header', $includesArray);
|
||||||
|
?>
|
||||||
|
<div id="main">
|
||||||
|
<router-view
|
||||||
|
default-semester="<?= $variables['semester_aktuell']; ?>"
|
||||||
|
active-addons="<?= defined('ACTIVE_ADDONS') ? ACTIVE_ADDONS : ''; ?>"
|
||||||
|
tempus-root="<?= site_url('Tempus'); ?>"
|
||||||
|
cis-root="<?= CIS_ROOT; ?>"
|
||||||
|
avatar-url="<?= site_url('Cis/Pub/bild/person/' . getAuthPersonId()); ?>"
|
||||||
|
logout-url="<?= site_url('Cis/Auth/logout'); ?>"
|
||||||
|
:permissions="<?= htmlspecialchars(json_encode($permissions)); ?>"
|
||||||
|
:config="<?= htmlspecialchars(json_encode($variables)); ?>"
|
||||||
|
>
|
||||||
|
</router-view>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php $this->load->view('templates/FHC-Footer', $includesArray); ?>
|
||||||
@@ -97,3 +97,15 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.weekPageContainer .calendar-event-collisions
|
||||||
|
{
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
135deg,
|
||||||
|
transparent,
|
||||||
|
transparent 16px,
|
||||||
|
rgba(0, 0, 0, 0.3) 16px,
|
||||||
|
rgba(0, 0, 0, 0.3) 32px
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
@import './Fhc.css';
|
||||||
|
@import './components/searchbar/searchbar.css';
|
||||||
|
@import './components/verticalsplit.css';
|
||||||
|
@import './components/FilterComponent.css';
|
||||||
|
@import './components/Tabs.css';
|
||||||
|
@import './components/Notiz.css';
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: .875em;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
/*display: flex;*/
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.heightfull {
|
||||||
|
height: 95%;
|
||||||
|
}
|
||||||
|
.navbar-dark .navbar-brand:focus {
|
||||||
|
box-shadow: 0 0 0 .25rem rgba(13,110,253,.25);
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
#main {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.tempus {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.searchbar {
|
||||||
|
margin-right: 0!important;
|
||||||
|
}
|
||||||
|
.searchbar > .input-group {
|
||||||
|
margin-right: 0!important;
|
||||||
|
}
|
||||||
|
.searchbar > .input-group > * {
|
||||||
|
border-radius: 0!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebarMenu {
|
||||||
|
width: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabulator-row.disabled.tabulator-row-odd .tabulator-cell {
|
||||||
|
color: var(--gray-400);
|
||||||
|
}
|
||||||
|
.tabulator-row.disabled.tabulator-row-even .tabulator-cell {
|
||||||
|
color: var(--gray-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dropdown Toolbar Interessent, submenu */
|
||||||
|
.dropend .dropdown-toggle.d-flex::after {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
#sidebarMenu {
|
||||||
|
visibility: visible!important;
|
||||||
|
transform: none;
|
||||||
|
position: inherit;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.toast-success {
|
||||||
|
color: #0f5132;
|
||||||
|
background-color: #d1e7dd!important;
|
||||||
|
border-color: #badbcc!important;
|
||||||
|
}
|
||||||
|
.toast.toast-danger {
|
||||||
|
color: #842029;
|
||||||
|
background-color: #f8d7da!important;
|
||||||
|
border-color: #f5c2c7!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-filter .fa-filter {
|
||||||
|
color: var(--bs-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#parkingslot {
|
||||||
|
border: 1px dashed;
|
||||||
|
min-height: 5vh;
|
||||||
|
max-height: 15vh;
|
||||||
|
color: #AAAAAA;
|
||||||
|
text-align: center;
|
||||||
|
font-size: large;
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-picker {
|
||||||
|
border: 1px dashed #AAAAAA;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 1 0;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-picker-row {
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
border: 1px solid var(--bs-border-color);
|
||||||
|
background-color: var(--event-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.parkingevent {
|
||||||
|
border: 1px dashed !important;
|
||||||
|
border-color: #AAAAAA;
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root{
|
||||||
|
--fhc-calendar-pane-height: calc(100vh - 120px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eckerltest {
|
||||||
|
box-shadow: 3px 3px 3px #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verband-selection {
|
||||||
|
border: 1px dashed #AAAAAA;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
max-height: 450px;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lecture-selection {
|
||||||
|
border: 1px dashed #AAAAAA;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
max-height: 20vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-selection {
|
||||||
|
border: 1px dashed #AAAAAA;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
padding: 0.5rem 0 0.5rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-link.text-danger {
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-link.text-danger:hover {
|
||||||
|
color: #dc3545;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.bg-lecturer-wish {
|
||||||
|
opacity: .15;
|
||||||
|
}
|
||||||
|
.bg-lecturer-block {
|
||||||
|
background: rgba(255, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.wish-w--2 {
|
||||||
|
background-color: #FF2200;
|
||||||
|
}
|
||||||
|
.wish-w--1 {
|
||||||
|
background-color: #FF9922;
|
||||||
|
}
|
||||||
|
.wish-w-1 {
|
||||||
|
background-color: #CCFFCC;
|
||||||
|
}
|
||||||
|
.wish-w-2 {
|
||||||
|
background-color: #48FA66;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2,6 +2,10 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: var(--fhc-calendar-fontsize-event, .875rem);
|
font-size: var(--fhc-calendar-fontsize-event, .875rem);
|
||||||
}
|
}
|
||||||
|
.event--parked {
|
||||||
|
opacity: 0.45 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.fhc-calendar-mode-day .fhc-calendar-base-grid-line,
|
.fhc-calendar-mode-day .fhc-calendar-base-grid-line,
|
||||||
.fhc-calendar-mode-week .fhc-calendar-base-grid-line {
|
.fhc-calendar-mode-week .fhc-calendar-base-grid-line {
|
||||||
padding: 0 var(--fhc-calendar-gap-events, var(--fhc-calendar-gap, 1px));
|
padding: 0 var(--fhc-calendar-gap-events, var(--fhc-calendar-gap, 1px));
|
||||||
@@ -69,3 +73,36 @@
|
|||||||
.fhc-calendar-base .event > *:hover {
|
.fhc-calendar-base .event > *:hover {
|
||||||
filter: brightness(120%);
|
filter: brightness(120%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fhc-resize-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 15px;
|
||||||
|
flex: 0 0 12px;
|
||||||
|
cursor: ns-resize;
|
||||||
|
position: relative;
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
background-color: transparent !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fhc-resize-bar--top {
|
||||||
|
margin-bottom: -16px;
|
||||||
|
}
|
||||||
|
.fhc-resize-bar--bottom {
|
||||||
|
margin-top: -16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fhc-calendar-base-grid-line-event:hover > .fhc-resize-bar {
|
||||||
|
visibility: visible;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
.fhc-calendar-base-grid-line-event.event:hover {
|
||||||
|
z-index: 50;
|
||||||
|
}
|
||||||
|
.fhc-resize-bar {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,4 +9,12 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
loadTempusRenderers() {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/frontend/v1/RendererLoader/GetTempusRenderers',
|
||||||
|
params: {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2025 fhcomplete.org
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
get() {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: 'api/frontend/v1/tempus/config/get'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getHeader() {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: 'api/frontend/v1/tempus/config/getHeader'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
set(params) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: 'api/frontend/v1/tempus/config/set',
|
||||||
|
params
|
||||||
|
};
|
||||||
|
},
|
||||||
|
updateCollision () {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: 'api/frontend/v1/tempus/config/updateCollision'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export default {
|
||||||
|
search(query) {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: 'api/frontend/v1/tempus/coursepicker/search',
|
||||||
|
params: { query }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getByStg(studiengaenge, studiensemester_kurzbz) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: 'api/frontend/v1/tempus/coursepicker/getByStg',
|
||||||
|
params: { studiengaenge, studiensemester_kurzbz }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export default {
|
||||||
|
getStudiengaenge() {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/frontend/v1/tempus/Info/getStudiengaenge',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
|
||||||
|
export default {
|
||||||
|
getPlan(filter, start_date, end_date)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/getPlan',
|
||||||
|
params: { ...filter, start_date, end_date }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getPlanLecturer(start_date, end_date)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/getPlanLecturer',
|
||||||
|
params: { start_date, end_date }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getPlanStudent(start_date, end_date)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/getPlanStudent',
|
||||||
|
params: { start_date, end_date }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
syncToLecturer(kalender_id)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/syncToLecturer',
|
||||||
|
params: { kalender_id }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
syncToStudent(kalender_id)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/syncToStudent',
|
||||||
|
params: { kalender_id }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
sync()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/sync',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getLektorZeitsperren(emp, start_date, end_date) {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/getZeitsperren',
|
||||||
|
params: { emp, start_date, end_date }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getLektorZeitwuensche(emp, start_date, end_date) {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/getZeitwuensche',
|
||||||
|
params: { emp, start_date, end_date }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getStunden() {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/getStunden',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getCalendarHours() {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/getCalendarHours',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
updateKalenderEvent(kalender_id, updatedInfos) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/updateKalenderEvent',
|
||||||
|
params: { kalender_id, updatedInfos}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
addKalenderEvent(lehreinheit_id, ort_kurzbz, start_date, end_date) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/addKalenderEvent',
|
||||||
|
params: { lehreinheit_id, ort_kurzbz, start_date, end_date}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
calculateMultiWeekPlan(lehreinheit_id, ort_kurzbz, start_date, end_date) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/calculateMultiWeekPlan',
|
||||||
|
params: { lehreinheit_id, ort_kurzbz, start_date, end_date }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
confirmMultiWeekPlan(plan, lehreinheit_id ) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/confirmMultiWeekPlan',
|
||||||
|
params: { plan, lehreinheit_id }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
addToKalenderEvent(target_kalender_id, lehreinheit_id ) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/addToKalenderEvent',
|
||||||
|
params: { target_kalender_id, lehreinheit_id }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getRaumvorschlag(kalender_id) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/getRaumvorschlag',
|
||||||
|
params: { kalender_id }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getHistory(kalender_id) {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/getHistory',
|
||||||
|
params: { kalender_id }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
deleteEntry(kalender_id) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/frontend/v1/tempus/Kalender/deleteEntry',
|
||||||
|
params: { kalender_id }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2025 fhcomplete.org
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getInformation() {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/frontend/v1/tempus/Reservierung/getInformation',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
searchTeilnehmer(query)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: `/api/frontend/v1/tempus/Reservierung/getLektor?query=${encodeURIComponent(query)}`
|
||||||
|
};
|
||||||
|
},
|
||||||
|
searchGroup(query)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: `/api/frontend/v1/tempus/Reservierung/searchGroup?query=${encodeURIComponent(query)}`
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getGruppen(query) {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: `/api/frontend/v1/tempus/Reservierung/getGruppen?query=${encodeURIComponent(query)}`
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getRollen() {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/frontend/v1/tempus/Reservierung/getRollen',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
addReservierung(titel, beschreibung, ort_kurzbz, start_date, end_date, teilnehmer, specialFinalGroups, specialGroups, groups) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/frontend/v1/tempus/Reservierung/addReservierung',
|
||||||
|
params: { titel, beschreibung, ort_kurzbz, start_date, end_date, teilnehmer, specialFinalGroups, specialGroups, groups}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2025 fhcomplete.org
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
get(path) {
|
||||||
|
let url = 'api/frontend/v1/lv/StgTree';
|
||||||
|
if (path)
|
||||||
|
url += '/' + path;
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url
|
||||||
|
};
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
get() {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: 'api/frontend/v1/tempus/favorites'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
set(favorites) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: 'api/frontend/v1/tempus/favorites/set',
|
||||||
|
params: { favorites }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2026 fhcomplete.org
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getSyncs(studiensemester_kurzbz) {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: 'api/frontend/v1/tempus/KalenderSync/getSyncs',
|
||||||
|
params: { studiensemester_kurzbz }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
loadSync(kalender_syncstatus_id) {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: 'api/frontend/v1/tempus/KalenderSync/loadSync',
|
||||||
|
params: { kalender_syncstatus_id }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getSyncStatus() {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: 'api/frontend/v1/tempus/KalenderSync/getSyncStatus',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
add(formData) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: 'api/frontend/v1/tempus/KalenderSync/add',
|
||||||
|
params: { formData }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
delete(kalender_syncstatus_id) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: 'api/frontend/v1/tempus/KalenderSync/delete',
|
||||||
|
params: { kalender_syncstatus_id }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
start(formData) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: 'api/frontend/v1/tempus/KalenderSync/start',
|
||||||
|
params: { formData }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
updateSync(formData) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: 'api/frontend/v1/tempus/KalenderSync/updateSync',
|
||||||
|
params: { formData }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getStudienplan(oe_kurzbz, studiensemester_kurzbz, ausbildungssemester) {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: 'api/frontend/v1/tempus/KalenderSync/getStudienplan',
|
||||||
|
params: { oe_kurzbz, studiensemester_kurzbz, ausbildungssemester }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getMaxSemester(studiengang_kzs) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: 'api/frontend/v1/stv/status/getMaxSemester/',
|
||||||
|
params: { studiengang_kzs }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2024 fhcomplete.org
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import FhcTempus from "../components/Tempus/Tempus.js";
|
||||||
|
import FhcTempusSync from "../components/Tempus/TempusSync.js";
|
||||||
|
|
||||||
|
import Phrasen from "../plugins/Phrasen.js";
|
||||||
|
import {capitalize} from "../helpers/StringHelpers.js";
|
||||||
|
|
||||||
|
|
||||||
|
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
|
||||||
|
|
||||||
|
const router = VueRouter.createRouter({
|
||||||
|
history: VueRouter.createWebHistory(),
|
||||||
|
routes: [
|
||||||
|
{ path: `/${ciPath}/Tempus`, component: FhcTempus },
|
||||||
|
{ path: `/${ciPath}/Tempus/sync`, component: FhcTempusSync },
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = Vue.createApp();
|
||||||
|
|
||||||
|
app.config.globalProperties.$capitalize = capitalize;
|
||||||
|
app
|
||||||
|
.use(router)
|
||||||
|
.use(primevue.config.default, {
|
||||||
|
zIndex: {
|
||||||
|
overlay: 1100,
|
||||||
|
tooltip: 99999
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.use(Phrasen)
|
||||||
|
.directive('tooltip', primevue.tooltip)
|
||||||
|
.mount('#main');
|
||||||
@@ -79,26 +79,30 @@ export default {
|
|||||||
this.$fhcAlert.alertSuccess(this.$p.t('ui/settings_saved'));
|
this.$fhcAlert.alertSuccess(this.$p.t('ui/settings_saved'));
|
||||||
})
|
})
|
||||||
.catch(this.$fhcAlert.handleSystemErrors);
|
.catch(this.$fhcAlert.handleSystemErrors);
|
||||||
|
},
|
||||||
|
reload()
|
||||||
|
{
|
||||||
|
this.$api
|
||||||
|
.call(this.endpoints.get())
|
||||||
|
.then(res => {
|
||||||
|
Object.keys(res.data).forEach(key => {
|
||||||
|
const binding = { ...res.data[key] };
|
||||||
|
delete binding.value;
|
||||||
|
delete binding.options;
|
||||||
|
const options = res.data[key].options;
|
||||||
|
this.tempValues[key] = res.data[key].value;
|
||||||
|
this.setup[key] = {
|
||||||
|
binding,
|
||||||
|
options
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.$emit('update:modelValue', { ...this.tempValues });
|
||||||
|
})
|
||||||
|
.catch(this.$fhcAlert.handleSystemErrors);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.$api
|
this.reload();
|
||||||
.call(this.endpoints.get())
|
|
||||||
.then(res => {
|
|
||||||
Object.keys(res.data).forEach(key => {
|
|
||||||
const binding = { ...res.data[key] };
|
|
||||||
delete binding.value;
|
|
||||||
delete binding.options;
|
|
||||||
const options = res.data[key].options;
|
|
||||||
this.tempValues[key] = res.data[key].value;
|
|
||||||
this.setup[key] = {
|
|
||||||
binding,
|
|
||||||
options
|
|
||||||
};
|
|
||||||
});
|
|
||||||
this.$emit('update:modelValue', { ...this.tempValues });
|
|
||||||
})
|
|
||||||
.catch(this.$fhcAlert.handleSystemErrors);
|
|
||||||
},
|
},
|
||||||
template: /* html */`
|
template: /* html */`
|
||||||
<fhc-form class="stv-config" ref="form" @submit.prevent="update">
|
<fhc-form class="stv-config" ref="form" @submit.prevent="update">
|
||||||
|
|||||||
@@ -1,26 +1,35 @@
|
|||||||
import BaseDraganddrop from './Base/DragAndDrop.js';
|
|
||||||
import BaseHeader from './Base/Header.js';
|
import BaseHeader from './Base/Header.js';
|
||||||
import BaseSlider from './Base/Slider.js';
|
import BaseSlider from './Base/Slider.js';
|
||||||
import BsModal from '../Bootstrap/Modal.js';
|
import BsModal from '../Bootstrap/Modal.js';
|
||||||
|
|
||||||
import CalClick from '../../directives/Calendar/Click.js';
|
import CalClick from '../../directives/Calendar/Click.js';
|
||||||
|
import DragClick from '../../directives/dragClick.js';
|
||||||
|
import Draggable from '../../directives/draggable.js';
|
||||||
|
import Drop from '../../directives/drop.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "CalendarBase",
|
name: "CalendarBase",
|
||||||
components: {
|
components: {
|
||||||
BaseDraganddrop,
|
|
||||||
BaseHeader,
|
BaseHeader,
|
||||||
BaseSlider,
|
BaseSlider,
|
||||||
BsModal
|
BsModal
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
CalClick
|
CalClick,
|
||||||
|
DragClick,
|
||||||
|
Draggable,
|
||||||
|
Drop
|
||||||
},
|
},
|
||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
|
events: Vue.computed(() => this.convertedEvents),
|
||||||
|
backgrounds: Vue.computed(() => this.convertedBackgrounds),
|
||||||
|
dropAllowed: Vue.computed(() => !!this.onDrop),
|
||||||
|
onDrop: Vue.computed(() => this.onDrop || null),
|
||||||
locale: Vue.computed(() => this.locale),
|
locale: Vue.computed(() => this.locale),
|
||||||
timezone: Vue.computed(() => this.timezone),
|
timezone: Vue.computed(() => this.timezone),
|
||||||
timeGrid: Vue.computed(() => this.timeGrid),
|
timeGrid: Vue.computed(() => this.timeGrid),
|
||||||
|
hoursPlan: Vue.computed(() => this.hoursPlan),
|
||||||
draggableEvents: Vue.computed(() => {
|
draggableEvents: Vue.computed(() => {
|
||||||
if (!this.draggableEvents)
|
if (!this.draggableEvents)
|
||||||
return () => false;
|
return () => false;
|
||||||
@@ -43,8 +52,20 @@ export default {
|
|||||||
|
|
||||||
return () => true;
|
return () => true;
|
||||||
}),
|
}),
|
||||||
|
resizableEvents: Vue.computed(() => {
|
||||||
|
if (!this.resizableEvents)
|
||||||
|
return () => false;
|
||||||
|
|
||||||
|
if (Array.isArray(this.resizableEvents))
|
||||||
|
return event => this.resizableEvents.includes(event.type);
|
||||||
|
if (this.resizableEvents instanceof Function)
|
||||||
|
return this.resizableEvents;
|
||||||
|
|
||||||
|
return () => true;
|
||||||
|
}),
|
||||||
hasDragoverFunc: Vue.computed(() => this.onDragover),
|
hasDragoverFunc: Vue.computed(() => this.onDragover),
|
||||||
mode: Vue.computed(() => this.mode)
|
mode: Vue.computed(() => this.mode),
|
||||||
|
onResize: Vue.computed(() => this.onResize || null),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
@@ -93,11 +114,18 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: undefined
|
default: undefined
|
||||||
},
|
},
|
||||||
|
btnTableList: {
|
||||||
|
type: Boolean,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
timeGrid: Array,
|
timeGrid: Array,
|
||||||
|
hoursPlan: Object,
|
||||||
draggableEvents: [Boolean, Array, Function],
|
draggableEvents: [Boolean, Array, Function],
|
||||||
dropableEvents: [Boolean, Array, Function],
|
dropableEvents: [Boolean, Array, Function],
|
||||||
|
resizableEvents: [Boolean, Array, Function],
|
||||||
onDragover: Function,
|
onDragover: Function,
|
||||||
onDrop: Function
|
onDrop: Function,
|
||||||
|
onResize: Function
|
||||||
},
|
},
|
||||||
emits: [
|
emits: [
|
||||||
"click:next",
|
"click:next",
|
||||||
@@ -136,7 +164,7 @@ export default {
|
|||||||
res.start = luxon.DateTime.fromMillis(res.start, { zone: this.timezone, locale: this.locale });
|
res.start = luxon.DateTime.fromMillis(res.start, { zone: this.timezone, locale: this.locale });
|
||||||
else if (res.start instanceof Date)
|
else if (res.start instanceof Date)
|
||||||
res.start = luxon.DateTime.fromJSDate(res.start, { zone: this.timezone, locale: this.locale });
|
res.start = luxon.DateTime.fromJSDate(res.start, { zone: this.timezone, locale: this.locale });
|
||||||
else if (typeof res.start ===
|
else if (typeof res.start ===
|
||||||
'string' || res.start instanceof String)
|
'string' || res.start instanceof String)
|
||||||
res.start = luxon.DateTime.fromISO(res.start, { zone: this.timezone, locale: this.locale });
|
res.start = luxon.DateTime.fromISO(res.start, { zone: this.timezone, locale: this.locale });
|
||||||
}
|
}
|
||||||
@@ -145,7 +173,7 @@ export default {
|
|||||||
res.end = luxon.DateTime.fromMillis(res.end, { zone: this.timezone, locale: this.locale });
|
res.end = luxon.DateTime.fromMillis(res.end, { zone: this.timezone, locale: this.locale });
|
||||||
else if (res.end instanceof Date)
|
else if (res.end instanceof Date)
|
||||||
res.end = luxon.DateTime.fromJSDate(res.end, { zone: this.timezone, locale: this.locale });
|
res.end = luxon.DateTime.fromJSDate(res.end, { zone: this.timezone, locale: this.locale });
|
||||||
else if (typeof res.end ===
|
else if (typeof res.end ===
|
||||||
'string' || res.end instanceof String)
|
'string' || res.end instanceof String)
|
||||||
res.end = luxon.DateTime.fromISO(res.end, { zone: this.timezone, locale: this.locale });
|
res.end = luxon.DateTime.fromISO(res.end, { zone: this.timezone, locale: this.locale });
|
||||||
}
|
}
|
||||||
@@ -171,7 +199,10 @@ export default {
|
|||||||
// choose default mode
|
// choose default mode
|
||||||
let mode = this.mode;
|
let mode = this.mode;
|
||||||
if (mode)
|
if (mode)
|
||||||
mode = mode.toLowerCase();
|
{
|
||||||
|
const lower = mode.toLowerCase();
|
||||||
|
mode = Object.keys(this.modes).find(key => key.toLowerCase() === lower) || null;
|
||||||
|
}
|
||||||
if (!mode || !this.modes[mode])
|
if (!mode || !this.modes[mode])
|
||||||
mode = Object.keys(this.modes).find(Boolean); // start with first entry as active mode
|
mode = Object.keys(this.modes).find(Boolean); // start with first entry as active mode
|
||||||
return mode || '';
|
return mode || '';
|
||||||
@@ -188,7 +219,7 @@ export default {
|
|||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
sDate(n, o) {
|
sDate(n, o) {
|
||||||
if (this.sDate.isValid && !this.sDate.hasSame(this.internalDate, 'day'))
|
if (this.sDate.isValid && (!this.internalDate || !this.sDate.hasSame(this.internalDate, 'day')))
|
||||||
this.internalDate = this.sDate;
|
this.internalDate = this.sDate;
|
||||||
},
|
},
|
||||||
sMode() {
|
sMode() {
|
||||||
@@ -240,9 +271,7 @@ export default {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDropItem(evt, start, end) {
|
|
||||||
this.$emit('drop', evt, start, end);
|
|
||||||
},
|
|
||||||
showEventModal(eventObj) {
|
showEventModal(eventObj) {
|
||||||
this.modalEvent = eventObj;
|
this.modalEvent = eventObj;
|
||||||
this.$refs.modal.show();
|
this.$refs.modal.show();
|
||||||
@@ -263,11 +292,7 @@ export default {
|
|||||||
},
|
},
|
||||||
template: /* html */`
|
template: /* html */`
|
||||||
<div class="fhc-calendar-base h-100">
|
<div class="fhc-calendar-base h-100">
|
||||||
<base-draganddrop
|
<div class="card h-100"
|
||||||
class="card h-100"
|
|
||||||
:events="convertedEvents"
|
|
||||||
:backgrounds="convertedBackgrounds"
|
|
||||||
@drop="onDropItem"
|
|
||||||
v-cal-click:container
|
v-cal-click:container
|
||||||
@cal-click-default.capture="handleClickDefaults"
|
@cal-click-default.capture="handleClickDefaults"
|
||||||
>
|
>
|
||||||
@@ -282,6 +307,7 @@ export default {
|
|||||||
:btn-week="!!modes['week'] && (btnWeek || (showBtns && btnWeek !== false))"
|
:btn-week="!!modes['week'] && (btnWeek || (showBtns && btnWeek !== false))"
|
||||||
:btn-month="!!modes['month'] && (btnMonth || (showBtns && btnMonth !== false))"
|
:btn-month="!!modes['month'] && (btnMonth || (showBtns && btnMonth !== false))"
|
||||||
:btn-list="!!modes['list'] && (btnList || (showBtns && btnList !== false))"
|
:btn-list="!!modes['list'] && (btnList || (showBtns && btnList !== false))"
|
||||||
|
:btn-table-list="!!modes['tableList'] && (btnTableList || (showBtns && btnTableList !== false))"
|
||||||
:mode-options="modeOptions ? modeOptions[cMode] : undefined"
|
:mode-options="modeOptions ? modeOptions[cMode] : undefined"
|
||||||
>
|
>
|
||||||
<slot name="actions" />
|
<slot name="actions" />
|
||||||
@@ -293,11 +319,12 @@ export default {
|
|||||||
@update:range="$emit('update:range', $event)"
|
@update:range="$emit('update:range', $event)"
|
||||||
@request-modal-open="showEventModal"
|
@request-modal-open="showEventModal"
|
||||||
@request-modal-close="hideEventModal"
|
@request-modal-close="hideEventModal"
|
||||||
|
@drop="$emit('drop', $event)"
|
||||||
v-bind="modeOptions ? modeOptions[cMode] : null || {}"
|
v-bind="modeOptions ? modeOptions[cMode] : null || {}"
|
||||||
>
|
>
|
||||||
<template v-slot="slot"><slot v-bind="slot" /></template>
|
<template v-slot="slot"><slot v-bind="slot" /></template>
|
||||||
</component>
|
</component>
|
||||||
</base-draganddrop>
|
</div>
|
||||||
<bs-modal ref="modal" dialog-class="modal-lg" body-class="" @hidden-bs-modal="onModalHidden">
|
<bs-modal ref="modal" dialog-class="modal-lg" body-class="" @hidden-bs-modal="onModalHidden">
|
||||||
<template #title>
|
<template #title>
|
||||||
<slot v-if="modalEvent" v-bind="{mode: 'eventheader', event: modalEvent.event}" />
|
<slot v-if="modalEvent" v-bind="{mode: 'eventheader', event: modalEvent.event}" />
|
||||||
@@ -308,4 +335,4 @@ export default {
|
|||||||
</bs-modal>
|
</bs-modal>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import GridLine from './Grid/Line.js';
|
import GridLine from './Grid/Line.js';
|
||||||
import GridLineEvent from './Grid/Line/Event.js';
|
import GridLineEvent from './Grid/Line/Event.js';
|
||||||
|
|
||||||
import CalDnd from '../../../directives/Calendar/DragAndDrop.js';
|
import drop from '../../../directives/drop.js';
|
||||||
|
import { useResizeHandler } from '../../../helpers/Tempus/ResizeHandler.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "CalendarGrid",
|
name: "CalendarGrid",
|
||||||
@@ -10,12 +11,18 @@ export default {
|
|||||||
GridLineEvent
|
GridLineEvent
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
CalDnd
|
drop
|
||||||
},
|
},
|
||||||
inject: {
|
inject: {
|
||||||
originalEvents: "events",
|
originalEvents: "events",
|
||||||
originalBackgrounds: "backgrounds",
|
originalBackgrounds: "backgrounds",
|
||||||
dropAllowed: "dropAllowed"
|
dropAllowed: "dropAllowed",
|
||||||
|
onDrop: "onDrop",
|
||||||
|
onResize: "onResize",
|
||||||
|
timeGrid: {
|
||||||
|
from: "timeGrid",
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
},
|
},
|
||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
@@ -57,10 +64,10 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
dragging: false,
|
|
||||||
resizeObserver: null,
|
resizeObserver: null,
|
||||||
mutationObserver: null,
|
mutationObserver: null,
|
||||||
userScroll: true
|
userScroll: true,
|
||||||
|
isDragging: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -252,17 +259,27 @@ export default {
|
|||||||
pageLeft += this.getPageLeft(el.offsetParent);
|
pageLeft += this.getPageLeft(el.offsetParent);
|
||||||
return pageLeft;
|
return pageLeft;
|
||||||
},
|
},
|
||||||
getTimestampFromMouse(evt, dayTimestamp) {
|
getTimestampFromMouse(evt, dayTimestamp)
|
||||||
|
{
|
||||||
let mouse, mouseFrac;
|
let mouse, mouseFrac;
|
||||||
if (this.flipAxis) {
|
|
||||||
mouse = evt.pageX - this.getPageLeft(this.$refs.body) + this.$refs.main.scrollLeft;
|
const grabOffsetY = parseFloat(evt.dataTransfer.getData('fhc-grab-offset-y')) || 0;
|
||||||
|
const grabOffsetX = parseFloat(evt.dataTransfer.getData('fhc-grab-offset-x')) || 0;
|
||||||
|
|
||||||
|
if (this.flipAxis)
|
||||||
|
{
|
||||||
|
mouse = evt.pageX - this.getPageLeft(this.$refs.body) + this.$refs.scroller.scrollLeft - grabOffsetX;
|
||||||
mouseFrac = mouse / this.$refs.body.offsetWidth;
|
mouseFrac = mouse / this.$refs.body.offsetWidth;
|
||||||
} else {
|
}
|
||||||
mouse = evt.pageY - this.getPageTop(this.$refs.body) + this.$refs.main.scrollTop;
|
else
|
||||||
|
{
|
||||||
|
mouse = evt.pageY - this.getPageTop(this.$refs.body) + this.$refs.scroller.scrollTop - grabOffsetY;
|
||||||
mouseFrac = mouse / this.$refs.body.offsetHeight;
|
mouseFrac = mouse / this.$refs.body.offsetHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dayTimestamp + this.start + Math.floor((this.end - this.start) * mouseFrac);
|
let rawTimestamp = dayTimestamp + this.start + Math.floor((this.end - this.start) * mouseFrac);
|
||||||
|
let fiveMinutes = 5 * 60 * 1000;
|
||||||
|
return Math.round(rawTimestamp / fiveMinutes) * fiveMinutes;
|
||||||
},
|
},
|
||||||
|
|
||||||
/* SCROLLING */
|
/* SCROLLING */
|
||||||
@@ -308,7 +325,153 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.$refs.scroller.scrollTo(0, 0);
|
this.$refs.scroller.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
calculateNettoDuration(start, end) {
|
||||||
|
const startDay = start.startOf('day');
|
||||||
|
const blocks = this.axisPartsWithBreaks.filter(p => p.index !== undefined);
|
||||||
|
|
||||||
|
let nettoDuration = luxon.Duration.fromMillis(0);
|
||||||
|
|
||||||
|
for (const block of blocks)
|
||||||
|
{
|
||||||
|
const blockStart = startDay.plus(block.start);
|
||||||
|
const blockEnd = startDay.plus(block.end);
|
||||||
|
|
||||||
|
const overlapStart = blockStart > start ? blockStart : start;
|
||||||
|
const overlapEnd = blockEnd < end ? blockEnd : end;
|
||||||
|
|
||||||
|
if (overlapStart < overlapEnd) {
|
||||||
|
nettoDuration = nettoDuration.plus(overlapEnd.diff(overlapStart));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nettoDuration;
|
||||||
|
},
|
||||||
|
|
||||||
|
calculateDropEnd(dropStart, durationMs) {
|
||||||
|
const duration = luxon.Duration.fromMillis(durationMs);
|
||||||
|
const blocks = this.axisPartsWithBreaks.filter(p => p.index !== undefined);
|
||||||
|
let accumulated = luxon.Duration.fromMillis(0);
|
||||||
|
|
||||||
|
for (const block of blocks)
|
||||||
|
{
|
||||||
|
const blockStart = dropStart.startOf('day').plus(block.start);
|
||||||
|
const blockEnd = dropStart.startOf('day').plus(block.end);
|
||||||
|
|
||||||
|
if (blockEnd <= dropStart) continue;
|
||||||
|
|
||||||
|
const relevantStart = blockStart > dropStart ? blockStart : dropStart;
|
||||||
|
const relevantDuration = blockEnd.diff(relevantStart);
|
||||||
|
|
||||||
|
accumulated = accumulated.plus(relevantDuration);
|
||||||
|
|
||||||
|
if (accumulated >= duration)
|
||||||
|
{
|
||||||
|
const overflow = accumulated.minus(duration);
|
||||||
|
return blockEnd.minus(overflow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastBlock = blocks[blocks.length - 1];
|
||||||
|
return dropStart.startOf('day').plus(lastBlock.end);
|
||||||
|
},
|
||||||
|
|
||||||
|
onDropSnap(evt, items, date, part) {
|
||||||
|
let obj = items;
|
||||||
|
if (!obj?.orig) return;
|
||||||
|
|
||||||
|
const dayStr = evt?.currentTarget?.dataset?.day;
|
||||||
|
const dropDay = dayStr ? luxon.DateTime.fromISO(dayStr) : date;
|
||||||
|
|
||||||
|
const rawTimestamp = this.getTimestampFromMouse(evt, dropDay.toMillis());
|
||||||
|
const grabTime = luxon.DateTime.fromMillis(rawTimestamp);
|
||||||
|
|
||||||
|
const blocks = this.axisPartsWithBreaks.filter(p => p.index !== undefined);
|
||||||
|
const grabOffset = grabTime.diff(dropDay);
|
||||||
|
|
||||||
|
const snappedPart = blocks.find(b => grabOffset >= b.start && grabOffset < b.end) || part;
|
||||||
|
const dropStart = dropDay.plus(snappedPart.start);
|
||||||
|
|
||||||
|
let nettoDuration = this._getNettoDurationForDrop(obj);
|
||||||
|
let dropEnd = this.calculateDropEnd(dropStart, nettoDuration);
|
||||||
|
|
||||||
|
this.onDrop?.({
|
||||||
|
item: [obj],
|
||||||
|
start: dropStart.toISO(),
|
||||||
|
end: dropEnd.toISO(),
|
||||||
|
ctrlKey: !!(evt?.ctrlKey || evt?.metaKey)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_getNettoDurationForDrop(obj) {
|
||||||
|
if (obj.orig?.isostart && obj.orig?.isoend)
|
||||||
|
{
|
||||||
|
const s = luxon.DateTime.fromISO(obj.orig.isostart);
|
||||||
|
const e = luxon.DateTime.fromISO(obj.orig.isoend);
|
||||||
|
if (s.isValid && e.isValid)
|
||||||
|
return this.calculateNettoDuration(s, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.stundenblockung)
|
||||||
|
{
|
||||||
|
let blocks = this.axisPartsWithBreaks.filter(p => p.index !== undefined);
|
||||||
|
let firstBlock = blocks[0];
|
||||||
|
let blockMinutes = luxon.Duration.fromISO(firstBlock.end).minus(luxon.Duration.fromISO(firstBlock.start)).as('minutes');
|
||||||
|
if (!Number.isFinite(blockMinutes) || blockMinutes <= 0) blockMinutes = 45;
|
||||||
|
return luxon.Duration.fromObject({ minutes: obj.stundenblockung * blockMinutes });
|
||||||
|
}
|
||||||
|
|
||||||
|
return luxon.Duration.fromObject({ minutes: 45 });
|
||||||
|
},
|
||||||
|
onDropFree(evt, items, date)
|
||||||
|
{
|
||||||
|
let obj = items;
|
||||||
|
if (!obj?.orig)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const timestamp = this.getTimestampFromMouse(evt, date);
|
||||||
|
const dropStart = luxon.DateTime.fromMillis(timestamp);
|
||||||
|
|
||||||
|
let nettoDuration = this._getNettoDurationForDrop(obj);
|
||||||
|
let dropEnd = this.calculateDropEnd(dropStart, nettoDuration);
|
||||||
|
|
||||||
|
this.onDrop?.({
|
||||||
|
item: [obj],
|
||||||
|
start: dropStart.toISO(),
|
||||||
|
end: dropEnd.toISO(),
|
||||||
|
ctrlKey: !!(evt?.ctrlKey || evt?.metaKey)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleResizeStart({ edge, evt, el, event })
|
||||||
|
{
|
||||||
|
const gridEl = this.$refs.body;
|
||||||
|
|
||||||
|
if (!gridEl)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.resizeHandler.startResize(edge, evt, {
|
||||||
|
el,
|
||||||
|
gridEl,
|
||||||
|
event,
|
||||||
|
timeGrid: this.timeGrid,
|
||||||
|
onEnd: ({ event, newStart, newEnd }) => {
|
||||||
|
const orig = event?.orig;
|
||||||
|
if (!orig || !newStart || !newEnd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.onResize?.({
|
||||||
|
item: [{ type: 'kalender', id: orig.kalender_id, orig }],
|
||||||
|
start: newStart,
|
||||||
|
end: newEnd
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup()
|
||||||
|
{
|
||||||
|
const resizeHandler = useResizeHandler();
|
||||||
|
return { resizeHandler };
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
this.disableAutoScroll();
|
this.disableAutoScroll();
|
||||||
@@ -382,11 +545,11 @@ export default {
|
|||||||
ref="body"
|
ref="body"
|
||||||
class="grid-body"
|
class="grid-body"
|
||||||
style="display:grid;grid-template-rows:subgrid;grid-template-columns:subgrid"
|
style="display:grid;grid-template-rows:subgrid;grid-template-columns:subgrid"
|
||||||
|
@dragenter="isDragging = true"
|
||||||
|
@dragleave.self="isDragging = false"
|
||||||
|
@dragend="isDragging = false"
|
||||||
|
@drop="isDragging = false"
|
||||||
:style="'grid-' + axisCol + ':2/-1;grid-' + axisRow + ':1/-1'"
|
:style="'grid-' + axisCol + ':2/-1;grid-' + axisRow + ':1/-1'"
|
||||||
v-cal-dnd:dropcage
|
|
||||||
@calendar-dragenter="dragging = true"
|
|
||||||
@calendar-dragleave="dragging = false"
|
|
||||||
@dragover="dropAllowed ? $event.preventDefault() : null"
|
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
v-for="(date, index) in axisMain"
|
v-for="(date, index) in axisMain"
|
||||||
@@ -401,9 +564,11 @@ export default {
|
|||||||
>
|
>
|
||||||
<slot name="part-body" v-bind="{ index, part }" />
|
<slot name="part-body" v-bind="{ index, part }" />
|
||||||
<div
|
<div
|
||||||
v-if="snapToGrid && dragging"
|
v-if="snapToGrid"
|
||||||
style="position:absolute;inset:0;z-index:1"
|
style="position:absolute;inset:0"
|
||||||
v-cal-dnd:dropzone.once="{date: date.plus(part.start || part), ends: ends.slice(ends.findIndex(end => end > date))}"
|
:style="{ zIndex: isDragging ? 10 : 1 }"
|
||||||
|
:data-day="date.toFormat('yyyy-MM-dd')"
|
||||||
|
v-drop:move.lehreinheit.kalender.reservierung="(evt, item) => onDropSnap(evt, item, date, part)"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<grid-line
|
<grid-line
|
||||||
@@ -413,6 +578,7 @@ export default {
|
|||||||
:events="eventsNormal[index]"
|
:events="eventsNormal[index]"
|
||||||
:backgrounds="backgrounds[index]"
|
:backgrounds="backgrounds[index]"
|
||||||
style="position:relative"
|
style="position:relative"
|
||||||
|
@resize-start="handleResizeStart"
|
||||||
:style="'grid-' + axisRow + ':1/-1;grid-' + axisCol + ':' + (1+index)"
|
:style="'grid-' + axisRow + ':1/-1;grid-' + axisCol + ':' + (1+index)"
|
||||||
>
|
>
|
||||||
<template #event="slot">
|
<template #event="slot">
|
||||||
@@ -420,9 +586,10 @@ export default {
|
|||||||
</template>
|
</template>
|
||||||
<template #dropzone>
|
<template #dropzone>
|
||||||
<div
|
<div
|
||||||
v-if="!snapToGrid && dragging"
|
v-if="!snapToGrid"
|
||||||
style="position:absolute;inset:0;z-index:1"
|
style="position:absolute;inset:0"
|
||||||
v-cal-dnd:dropzone="evt => getTimestampFromMouse(evt, date)"
|
:style="{ zIndex: isDragging ? 10 : 1 }"
|
||||||
|
v-drop:move.lehreinheit.kalender.reservierung="(evt, item) => onDropFree(evt, item, date)"
|
||||||
></div>
|
></div>
|
||||||
</template>
|
</template>
|
||||||
</grid-line>
|
</grid-line>
|
||||||
@@ -432,4 +599,4 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
@@ -137,6 +137,7 @@ export default {
|
|||||||
v-if="!event.display || event.display === 'default'"
|
v-if="!event.display || event.display === 'default'"
|
||||||
:style="'grid-' + axisRow + ': ' + event.rows.join('/')"
|
:style="'grid-' + axisRow + ': ' + event.rows.join('/')"
|
||||||
:event="event"
|
:event="event"
|
||||||
|
@resize-start="$emit('resize-start', $event)"
|
||||||
>
|
>
|
||||||
<template v-slot="slot">
|
<template v-slot="slot">
|
||||||
<slot name="event" v-bind="slot" />
|
<slot name="event" v-bind="slot" />
|
||||||
|
|||||||
@@ -1,15 +1,41 @@
|
|||||||
import CalDnd from '../../../../../directives/Calendar/DragAndDrop.js';
|
import draggable from '../../../../../directives/draggable.js';
|
||||||
|
import drop from '../../../../../directives/drop.js';
|
||||||
import CalClick from '../../../../../directives/Calendar/Click.js';
|
import CalClick from '../../../../../directives/Calendar/Click.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "GridLineEvent",
|
name: "GridLineEvent",
|
||||||
directives: {
|
directives: {
|
||||||
CalDnd,
|
draggable,
|
||||||
|
drop,
|
||||||
CalClick
|
CalClick
|
||||||
},
|
},
|
||||||
|
emits: [
|
||||||
|
'resize-start'
|
||||||
|
],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
contextMenu: {
|
||||||
|
show: false,
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
inject: {
|
inject: {
|
||||||
draggableEvents: "draggableEvents",
|
draggableEvents: "draggableEvents",
|
||||||
mode: "mode"
|
resizableEvents: {
|
||||||
|
from: "resizableEvents",
|
||||||
|
default: () => () => false
|
||||||
|
},
|
||||||
|
mode: "mode",
|
||||||
|
contextMenuActions: {
|
||||||
|
from: "contextMenuActions",
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
onDrop: {
|
||||||
|
from: "onDrop",
|
||||||
|
default: () => null
|
||||||
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
event: {
|
event: {
|
||||||
@@ -27,6 +53,9 @@ export default {
|
|||||||
draggable() {
|
draggable() {
|
||||||
return !this.isHeaderOrFooter && this.draggableEvents(this.event.orig, this.mode);
|
return !this.isHeaderOrFooter && this.draggableEvents(this.event.orig, this.mode);
|
||||||
},
|
},
|
||||||
|
resizable() {
|
||||||
|
return !this.isHeaderOrFooter && this.resizableEvents(this.event.orig, this.mode);
|
||||||
|
},
|
||||||
classes() {
|
classes() {
|
||||||
const classes = [];
|
const classes = [];
|
||||||
if (this.isHeaderOrFooter) {
|
if (this.isHeaderOrFooter) {
|
||||||
@@ -37,21 +66,131 @@ export default {
|
|||||||
if (this.event.endsHere)
|
if (this.event.endsHere)
|
||||||
classes.push('event-end');
|
classes.push('event-end');
|
||||||
}
|
}
|
||||||
return classes
|
|
||||||
|
classes.push(`calender_id-${this.event.orig.kalender_id}`);
|
||||||
|
return classes;
|
||||||
|
},
|
||||||
|
dragKalenderCollection() {
|
||||||
|
const orig = this.event.orig;
|
||||||
|
return {
|
||||||
|
type: 'kalender',
|
||||||
|
id: orig?.kalender_id ?? null,
|
||||||
|
orig,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
activeContextActions() {
|
||||||
|
if (this.isHeaderOrFooter) return [];
|
||||||
|
const type = this.event.orig?.type ?? 'lehreinheit';
|
||||||
|
return this.contextMenuActions[type] ?? this.contextMenuActions['default'] ?? [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
template: /* html */`
|
methods: {
|
||||||
|
onResizeStart(edge, evt) {
|
||||||
|
this.$emit('resize-start', {
|
||||||
|
edge,
|
||||||
|
evt,
|
||||||
|
el: this.$refs.eventEl,
|
||||||
|
event: this.event
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onRightClick(evt) {
|
||||||
|
this.contextMenu.show = true;
|
||||||
|
this.contextMenu.x = evt.clientX;
|
||||||
|
this.contextMenu.y = evt.clientY;
|
||||||
|
},
|
||||||
|
onContextAction(action) {
|
||||||
|
this.contextMenu.show = false;
|
||||||
|
action(this.event.orig);
|
||||||
|
},
|
||||||
|
closeContextMenu() {
|
||||||
|
this.contextMenu.show = false;
|
||||||
|
},
|
||||||
|
onDragStart(evt) {
|
||||||
|
const rect = this.$refs.eventEl.getBoundingClientRect();
|
||||||
|
evt.dataTransfer.setData('fhc-grab-offset-y', evt.clientY - rect.top);
|
||||||
|
evt.dataTransfer.setData('fhc-grab-offset-x', evt.clientX - rect.left);
|
||||||
|
},
|
||||||
|
onDropOnCard(evt, items) {
|
||||||
|
if (this.isHeaderOrFooter || !this.onDrop)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const list = Array.isArray(items) ? items : [items];
|
||||||
|
const obj = list[0];
|
||||||
|
if (!obj)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((evt.ctrlKey || evt.metaKey) && obj.type === 'lehreinheit')
|
||||||
|
{
|
||||||
|
return this.onDrop({
|
||||||
|
item: [obj],
|
||||||
|
ctrlKey: true,
|
||||||
|
targetKalenderId: this.event.orig?.kalender_id ?? null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.onDrop({
|
||||||
|
item: [obj],
|
||||||
|
start: this.event.start.toISO(),
|
||||||
|
end: this.event.end.toISO(),
|
||||||
|
ctrlKey: false,
|
||||||
|
targetKalenderId: null
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template:`
|
||||||
<div
|
<div
|
||||||
class="fhc-calendar-base-grid-line-event event"
|
class="fhc-calendar-base-grid-line-event event"
|
||||||
:class="classes"
|
:class="classes"
|
||||||
style="z-index: 1"
|
style="z-index: 11"
|
||||||
:draggable="draggable"
|
:draggable="draggable"
|
||||||
v-cal-dnd:draggable="event"
|
ref="eventEl"
|
||||||
|
@dragstart="onDragStart"
|
||||||
|
v-draggable:move.noimage="draggable ? dragKalenderCollection : {}"
|
||||||
|
v-drop:move.lehreinheit.kalender.reservierung="onDropOnCard"
|
||||||
v-cal-click:event="isHeaderOrFooter ? event : event.orig"
|
v-cal-click:event="isHeaderOrFooter ? event : event.orig"
|
||||||
|
@contextmenu.prevent="onRightClick"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
v-if="resizable"
|
||||||
|
class="fhc-resize-bar fhc-resize-bar--top"
|
||||||
|
@pointerdown.prevent.stop="onResizeStart('start', $event)"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-grip-lines text-muted"></i>
|
||||||
|
</div>
|
||||||
<slot :event="isHeaderOrFooter ? event : event.orig">
|
<slot :event="isHeaderOrFooter ? event : event.orig">
|
||||||
{{ event.orig }}
|
{{ event.orig }}
|
||||||
</slot>
|
</slot>
|
||||||
|
<div
|
||||||
|
v-if="resizable"
|
||||||
|
class="fhc-resize-bar fhc-resize-bar--bottom"
|
||||||
|
@pointerdown.prevent.stop="onResizeStart('end', $event)"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-grip-lines text-muted"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<teleport to="body">
|
||||||
|
<div
|
||||||
|
v-if="contextMenu.show"
|
||||||
|
style="position:fixed; inset:0; z-index:9998"
|
||||||
|
@click="closeContextMenu"
|
||||||
|
@contextmenu.prevent="closeContextMenu"
|
||||||
|
/>
|
||||||
|
<ul
|
||||||
|
v-if="contextMenu.show"
|
||||||
|
class="dropdown-menu show"
|
||||||
|
:style="{ position: 'fixed', top: contextMenu.y + 'px', left: contextMenu.x + 'px', zIndex: 9999 }"
|
||||||
|
>
|
||||||
|
<li v-for="action in activeContextActions" :key="action.label">
|
||||||
|
<button class="dropdown-item" type="button" @click.stop="onContextAction(action.action)">
|
||||||
|
<i v-if="action.icon" :class="action.icon + ' me-2'"></i>
|
||||||
|
{{ action.label }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</teleport>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import draggable from '../../../../../directives/draggable.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'EventCard',
|
||||||
|
directives: {
|
||||||
|
draggable,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
event: { type: Object, required: true },
|
||||||
|
parked: Boolean
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
dragKalenderCollection() {
|
||||||
|
return this.event
|
||||||
|
},
|
||||||
|
topicString() {
|
||||||
|
return Array.isArray(this.event.orig.topic) ? this.event.orig.topic.join(', ') : this.event.orig.topic;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div
|
||||||
|
class="fhc-calendar-base-grid-line-event event"
|
||||||
|
v-draggable:move.noimage="dragKalenderCollection"
|
||||||
|
style="border:1px"
|
||||||
|
>
|
||||||
|
<div class="title">
|
||||||
|
{{ topicString || event.orig.titel || event.orig.lehrfach }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ event.orig.datum }} {{ event.orig.beginn }}–{{ event.orig.ende }}
|
||||||
|
<span v-if="event.ort_kurzbz">· {{ event.orig.ort_kurzbz }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
};
|
||||||
@@ -24,7 +24,8 @@ export default {
|
|||||||
btnMonth: Boolean,
|
btnMonth: Boolean,
|
||||||
btnWeek: Boolean,
|
btnWeek: Boolean,
|
||||||
btnDay: Boolean,
|
btnDay: Boolean,
|
||||||
btnList: Boolean
|
btnList: Boolean,
|
||||||
|
btnTableList: Boolean
|
||||||
},
|
},
|
||||||
emits: [
|
emits: [
|
||||||
"next",
|
"next",
|
||||||
@@ -89,6 +90,16 @@ export default {
|
|||||||
>
|
>
|
||||||
<i class="fa fa-table-list"></i>
|
<i class="fa fa-table-list"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
v-if="btnTableList"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
:class="{active: mode === 'tableList'}"
|
||||||
|
@click="clickMode($event, 'tableList')"
|
||||||
|
>
|
||||||
|
<i class="fa fa-table-list"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export default {
|
|||||||
case "list":
|
case "list":
|
||||||
return [this.convertedDate.startOf('day').ts, this.convertedDate.startOf('day').plus({ days: this.listLength }).ts - 1];
|
return [this.convertedDate.startOf('day').ts, this.convertedDate.startOf('day').plus({ days: this.listLength }).ts - 1];
|
||||||
case "week":
|
case "week":
|
||||||
|
case "tableList":
|
||||||
return [this.convertedDate.startOf('week', { useLocaleWeeks: true }).ts, this.convertedDate.endOf('week', { useLocaleWeeks: true }).ts];
|
return [this.convertedDate.startOf('week', { useLocaleWeeks: true }).ts, this.convertedDate.endOf('week', { useLocaleWeeks: true }).ts];
|
||||||
case "day":
|
case "day":
|
||||||
return this.convertedDate;
|
return this.convertedDate;
|
||||||
@@ -51,6 +52,7 @@ export default {
|
|||||||
case "month":
|
case "month":
|
||||||
return this.date.toLocaleString({ month: 'long', year: 'numeric' });
|
return this.date.toLocaleString({ month: 'long', year: 'numeric' });
|
||||||
case "week":
|
case "week":
|
||||||
|
case "tableList":
|
||||||
var year = this.date.localWeekYear;
|
var year = this.date.localWeekYear;
|
||||||
var week = this.date.toFormat('nn');
|
var week = this.date.toFormat('nn');
|
||||||
return this.$p.t('calendar/year_kw', { year, week });
|
return this.$p.t('calendar/year_kw', { year, week });
|
||||||
@@ -76,6 +78,7 @@ export default {
|
|||||||
break;
|
break;
|
||||||
case "list":
|
case "list":
|
||||||
case "week":
|
case "week":
|
||||||
|
case "tableList":
|
||||||
date = luxon.DateTime.fromJSDate(value[0]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
|
date = luxon.DateTime.fromJSDate(value[0]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
|
||||||
break;
|
break;
|
||||||
case "day":
|
case "day":
|
||||||
@@ -96,7 +99,7 @@ export default {
|
|||||||
@update:model-value="update"
|
@update:model-value="update"
|
||||||
:format="() => title"
|
:format="() => title"
|
||||||
:month-picker="mode == 'month'"
|
:month-picker="mode == 'month'"
|
||||||
:week-picker="mode == 'week'"
|
:week-picker="mode == 'week' || mode == 'tableList'"
|
||||||
:range="mode == 'list' ? { autoRange: listLength - 1 } : false"
|
:range="mode == 'list' ? { autoRange: listLength - 1 } : false"
|
||||||
:text-input="mode == 'day'"
|
:text-input="mode == 'day'"
|
||||||
:week-start="weekStart"
|
:week-start="weekStart"
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import LabelDay from '../../Base/Label/Day.js';
|
import LabelDay from '../../Base/Label/Day.js';
|
||||||
import LabelDow from '../../Base/Label/Dow.js';
|
import LabelDow from '../../Base/Label/Dow.js';
|
||||||
|
|
||||||
import CalDnd from '../../../../directives/Calendar/DragAndDrop.js';
|
import draggable from '../../../../directives/draggable.js';
|
||||||
|
|
||||||
import CalClick from '../../../../directives/Calendar/Click.js';
|
import CalClick from '../../../../directives/Calendar/Click.js';
|
||||||
|
|
||||||
// TODO(chris): drag and drop
|
// TODO(chris): drag and drop
|
||||||
@@ -13,7 +14,7 @@ export default {
|
|||||||
LabelDow
|
LabelDow
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
CalDnd,
|
draggable,
|
||||||
CalClick
|
CalClick
|
||||||
},
|
},
|
||||||
inject: {
|
inject: {
|
||||||
@@ -86,7 +87,7 @@ export default {
|
|||||||
v-else
|
v-else
|
||||||
class="event"
|
class="event"
|
||||||
:draggable="draggable(event)"
|
:draggable="draggable(event)"
|
||||||
v-cal-dnd:draggable="event"
|
v-draggable="event.orig"
|
||||||
v-cal-click:event="event.orig"
|
v-cal-click:event="event.orig"
|
||||||
>
|
>
|
||||||
<slot :event="event.orig" mode="list" />
|
<slot :event="event.orig" mode="list" />
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return events;
|
return events;
|
||||||
})
|
}),
|
||||||
|
draggableEvents: () => false,
|
||||||
|
resizableEvents: () => false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
inject: {
|
inject: {
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import BaseSlider from '../Base/Slider.js';
|
||||||
|
import TableView from './Table/View.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ModeTable",
|
||||||
|
components: {
|
||||||
|
BaseSlider,
|
||||||
|
TableView
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
currentDate: {
|
||||||
|
type: luxon.DateTime,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: [
|
||||||
|
"update:currentDate",
|
||||||
|
"update:range",
|
||||||
|
"click",
|
||||||
|
"requestModalOpen"
|
||||||
|
],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
focusDate: this.currentDate,
|
||||||
|
rangeOffset: 0
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
range() {
|
||||||
|
let first = this.focusDate.startOf('week', { useLocaleWeeks: true });
|
||||||
|
let last = this.focusDate.endOf('week', { useLocaleWeeks: true });
|
||||||
|
|
||||||
|
if (this.rangeOffset != 0) {
|
||||||
|
if (this.rangeOffset < 0) {
|
||||||
|
first = first.plus({ weeks: this.rangeOffset });
|
||||||
|
} else {
|
||||||
|
last = last.plus({ weeks: this.rangeOffset });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return luxon.Interval.fromDateTimes(first, last);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
currentDate() {
|
||||||
|
if (this.currentDate.locale != this.focusDate.locale) {
|
||||||
|
this.focusDate = this.currentDate;
|
||||||
|
this.$emit('update:range', this.range);
|
||||||
|
} else {
|
||||||
|
this.rangeOffset = this.currentDate.startOf('week', { useLocaleWeeks: true }).diff(this.focusDate.startOf('week', { useLocaleWeeks: true }), 'weeks').weeks;
|
||||||
|
if (this.rangeOffset) {
|
||||||
|
this.$emit('update:range', this.range);
|
||||||
|
this.$refs.slider.slidePages(this.rangeOffset).then(this.updatePage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
prevPage() {
|
||||||
|
this.rangeOffset = this.$refs.slider.target - 1;
|
||||||
|
this.$emit('update:range', this.range);
|
||||||
|
this.$refs.slider.prevPage().then(this.updatePage);
|
||||||
|
},
|
||||||
|
nextPage() {
|
||||||
|
this.rangeOffset = this.$refs.slider.target + 1;
|
||||||
|
this.$emit('update:range', this.range);
|
||||||
|
this.$refs.slider.nextPage().then(this.updatePage);
|
||||||
|
},
|
||||||
|
updatePage(weeks) {
|
||||||
|
const newFocusDate = this.focusDate.plus({ weeks });
|
||||||
|
this.focusDate = newFocusDate;
|
||||||
|
this.rangeOffset = 0;
|
||||||
|
this.$emit('update:currentDate', this.focusDate);
|
||||||
|
this.$emit('update:range', this.range);
|
||||||
|
},
|
||||||
|
viewAttrs(weeks) {
|
||||||
|
const day = this.focusDate.plus({ weeks });
|
||||||
|
return { ...this.$attrs, day };
|
||||||
|
},
|
||||||
|
handleClickDefaults(evt) {
|
||||||
|
|
||||||
|
switch (evt.detail.source) {
|
||||||
|
case 'day':
|
||||||
|
// default: Set current-date
|
||||||
|
this.$emit('update:currentDate', evt.detail.value);
|
||||||
|
break;
|
||||||
|
case 'event':
|
||||||
|
// default: Request Modal
|
||||||
|
this.$emit('requestModalOpen', { event: evt.detail.value });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$emit('update:range', this.range);
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div
|
||||||
|
class="fhc-calendar-mode-week flex-grow-1 position-relative"
|
||||||
|
@cal-click-default.capture="handleClickDefaults"
|
||||||
|
>
|
||||||
|
<base-slider ref="slider" v-slot="slot">
|
||||||
|
<table-view ref="view" v-bind="viewAttrs(slot.offset)">
|
||||||
|
<template v-slot="slot"><slot v-bind="slot" mode="week" /></template>
|
||||||
|
</table-view>
|
||||||
|
</base-slider>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
import {CoreFilterCmpt} from "../../../../components/filter/Filter.js";
|
||||||
|
import BsModal from '../../../Bootstrap/Modal.js';
|
||||||
|
import FormInput from "../../../Form/Input.js";
|
||||||
|
import ApiDetails from "../../../../api/lehrveranstaltung/details.js";
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "TableView",
|
||||||
|
inject: {
|
||||||
|
events: "events",
|
||||||
|
timezone: "timezone",
|
||||||
|
tableActions: "tableActions",
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
CoreFilterCmpt,
|
||||||
|
BsModal,
|
||||||
|
FormInput
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
day: {
|
||||||
|
type: luxon.DateTime,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
raumtyp_array: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
start() {
|
||||||
|
return this.day.startOf('week', { useLocaleWeeks: true });
|
||||||
|
},
|
||||||
|
preparedEvents() {
|
||||||
|
const end = this.start.plus({ days: 7 });
|
||||||
|
return this.events
|
||||||
|
.filter(e => e.start < end && e.end > this.start)
|
||||||
|
.sort((a, b) => a.start.ts - b.start.ts)
|
||||||
|
.map(event => ({
|
||||||
|
...event.orig,
|
||||||
|
row_index: event.id,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
tabulatorOptions() {
|
||||||
|
return {
|
||||||
|
index: "row_index",
|
||||||
|
layout: 'fitDataStretch',
|
||||||
|
placeholder: "Keine Daten verfügbar",
|
||||||
|
persistenceID: "2026_03_09_table_view_v1",
|
||||||
|
data: this.preparedEvents,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
formatter: 'rowSelection',
|
||||||
|
titleFormatter: 'rowSelection',
|
||||||
|
titleFormatterParams: {
|
||||||
|
rowRange: "active"
|
||||||
|
},
|
||||||
|
headerSort: false,
|
||||||
|
width: 40
|
||||||
|
},
|
||||||
|
{title: 'Datum', field: 'datum', headerFilter: "input", formatter: (cell) => {
|
||||||
|
let val = cell.getValue();
|
||||||
|
if (!val)
|
||||||
|
return ' ';
|
||||||
|
return luxon.DateTime.fromISO(val).toFormat('dd.MM.yyyy')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{title: 'Von', field: 'beginn', headerFilter: "input"},
|
||||||
|
{title: 'Bis', field: 'ende', headerFilter: "input"},
|
||||||
|
{title: 'Lehrfach', field: 'lehrfach', headerFilter: "input"},
|
||||||
|
{title: 'Bezeichnung', field: 'lehrfach_bez', headerFilter: "input"},
|
||||||
|
{title: 'Lehrform', field: 'lehrform', headerFilter: "input"},
|
||||||
|
{title: 'Raum', field: 'ort_kurzbz', headerFilter: "input"},
|
||||||
|
{
|
||||||
|
title: 'Lektor',
|
||||||
|
field: 'lektor',
|
||||||
|
headerFilter: "input",
|
||||||
|
mutator: (value) => {
|
||||||
|
if (!value)
|
||||||
|
return '';
|
||||||
|
return value.map(l => l.kurzbz).join(', ') ?? '–'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{title: 'OE', field: 'organisationseinheit', headerFilter: "input"},
|
||||||
|
{title: 'Status', field: 'status_kurzbz', headerFilter: "input"},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods:
|
||||||
|
{
|
||||||
|
openModal() {
|
||||||
|
this.$refs.raumModal.show();
|
||||||
|
},
|
||||||
|
async deleteSelected() {
|
||||||
|
let selected = this.$refs.tableViewTable?.tabulator?.getSelectedData() ?? [];
|
||||||
|
|
||||||
|
if (!selected.length) return;
|
||||||
|
|
||||||
|
let isConfirmed = await this.$fhcAlert.confirmDelete();
|
||||||
|
|
||||||
|
if (!isConfirmed) return;
|
||||||
|
|
||||||
|
await this.tableActions?.deleteEntries(selected)
|
||||||
|
|
||||||
|
this.$refs.tableViewTable.tabulator.deselectRow();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
preparedEvents(newData) {
|
||||||
|
this.$refs.tableViewTable?.tabulator?.setData(newData);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* mounted() {
|
||||||
|
|
||||||
|
this.$api.call(ApiDetails.getRaumtyp())
|
||||||
|
.then(result => {
|
||||||
|
this.raumtyp_array = result.data;
|
||||||
|
})
|
||||||
|
.catch(this.$fhcAlert.handleSystemError);
|
||||||
|
|
||||||
|
},*/
|
||||||
|
template: /* html */`
|
||||||
|
<div class="fhc-calendar-mode-table-view h-100 overflow-auto">
|
||||||
|
<core-filter-cmpt
|
||||||
|
ref="tableViewTable"
|
||||||
|
:tabulator-options="tabulatorOptions"
|
||||||
|
:table-only="true"
|
||||||
|
:side-menu="false"
|
||||||
|
:download="true"
|
||||||
|
>
|
||||||
|
<template #actions>
|
||||||
|
<button @click="deleteSelected" class="btn btn-outline-danger btn-sm">Löschen</button>
|
||||||
|
<button class="btn btn-outline-secondary btn-sm">Verschieben</button>
|
||||||
|
<button @click="openModal" class="btn btn-outline-secondary btn-sm">Raum wechsel</button>
|
||||||
|
</template>
|
||||||
|
</core-filter-cmpt>
|
||||||
|
|
||||||
|
<!--<bs-modal ref="raumModal" class="bootstrap-prompt" dialogClass="modal-lg">
|
||||||
|
<template #title>Raum verschiebung</template>
|
||||||
|
<form-input
|
||||||
|
:label="$p.t('lehre', 'raumtyp')"
|
||||||
|
type="select"
|
||||||
|
container-class="col-3"
|
||||||
|
name="raumtyp"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="raumtyp in raumtyp_array"
|
||||||
|
:value="raumtyp.raumtyp_kurzbz"
|
||||||
|
:key="raumtyp.raumtyp_kurzbz"
|
||||||
|
>
|
||||||
|
{{ raumtyp.raumtyp_kurzbz }} {{ raumtyp.beschreibung }}
|
||||||
|
</option>
|
||||||
|
</form-input>
|
||||||
|
<template #footer>
|
||||||
|
<button type="button" class="btn btn-primary">{{ $p.t('ui', 'speichern') }}</button>
|
||||||
|
</template>
|
||||||
|
</bs-modal>-->
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -13,7 +13,8 @@ export default {
|
|||||||
},
|
},
|
||||||
inject: {
|
inject: {
|
||||||
timeGrid: "timeGrid",
|
timeGrid: "timeGrid",
|
||||||
timezone: "timezone"
|
timezone: "timezone",
|
||||||
|
hoursPlan: { from: "hoursPlan", default: null },
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
day: {
|
day: {
|
||||||
@@ -39,8 +40,9 @@ export default {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// create 07:00-23:00
|
const start = this.hoursPlan?.start ?? 7;
|
||||||
return Array.from({ length: 17 }, (e, i) => luxon.Duration.fromObject({ hours: i + 7 }));
|
const end = this.hoursPlan?.end ?? 23;
|
||||||
|
return Array.from({ length: end - start + 1 }, (e, i) => luxon.Duration.fromObject({ hours: i + start }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,283 @@
|
|||||||
|
import FhcCalendar from "./Base.js";
|
||||||
|
|
||||||
|
import { useEventLoader } from '../../composables/EventLoader.js';
|
||||||
|
|
||||||
|
import ModeWeek from './Mode/Week.js';
|
||||||
|
import ModeMonth from './Mode/Month.js';
|
||||||
|
import ModeTable from './Mode/Table.js';
|
||||||
|
import ApiKalender from '../../api/factory/tempus/kalender.js';
|
||||||
|
import draggable from '../../directives/draggable.js';
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "CalendarTempus",
|
||||||
|
components: {
|
||||||
|
FhcCalendar
|
||||||
|
},
|
||||||
|
inject: {
|
||||||
|
renderers: {from: 'renderers'},
|
||||||
|
appConfig: {
|
||||||
|
from: 'appConfig',
|
||||||
|
default: {
|
||||||
|
visible_status: 'all'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
draggable,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
timezone: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
date: {
|
||||||
|
type: [Date, String, Number, luxon.DateTime],
|
||||||
|
default: luxon.DateTime.local()
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: 'Week'
|
||||||
|
},
|
||||||
|
getPromiseFunc: {
|
||||||
|
type: Function,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
parkedEvents: {
|
||||||
|
type: Object,
|
||||||
|
default: () => new Set()
|
||||||
|
},
|
||||||
|
visibleLecturers: {
|
||||||
|
type: Array,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
extraBackgrounds: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
visibleStatus: {
|
||||||
|
type: Array,
|
||||||
|
default: () => ['all']
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: [
|
||||||
|
"update:date",
|
||||||
|
"update:mode",
|
||||||
|
"update:range",
|
||||||
|
"drop",
|
||||||
|
"resize",
|
||||||
|
"event-hover",
|
||||||
|
"event-unhover"
|
||||||
|
],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
modes: {
|
||||||
|
week: Vue.markRaw(ModeWeek),
|
||||||
|
month: Vue.markRaw(ModeMonth),
|
||||||
|
tableList: Vue.markRaw(ModeTable),
|
||||||
|
},
|
||||||
|
modeOptions: {
|
||||||
|
day: {
|
||||||
|
emptyMessage: Vue.computed(() => this.$p.t('lehre/noLvFound')),
|
||||||
|
emptyMessageDetails: Vue.computed(() => this.$p.t('lehre/noLvFound'))
|
||||||
|
},
|
||||||
|
week: {
|
||||||
|
collapseEmptyDays: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
currentMode: this.mode,
|
||||||
|
teachingunits: null,
|
||||||
|
hoursplan: null,
|
||||||
|
showRaster: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
backgrounds() {
|
||||||
|
let now = luxon.DateTime.now().setZone(this.timezone);
|
||||||
|
|
||||||
|
let past = [];
|
||||||
|
if (this.mode == 'Month')
|
||||||
|
{
|
||||||
|
past = [{
|
||||||
|
class: 'background-past',
|
||||||
|
end: now.startOf('day')
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
past = [{
|
||||||
|
class: 'background-past',
|
||||||
|
end: now,
|
||||||
|
label: now.startOf('minute').toISOTime({ suppressSeconds: true, includeOffset: false })
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
...past,
|
||||||
|
...(this.extraBackgrounds || [])
|
||||||
|
];
|
||||||
|
},
|
||||||
|
visibleEvents()
|
||||||
|
{
|
||||||
|
let list = this.events;
|
||||||
|
|
||||||
|
if (Array.isArray(this.visibleLecturers))
|
||||||
|
{
|
||||||
|
const visibleLectures = new Set(this.visibleLecturers);
|
||||||
|
|
||||||
|
list = list.filter(event => {
|
||||||
|
if (!event.lektor?.length)
|
||||||
|
return true;
|
||||||
|
return event.lektor.some(lektor => visibleLectures.has(lektor.mitarbeiter_uid));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.visibleStatus.length || this.visibleStatus.includes('all'))
|
||||||
|
return list;
|
||||||
|
|
||||||
|
return list.filter(event => this.visibleStatus.includes(event.status_kurzbz));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
eventStyle(event) {
|
||||||
|
if (!event.farbe)
|
||||||
|
return undefined;
|
||||||
|
return '--event-bg:#' + event.farbe;
|
||||||
|
},
|
||||||
|
updateRange(rangeInterval) {
|
||||||
|
this.rangeInterval = rangeInterval;
|
||||||
|
this.$emit('update:range', rangeInterval);
|
||||||
|
},
|
||||||
|
ondrop(payload){
|
||||||
|
this.$emit('drop', payload);
|
||||||
|
},
|
||||||
|
onresize(payload){
|
||||||
|
this.$emit('resize', payload);
|
||||||
|
},
|
||||||
|
resetEventLoader() {
|
||||||
|
this.reset();
|
||||||
|
},
|
||||||
|
navigatePrev() {
|
||||||
|
this.$refs.calendar.clickPrev();
|
||||||
|
},
|
||||||
|
navigateNext() {
|
||||||
|
this.$refs.calendar.clickNext();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props, context) {
|
||||||
|
const rangeInterval = Vue.ref(null);
|
||||||
|
|
||||||
|
const { events, lv, reset } = useEventLoader(rangeInterval, props.getPromiseFunc);
|
||||||
|
|
||||||
|
Vue.watch(lv, newValue => {
|
||||||
|
context.emit('update:lv', newValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
rangeInterval,
|
||||||
|
events,
|
||||||
|
lv,
|
||||||
|
reset
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.$api
|
||||||
|
.call(ApiKalender.getStunden())
|
||||||
|
.then(res => {
|
||||||
|
return this.teachingunits = res.data.map(el => ({
|
||||||
|
id: el.stunde,
|
||||||
|
start: el.beginn,
|
||||||
|
end: el.ende
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$api
|
||||||
|
.call(ApiKalender.getCalendarHours())
|
||||||
|
.then(res => {
|
||||||
|
this.hoursplan = {
|
||||||
|
start: res.data.start,
|
||||||
|
end: res.data.end
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
template: /* html */`
|
||||||
|
<fhc-calendar
|
||||||
|
ref="calendar"
|
||||||
|
class="fhc-calendar-lvplan"
|
||||||
|
:date="date"
|
||||||
|
:modes="modes"
|
||||||
|
:mode-options="modeOptions"
|
||||||
|
:mode="mode"
|
||||||
|
:timezone="timezone"
|
||||||
|
:locale="$p.user_locale.value"
|
||||||
|
:events="visibleEvents || []"
|
||||||
|
:backgrounds="backgrounds"
|
||||||
|
:time-grid="showRaster ? teachingunits : null"
|
||||||
|
:hours-plan="hoursplan"
|
||||||
|
show-btns
|
||||||
|
:draggable-events="true"
|
||||||
|
:resizable-events="true"
|
||||||
|
:on-drop="currentMode === 'week' ? ondrop : null"
|
||||||
|
:on-resize="onresize"
|
||||||
|
@update:date="(newDate, newMode) => $emit('update:date', newDate, newMode)"
|
||||||
|
@update:mode="(newMode, newDate) => { currentMode = newMode; $emit('update:mode', newMode, newDate) }"
|
||||||
|
@update:range="updateRange"
|
||||||
|
>
|
||||||
|
<template v-slot="{ event, mode }">
|
||||||
|
<div
|
||||||
|
:class="['event-type-' + event.type + ' ' + mode + 'PageContainer', { 'event--parked': parkedEvents.has(String(event.kalender_id)) }]"
|
||||||
|
:type="mode == 'day' ? 'button' : undefined"
|
||||||
|
:style="eventStyle(event)"
|
||||||
|
@mouseenter="$emit('event-hover', event)"
|
||||||
|
@mouseleave="$emit('event-unhover', event)"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
v-if="mode == 'event'"
|
||||||
|
:is="renderers[event.type]?.modalContent"
|
||||||
|
:event="event"
|
||||||
|
></component>
|
||||||
|
<component
|
||||||
|
v-else-if="mode == 'eventheader'"
|
||||||
|
:is="renderers[event.type]?.modalTitle"
|
||||||
|
:event="event"
|
||||||
|
></component>
|
||||||
|
<component
|
||||||
|
v-else
|
||||||
|
:is="renderers[event.type]?.calendarEvent"
|
||||||
|
:event="event"
|
||||||
|
></component>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #actions>
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<div
|
||||||
|
class="d-flex align-items-center gap-2"
|
||||||
|
style="cursor:pointer"
|
||||||
|
@click="showRaster = !showRaster"
|
||||||
|
>
|
||||||
|
<i :class="showRaster ? 'fa-solid fa-toggle-on text-primary' : 'fa-solid fa-toggle-off text-muted'"></i>
|
||||||
|
<span class="form-check-label">Stundenraster</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="d-flex align-items-center gap-2 "
|
||||||
|
v-draggable:move.noimage="{ type: 'reservierung', id: null, orig: {} }"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa-solid fa-calendar-plus text-primary"
|
||||||
|
style="cursor:pointer"
|
||||||
|
@click.stop="$emit('open-reservierung')"
|
||||||
|
></i>
|
||||||
|
<span>Reservierung</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<i :class="appConfig.ignore_kollision ? 'fa-solid fa-triangle-exclamation text-danger' : 'fa-solid fa-circle-check text-success'"></i>
|
||||||
|
<span class="form-check-label">
|
||||||
|
{{ appConfig.ignore_kollision ? 'Kollisionscheck aus' : 'Kollisionscheck an' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</fhc-calendar>`
|
||||||
|
}
|
||||||
@@ -131,7 +131,7 @@ export default {
|
|||||||
},
|
},
|
||||||
onSelectTreeNode(node) {
|
onSelectTreeNode(node) {
|
||||||
if (node.data.link)
|
if (node.data.link)
|
||||||
this.$emit('selectVerband', {link: node.data.link, studiengang_kz: node.data.stg_kz, semester: node.data.semester, orgform_kurzbz: node.data.orgform_kurzbz});
|
this.$emit('selectVerband', {link: node.data.link, studiengang_kz: node.data.stg_kz, semester: node.data.semester, orgform_kurzbz: node.data.orgform_kurzbz, name: node.data.name});
|
||||||
},
|
},
|
||||||
mapNodesToNoSemReloadNodes(result, node) {
|
mapNodesToNoSemReloadNodes(result, node) {
|
||||||
if (node.data.no_sem_reload)
|
if (node.data.no_sem_reload)
|
||||||
|
|||||||
@@ -0,0 +1,335 @@
|
|||||||
|
import FormInput from '../Form/Input.js';
|
||||||
|
import draggable from "../../directives/draggable.js";
|
||||||
|
import ApiCoursePicker from '../../api/factory/tempus/coursepicker.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
FormInput
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
draggable
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
studiengaenge: {
|
||||||
|
type: Array,
|
||||||
|
default: [],
|
||||||
|
},
|
||||||
|
studiensemester: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['select-lecturer', 'select-kw'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
searchparam: '',
|
||||||
|
allCourses: [],
|
||||||
|
sortBy: null,
|
||||||
|
multiWeekIds: new Set(),
|
||||||
|
favorites: new Set()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
courses() {
|
||||||
|
const query = (this.searchparam ?? '').trim().toLowerCase();
|
||||||
|
|
||||||
|
let result;
|
||||||
|
if (query)
|
||||||
|
{
|
||||||
|
result = this.allCourses.filter(course =>
|
||||||
|
course.showname.toLowerCase().includes(query) ||
|
||||||
|
course.lektoren?.some(lektor =>
|
||||||
|
lektor.name.toLowerCase().includes(query) ||
|
||||||
|
lektor.kurzbz.toLowerCase().includes(query)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result = this.allCourses;
|
||||||
|
|
||||||
|
if (this.sortBy === 'lektor-asc' || this.sortBy === 'lektor-desc')
|
||||||
|
{
|
||||||
|
let dir = this.sortBy === 'lektor-asc' ? 1 : -1;
|
||||||
|
|
||||||
|
result = result.sort((a, b) => {
|
||||||
|
let an = a.lektoren?.[0]?.kurzbz ?? '';
|
||||||
|
let bn = b.lektoren?.[0]?.kurzbz ?? '';
|
||||||
|
return an.localeCompare(bn) * dir;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (this.sortBy === 'kw-asc')
|
||||||
|
{
|
||||||
|
result = result.sort((a, b) => (a.start_kw ?? 0) - (b.start_kw ?? 0));
|
||||||
|
}
|
||||||
|
else if (this.sortBy === 'kw-desc')
|
||||||
|
{
|
||||||
|
result = result.sort((a, b) => (b.start_kw ?? 0) - (a.start_kw ?? 0));
|
||||||
|
}
|
||||||
|
else if (this.sortBy === 'stunden-asc')
|
||||||
|
{
|
||||||
|
result = result.sort((a, b) => (a.offenestunden ?? 0) - (b.offenestunden ?? 0));
|
||||||
|
}
|
||||||
|
else if (this.sortBy === 'stunden-desc')
|
||||||
|
{
|
||||||
|
result = result.sort((a, b) => (b.offenestunden ?? 0) - (a.offenestunden ?? 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...result].sort((a, b) => {
|
||||||
|
let ap = this.isPinned(a) ? 1 : 0;
|
||||||
|
let bp = this.isPinned(b) ? 1 : 0;
|
||||||
|
return bp - ap;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
studiengaenge: {
|
||||||
|
deep: true,
|
||||||
|
handler(val) {
|
||||||
|
this.searchparam = '';
|
||||||
|
this.loadCoursesByStg(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
studiensemester() {
|
||||||
|
if (this.studiengaenge)
|
||||||
|
this.loadCoursesByStg(this.studiengaenge);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadCoursesByStg(stg) {
|
||||||
|
if (stg.length <= 0)
|
||||||
|
{
|
||||||
|
this.allCourses = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload = stg.map(({ studiengang_kz, semester, orgform_kurzbz }) => ({
|
||||||
|
studiengang_kz,
|
||||||
|
semester,
|
||||||
|
orgform_kurzbz
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.$api.call(ApiCoursePicker.getByStg(payload, this.studiensemester))
|
||||||
|
.then(result => {
|
||||||
|
this.allCourses = result.data.map(e => ({
|
||||||
|
lehreinheit_id: e.lehreinheit_id,
|
||||||
|
lektoren: e.lektoren,
|
||||||
|
raumtyp: e.raumtyp,
|
||||||
|
raumtypalternativ: e.raumtypalternativ,
|
||||||
|
semesterstunden: e.planstunden,
|
||||||
|
stundenblockung: e.stundenblockung,
|
||||||
|
wochenrythmus: e.wochenrythmus,
|
||||||
|
offenestunden: e.offenestunden,
|
||||||
|
start_kw: e.start_kw,
|
||||||
|
anmerkung: e.anmerkung,
|
||||||
|
lehrfach: e.lehrfach,
|
||||||
|
lehrform: e.lehrform,
|
||||||
|
lehrfach_bez: e.lehrfach_bez,
|
||||||
|
lehrfach_farbe: e.lehrfach_farbe,
|
||||||
|
lehrverband: e.lehrverband,
|
||||||
|
showname: `${e.lehrfach} ${e.lehrform}`,
|
||||||
|
orig: {
|
||||||
|
type: 'lehreinheit',
|
||||||
|
lehreinheit_id: e.lehreinheit_id,
|
||||||
|
blockung: e.stundenblockung,
|
||||||
|
entry: e,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
.catch(this.$fhcAlert.handleSystemError);
|
||||||
|
},
|
||||||
|
dragLehreinheitCollection(course) {
|
||||||
|
const orig = course.orig;
|
||||||
|
return {
|
||||||
|
type: 'lehreinheit',
|
||||||
|
id: orig.lehreinheit_id,
|
||||||
|
orig: orig,
|
||||||
|
stundenblockung: course.stundenblockung,
|
||||||
|
multiweek: this.isMultiWeek(course),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
courseStyle(course) {
|
||||||
|
if (!course.lehrfach_farbe)
|
||||||
|
return {};
|
||||||
|
return '--event-bg:#' + course.lehrfach_farbe;
|
||||||
|
|
||||||
|
},
|
||||||
|
selectLecturer(lektor) {
|
||||||
|
this.$emit('select-lecturer', lektor);
|
||||||
|
},
|
||||||
|
setSort(field) {
|
||||||
|
if (this.sortBy === `${field}-asc`)
|
||||||
|
this.sortBy = `${field}-desc`;
|
||||||
|
else if (this.sortBy === `${field}-desc`)
|
||||||
|
this.sortBy = null;
|
||||||
|
else
|
||||||
|
this.sortBy = `${field}-asc`;
|
||||||
|
},
|
||||||
|
sortIcon(field) {
|
||||||
|
if (this.sortBy === `${field}-asc`)
|
||||||
|
return 'fa-arrow-up';
|
||||||
|
if (this.sortBy === `${field}-desc`)
|
||||||
|
return 'fa-arrow-down';
|
||||||
|
return 'fa-sort';
|
||||||
|
},
|
||||||
|
isSortActive(field) {
|
||||||
|
return this.sortBy === `${field}-asc` || this.sortBy === `${field}-desc`;
|
||||||
|
},
|
||||||
|
reload()
|
||||||
|
{
|
||||||
|
this.loadCoursesByStg(this.studiengaenge);
|
||||||
|
},
|
||||||
|
toggleMultiWeek(course)
|
||||||
|
{
|
||||||
|
let id = course.lehreinheit_id;
|
||||||
|
let weekIds = new Set(this.multiWeekIds);
|
||||||
|
if (weekIds.has(id))
|
||||||
|
weekIds.delete(id);
|
||||||
|
else
|
||||||
|
weekIds.add(id);
|
||||||
|
|
||||||
|
this.multiWeekIds = weekIds;
|
||||||
|
},
|
||||||
|
isMultiWeek(course)
|
||||||
|
{
|
||||||
|
return this.multiWeekIds.has(course.lehreinheit_id);
|
||||||
|
},
|
||||||
|
togglePin(course)
|
||||||
|
{
|
||||||
|
let id = course.lehreinheit_id;
|
||||||
|
let favs = new Set(this.favorites);
|
||||||
|
if (favs.has(id))
|
||||||
|
favs.delete(id);
|
||||||
|
else
|
||||||
|
favs.add(id);
|
||||||
|
|
||||||
|
this.favorites = favs;
|
||||||
|
localStorage.setItem('tempus_coursepicker_favs', JSON.stringify([...favs]));
|
||||||
|
},
|
||||||
|
isPinned(course)
|
||||||
|
{
|
||||||
|
return this.favorites.has(course.lehreinheit_id);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.favorites = new Set(JSON.parse(localStorage.getItem('tempus_coursepicker_favs') || '[]'));
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div class="course-picker d-flex flex-column h-100">
|
||||||
|
<div class="p-2">
|
||||||
|
<form-input
|
||||||
|
:label="$p.t('ui', 'suche')"
|
||||||
|
type="text"
|
||||||
|
v-model="searchparam"
|
||||||
|
/>
|
||||||
|
<div class="d-flex gap-1 mt-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-outline-secondary"
|
||||||
|
:disabled="studiengaenge.length <= 0"
|
||||||
|
@click="reload">
|
||||||
|
<i class="fa fa-rotate-right"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm"
|
||||||
|
:class="isSortActive('lektor') ? 'btn-primary' : 'btn-outline-secondary'"
|
||||||
|
@click="setSort('lektor')">
|
||||||
|
Lkt <i class="fa" :class="sortIcon('lektor')"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm"
|
||||||
|
:class="isSortActive('kw') ? 'btn-primary' : 'btn-outline-secondary'"
|
||||||
|
@click="setSort('kw')">
|
||||||
|
KW <i class="fa" :class="sortIcon('kw')"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm"
|
||||||
|
:class="isSortActive('stunden') ? 'btn-primary' : 'btn-outline-secondary'"
|
||||||
|
@click="setSort('stunden')">
|
||||||
|
Std <i class="fa" :class="sortIcon('stunden')"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="studiengaenge.length <= 0" class="d-flex flex-column align-items-center justify-content-center text-center text-muted py-5 px-3 h-100">
|
||||||
|
<span class="small fw-semibold mb-1">{{$p.t('lehre', 'cptitleempty')}}</span>
|
||||||
|
<span class="small">{{$p.t('lehre', 'cpempty')}}</span>
|
||||||
|
</div>
|
||||||
|
<div v-else class="overflow-auto px-2 pb-2 flex-grow-1">
|
||||||
|
<div
|
||||||
|
v-for="course in courses"
|
||||||
|
:key="course.lehreinheit_id"
|
||||||
|
style="cursor:grab"
|
||||||
|
:style="courseStyle(course)"
|
||||||
|
class="course-picker-row rounded-3 mb-2 p-2"
|
||||||
|
:class="{ 'course-picker-row-pinned': isPinned(course) }"
|
||||||
|
v-draggable:move.noimage="dragLehreinheitCollection(course)"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div class="d-flex gap-1 align-items-start">
|
||||||
|
<span class="fw-semibold small w-50" v-tooltip="course.lehrfach_bez">{{ course.lehrfach }} {{ course.lehrform }}</span>
|
||||||
|
<span class="fw-semibold small w-50" v-tooltip="course.raumtypalternativ">{{ course.raumtyp }}</span>
|
||||||
|
<i
|
||||||
|
class="fa fa-thumbtack"
|
||||||
|
:class="isPinned(course) ? 'text-primary' : 'text-muted'"
|
||||||
|
style="cursor:pointer"
|
||||||
|
@click.stop="togglePin(course)"
|
||||||
|
></i>
|
||||||
|
<i
|
||||||
|
class="fa fa-calendar-week"
|
||||||
|
:class="isMultiWeek(course) ? 'text-primary' : 'text-muted'"
|
||||||
|
style="cursor:pointer"
|
||||||
|
v-tooltip="isMultiWeek(course) ? 'MultiWeek aktiv' : 'MultiWeek Verplanung aktivieren'"
|
||||||
|
@click.stop="toggleMultiWeek(course)"
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--TODO(david) entfernen, dient nur für das mappen mit der lvverwaltung-->
|
||||||
|
<div class="d-flex gap-1">
|
||||||
|
<span class="small w-50" v-tooltip="course.lehreinheit_id">{{ course.lehreinheit_id }} </span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-1 text-muted">
|
||||||
|
<div class="w-50 d-flex flex-column text-truncate" v-tooltip="course.anmerkung">
|
||||||
|
<span
|
||||||
|
v-for="verband in course.lehrverband"
|
||||||
|
:key="verband"
|
||||||
|
class="text-truncate">
|
||||||
|
{{ verband }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
style="cursor:pointer"
|
||||||
|
class="text-decoration-underline w-50"
|
||||||
|
@click.stop="$emit('select-kw', course.start_kw)">KW: {{ course.start_kw }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-1 text-muted">
|
||||||
|
<div class="w-50 d-flex flex-column"
|
||||||
|
v-tooltip="course.lektoren.length > 3 ? course.lektoren.map(l => l.kurzbz).join(', ') : null">
|
||||||
|
<span
|
||||||
|
v-for="lektor in course.lektoren.slice(0, 3)"
|
||||||
|
:key="lektor.uid"
|
||||||
|
style="cursor:pointer"
|
||||||
|
class="text-decoration-underline"
|
||||||
|
@click.stop="selectLecturer(lektor)">
|
||||||
|
{{ lektor.kurzbz }}
|
||||||
|
</span>
|
||||||
|
<span v-if="course.lektoren.length > 3" class="text-muted fst-italic">
|
||||||
|
+{{ course.lektoren.length - 3 }} {{$p.t('lehre', 'cpmore')}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span class="w-50 align-self-start">WR: {{ course.wochenrythmus }} Bl: {{ course.stundenblockung }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-1 text-muted">
|
||||||
|
<span class="w-50">Offen: {{ course.offenestunden }}</span>
|
||||||
|
<span class="w-50">{{ course.semesterstunden }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
export default {
|
||||||
|
name: "LectureSelection",
|
||||||
|
props: {
|
||||||
|
lecturers: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['remove'],
|
||||||
|
template: `
|
||||||
|
<div class="lecture-selection">
|
||||||
|
<div class="room-selection px-2" >
|
||||||
|
<div class="d-flex align-items-center justify-content-between">
|
||||||
|
<span class="fw-semibold"><i class="fa-solid fa-user me-2"></i>Lektoren</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-link text-danger p-0"
|
||||||
|
@click="$emit('remove')"
|
||||||
|
title="Alle Lektoren entfernen"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-xmark"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-for="l in lecturers" :key="l.uid">
|
||||||
|
<div class="fw-semibold pt-2 d-flex align-items-center justify-content-between">
|
||||||
|
{{ l.label }}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-link text-danger p-0"
|
||||||
|
@click="$emit('remove', l.uid)"
|
||||||
|
title="Lektor entfernen"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-xmark"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="overflow-auto flex-grow-1 pb-2">
|
||||||
|
<div class="d-flex align-items-center gap-2" @click="l.showEvents = !l.showEvents" style="cursor:pointer">
|
||||||
|
<i :class="l.showEvents ? 'fa-solid fa-toggle-on text-primary' : 'fa-solid fa-toggle-off text-muted'"></i>
|
||||||
|
<span class="form-check-label">Plan</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center gap-2" @click="l.overlays.blocks = !l.overlays.blocks" style="cursor:pointer">
|
||||||
|
<i :class="l.overlays.blocks ? 'fa-solid fa-toggle-on text-primary' : 'fa-solid fa-toggle-off text-muted'"></i>
|
||||||
|
<span class="form-check-label">Zeitsperren</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center gap-2" @click="l.overlays.wishes = !l.overlays.wishes" style="cursor:pointer">
|
||||||
|
<i :class="l.overlays.wishes ? 'fa-solid fa-toggle-on text-primary' : 'fa-solid fa-toggle-off text-muted'"></i>
|
||||||
|
<span class="form-check-label">Zeitwünsche</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
export default {
|
||||||
|
name: "RoomSelection",
|
||||||
|
props: {
|
||||||
|
rooms: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['update:rooms'],
|
||||||
|
methods: {
|
||||||
|
removeRoom(ort_kurzbz)
|
||||||
|
{
|
||||||
|
if (ort_kurzbz == null)
|
||||||
|
{
|
||||||
|
this.$emit('update:rooms', []);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.$emit('update:rooms', this.rooms.filter(room => room.ort_kurzbz !== ort_kurzbz));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div class="room-selection px-2">
|
||||||
|
<div class="d-flex align-items-center justify-content-between">
|
||||||
|
<span class="fw-semibold"><i class="fa-solid fa-door-open me-2"></i>Räume</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-link text-danger p-0"
|
||||||
|
@click="removeRoom(null)"
|
||||||
|
title="Alle Räume entfernen"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-xmark"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ul class="list-unstyled mb-0">
|
||||||
|
<li
|
||||||
|
v-for="room in rooms"
|
||||||
|
:key="room.ort_kurzbz"
|
||||||
|
class="d-flex align-items-center justify-content-between"
|
||||||
|
>
|
||||||
|
<span>{{ room.ort_kurzbz }}</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-link text-danger p-0"
|
||||||
|
@click="removeRoom(room.ort_kurzbz)"
|
||||||
|
title="Raum entfernen"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-xmark"></i>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
export default {
|
||||||
|
name: "VerbandSelection",
|
||||||
|
props: {
|
||||||
|
studiengaenge: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
studiengaengeAll: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['update:studiengaenge'],
|
||||||
|
methods: {
|
||||||
|
removeStg(stg_kz, semester, orgform_kurzbz)
|
||||||
|
{
|
||||||
|
if (stg_kz == null)
|
||||||
|
{
|
||||||
|
this.$emit('update:studiengaenge', []);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.$emit('update:studiengaenge', this.studiengaenge.filter(studiengang => !(studiengang.studiengang_kz === stg_kz && studiengang.semester === semester && studiengang.orgform_kurzbz === orgform_kurzbz)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stgLabel(stg)
|
||||||
|
{
|
||||||
|
let found = this.studiengaengeAll.find(s => s.studiengang_kz == stg.studiengang_kz);
|
||||||
|
let name = found?.name ?? stg.studiengang_kz;
|
||||||
|
|
||||||
|
if (stg.orgform_kurzbz)
|
||||||
|
return `${name} - ${stg.orgform_kurzbz}`;
|
||||||
|
|
||||||
|
return stg.semester ? `${name} - ${stg.semester}. Semester` : name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div class="room-selection px-2">
|
||||||
|
<div class="d-flex align-items-center justify-content-between">
|
||||||
|
<span class="fw-semibold"><i class="fa-solid fa-door-open me-2"></i>Studiengänge</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-link text-danger p-0"
|
||||||
|
@click="removeStg(null)"
|
||||||
|
title="Alle Studiengänge entfernen"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-xmark"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ul class="list-unstyled mb-0">
|
||||||
|
<li
|
||||||
|
v-for="studiengang in studiengaenge"
|
||||||
|
class="d-flex align-items-center justify-content-between"
|
||||||
|
>
|
||||||
|
<span>{{ stgLabel(studiengang) }}</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-link text-danger p-0"
|
||||||
|
@click="removeStg(studiengang.studiengang_kz, studiengang.semester, studiengang.orgform_kurzbz)"
|
||||||
|
title="Studiengang entfernen"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-xmark"></i>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
export function getTempusSearchbarOptions(self)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
origin: 'tempus',
|
||||||
|
cssclass: "position-relative",
|
||||||
|
calcheightonly: true,
|
||||||
|
types: [
|
||||||
|
//"student",
|
||||||
|
"raum",
|
||||||
|
"mitarbeiter",
|
||||||
|
"mitarbeiter_ohne_zuordnung"
|
||||||
|
],
|
||||||
|
actions: {
|
||||||
|
raum: {
|
||||||
|
defaultaction: {
|
||||||
|
type: "function",
|
||||||
|
action: self.setOrt
|
||||||
|
},
|
||||||
|
childactions: [
|
||||||
|
{
|
||||||
|
label: "zum Filter hinzufügen",
|
||||||
|
icon: "fas fa-plus",
|
||||||
|
type: "function",
|
||||||
|
action: (data) => {
|
||||||
|
self.addToFilter(data, 'ort');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
employee: {
|
||||||
|
defaultaction: {
|
||||||
|
type: "function",
|
||||||
|
action: (data) => {
|
||||||
|
self.setEmp(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
childactions: [
|
||||||
|
{
|
||||||
|
label: "zum Filter hinzufügen",
|
||||||
|
icon: "fas fa-plus",
|
||||||
|
type: "function",
|
||||||
|
action: (data) => {
|
||||||
|
self.addToFilter(data, 'mitarbeiter');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
export default {
|
||||||
|
name: "KeyboardShortcuts",
|
||||||
|
props: {
|
||||||
|
shortcuts: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isInputFocused(target)
|
||||||
|
{
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target.isContentEditable)
|
||||||
|
},
|
||||||
|
shortcutMatches(shortcut, e)
|
||||||
|
{
|
||||||
|
let shortcutWantsCtrl = shortcut.ctrl === true;
|
||||||
|
let ctrlIsPressed = e.ctrlKey || e.metaKey;
|
||||||
|
|
||||||
|
if (shortcutWantsCtrl !== ctrlIsPressed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (e.key !== shortcut.key)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let shouldSkipWhenTyping = shortcut.skipWhenTyping !== false;
|
||||||
|
return !(shouldSkipWhenTyping && this.isInputFocused(e.target));
|
||||||
|
},
|
||||||
|
handleKeydown(e)
|
||||||
|
{
|
||||||
|
for (let shortcut of this.shortcuts)
|
||||||
|
{
|
||||||
|
if (!this.shortcutMatches(shortcut, e))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let shouldPreventDefault = shortcut.preventDefault !== false;
|
||||||
|
if (shouldPreventDefault)
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
shortcut.handler(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
window.addEventListener('keydown', this.handleKeydown);
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
window.removeEventListener('keydown', this.handleKeydown);
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,340 @@
|
|||||||
|
import BsModal from '../Bootstrap/Modal.js';
|
||||||
|
import CoreForm from '../Form/Form.js';
|
||||||
|
import FormValidation from '../Form/Validation.js';
|
||||||
|
import FormInput from '../Form/Input.js';
|
||||||
|
|
||||||
|
import ApiTempusSync from '../../api/factory/tempus/sync.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
BsModal,
|
||||||
|
CoreForm,
|
||||||
|
FormValidation,
|
||||||
|
FormInput
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
config: Object,
|
||||||
|
stsem_kurzbz: String
|
||||||
|
},
|
||||||
|
emits: [
|
||||||
|
'saved'
|
||||||
|
],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
mode: 'new',
|
||||||
|
formData: {},
|
||||||
|
studienplaene: [],
|
||||||
|
max_semester: null,
|
||||||
|
syncStati: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
semester() {
|
||||||
|
if (!this.max_semester)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return Array.from({length: this.max_semester}, (_, index) => index);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'formData.oe_kurzbz'(newOrganisation) {
|
||||||
|
if (!newOrganisation) {
|
||||||
|
this.max_semester = null;
|
||||||
|
this.studienplaene = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let filtered_org = this.config.organisationen.filter(org => org.oe_kurzbz === newOrganisation)[0];
|
||||||
|
|
||||||
|
if (filtered_org && filtered_org.studiengang_kz)
|
||||||
|
this.getSemester(filtered_org.studiengang_kz);
|
||||||
|
else
|
||||||
|
this.max_semester = null;
|
||||||
|
|
||||||
|
this.getStudienplan();
|
||||||
|
},
|
||||||
|
'formData.studiensemester_kurzbz'() {
|
||||||
|
this.getStudienplan();
|
||||||
|
},
|
||||||
|
'formData.ausbildungssemester'() {
|
||||||
|
this.getStudienplan();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
openNew() {
|
||||||
|
this.$refs.modal.hide();
|
||||||
|
|
||||||
|
this.mode = 'new';
|
||||||
|
this.max_semester = null;
|
||||||
|
this.studienplaene = [];
|
||||||
|
this.formData = {
|
||||||
|
oe_kurzbz: null,
|
||||||
|
studiensemester_kurzbz: this.stsem_kurzbz ?? null,
|
||||||
|
datum_bis: null,
|
||||||
|
studienplan_id: null,
|
||||||
|
ausbildungssemester: null,
|
||||||
|
sync_status_kurzbz: null,
|
||||||
|
mail: false
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$refs.form.clearValidation()
|
||||||
|
this.$refs.modal.show()
|
||||||
|
},
|
||||||
|
openEdit(entry) {
|
||||||
|
if (!entry || entry.kalender_syncstatus_id === undefined || entry.kalender_syncstatus_id === null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$refs.modal.hide();
|
||||||
|
this.mode = 'edit';
|
||||||
|
|
||||||
|
this.$api
|
||||||
|
.call(ApiTempusSync.loadSync(entry.kalender_syncstatus_id))
|
||||||
|
.then(result => {
|
||||||
|
this.formData = result.data;
|
||||||
|
return this.$refs.form.clearValidation();
|
||||||
|
})
|
||||||
|
.then(() => this.$refs.modal.show())
|
||||||
|
.catch(this.$fhcAlert.handleSystemError);
|
||||||
|
},
|
||||||
|
|
||||||
|
openStart() {
|
||||||
|
this.$refs.modal.hide();
|
||||||
|
|
||||||
|
this.mode = 'start';
|
||||||
|
this.max_semester = null;
|
||||||
|
this.studienplaene = [];
|
||||||
|
this.formData = {
|
||||||
|
oe_kurzbz: null,
|
||||||
|
studiensemester_kurzbz: this.stsem_kurzbz ?? null,
|
||||||
|
datum_bis: null,
|
||||||
|
sync_status_kurzbz: null,
|
||||||
|
studienplan_id: null,
|
||||||
|
ausbildungssemester: null,
|
||||||
|
mail: false
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$refs.form.clearValidation()
|
||||||
|
this.$refs.modal.show()
|
||||||
|
},
|
||||||
|
getSemester(stg_kz) {
|
||||||
|
this.$api.call(ApiTempusSync.getMaxSemester([stg_kz]))
|
||||||
|
.then(response => response.data)
|
||||||
|
.then(response => {
|
||||||
|
this.max_semester = response;
|
||||||
|
|
||||||
|
let found = this.semester.filter(semester => semester === this.formData.ausbildungssemester);
|
||||||
|
if (found.length === 0)
|
||||||
|
this.formData.ausbildungssemester = null;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.$fhcAlert.handleSystemError(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getStudienplan() {
|
||||||
|
if (!this.formData.oe_kurzbz || !this.formData.studiensemester_kurzbz) {
|
||||||
|
this.studienplaene = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$api.call(ApiTempusSync.getStudienplan(
|
||||||
|
this.formData.oe_kurzbz,
|
||||||
|
this.formData.studiensemester_kurzbz,
|
||||||
|
this.formData.ausbildungssemester ?? null
|
||||||
|
))
|
||||||
|
.then(response => response.data)
|
||||||
|
.then(response => {
|
||||||
|
this.studienplaene = response;
|
||||||
|
let found = this.studienplaene.filter(plan => plan.studienplan_id === this.formData.studienplan_id);
|
||||||
|
if (found.length === 0)
|
||||||
|
this.formData.studienplan_id = null;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.$fhcAlert.handleSystemError(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
save() {
|
||||||
|
if (this.mode === 'start')
|
||||||
|
this.start();
|
||||||
|
else if (this.mode === 'new')
|
||||||
|
this.insert();
|
||||||
|
else
|
||||||
|
this.update();
|
||||||
|
},
|
||||||
|
insert() {
|
||||||
|
this.$refs.form
|
||||||
|
.call(ApiTempusSync.add(this.formData))
|
||||||
|
.then(() => {
|
||||||
|
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||||
|
this.$emit('saved');
|
||||||
|
this.$refs.modal.hide();
|
||||||
|
})
|
||||||
|
.catch(this.$fhcAlert.handleSystemError);
|
||||||
|
},
|
||||||
|
update() {
|
||||||
|
this.$refs.form
|
||||||
|
.call(ApiTempusSync.updateSync(this.formData))
|
||||||
|
.then(() => {
|
||||||
|
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||||
|
this.$emit('saved');
|
||||||
|
this.$refs.modal.hide();
|
||||||
|
})
|
||||||
|
.catch(this.$fhcAlert.handleSystemError);
|
||||||
|
},
|
||||||
|
start() {
|
||||||
|
this.$refs.form
|
||||||
|
.call(ApiTempusSync.start(this.formData))
|
||||||
|
.then(() => {
|
||||||
|
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||||
|
this.$emit('saved');
|
||||||
|
this.$refs.modal.hide();
|
||||||
|
})
|
||||||
|
.catch(this.$fhcAlert.handleSystemError);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
console.log(this.config)
|
||||||
|
this.$api
|
||||||
|
.call(ApiTempusSync.getSyncStatus())
|
||||||
|
.then(result => this.syncStati = result.data)
|
||||||
|
.catch(error => this.$fhcAlert.handleSystemError(error));
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<bs-modal ref="modal" >
|
||||||
|
<template #title>
|
||||||
|
<template v-if="mode === 'new'">{{ $p.t('lehre', 'tempus_sync_new') }}</template>
|
||||||
|
<template v-else-if="mode === 'edit'">{{ $p.t('lehre', 'tempus_sync_edit') }}</template>
|
||||||
|
<template v-else>{{ $p.t('lehre', 'tempus_sync_start') }}</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<core-form ref="form">
|
||||||
|
<form-validation></form-validation>
|
||||||
|
|
||||||
|
<form-input
|
||||||
|
container-class="mb-3"
|
||||||
|
type="select"
|
||||||
|
v-model="formData.oe_kurzbz"
|
||||||
|
name="oe_kurzbz"
|
||||||
|
:label="$p.t('lehre/organisationseinheit')"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="organisation in config.organisationen"
|
||||||
|
:key="organisation.oe_kurzbz"
|
||||||
|
:value="organisation.oe_kurzbz"
|
||||||
|
>
|
||||||
|
{{ organisation.typ.toUpperCase() }}{{ organisation.kurzbz.toUpperCase() }} ({{ organisation.kurzbzlang }})
|
||||||
|
</option>
|
||||||
|
</form-input>
|
||||||
|
|
||||||
|
<form-input
|
||||||
|
container-class="mb-3"
|
||||||
|
type="select"
|
||||||
|
v-model="formData.studiensemester_kurzbz"
|
||||||
|
name="studiensemester_kurzbz"
|
||||||
|
:label="$p.t('lehre/studiensemester')"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="studiensemester in config.studiensemestern"
|
||||||
|
:key="studiensemester.studiensemester_kurzbz"
|
||||||
|
:value="studiensemester.studiensemester_kurzbz"
|
||||||
|
>
|
||||||
|
{{ studiensemester.studiensemester_kurzbz }}
|
||||||
|
</option>
|
||||||
|
</form-input>
|
||||||
|
|
||||||
|
<form-input
|
||||||
|
container-class="mb-3"
|
||||||
|
type="select"
|
||||||
|
v-model="formData.ausbildungssemester"
|
||||||
|
name="ausbildungssemester"
|
||||||
|
:label="$p.t('lehre/ausbildungssemester')"
|
||||||
|
:disabled="!max_semester"
|
||||||
|
>
|
||||||
|
<option :value="null">-- {{ $p.t('fehlermonitoring', 'keineAuswahl') }} --</option>
|
||||||
|
<option
|
||||||
|
v-for="number in semester"
|
||||||
|
:key="number"
|
||||||
|
:value="number"
|
||||||
|
>
|
||||||
|
{{ number }}
|
||||||
|
</option>
|
||||||
|
</form-input>
|
||||||
|
|
||||||
|
<form-input
|
||||||
|
container-class="mb-3"
|
||||||
|
type="select"
|
||||||
|
v-model="formData.studienplan_id"
|
||||||
|
name="studienplan_id"
|
||||||
|
:label="$p.t('lehre/studienplan')"
|
||||||
|
:disabled="!studienplaene.length"
|
||||||
|
>
|
||||||
|
<option :value="null">-- {{ $p.t('fehlermonitoring', 'keineAuswahl') }} --</option>
|
||||||
|
<option
|
||||||
|
v-for="plan in studienplaene"
|
||||||
|
:key="plan.studienplan_id"
|
||||||
|
:value="plan.studienplan_id"
|
||||||
|
>
|
||||||
|
{{ plan.bezeichnung }}-{{plan.orgform_kurzbz}}
|
||||||
|
</option>
|
||||||
|
</form-input>
|
||||||
|
|
||||||
|
<form-input
|
||||||
|
container-class="mb-3"
|
||||||
|
type="DatePicker"
|
||||||
|
v-model="formData.datum_bis"
|
||||||
|
name="datum_bis"
|
||||||
|
:label="$p.t('ui/dateTo')"
|
||||||
|
auto-apply
|
||||||
|
:enable-time-picker="false"
|
||||||
|
text-input
|
||||||
|
format="dd.MM.yyyy"
|
||||||
|
preview-format="dd.MM.yyyy"
|
||||||
|
:teleport="true"
|
||||||
|
>
|
||||||
|
</form-input>
|
||||||
|
|
||||||
|
<form-input
|
||||||
|
|
||||||
|
container-class="mb-3"
|
||||||
|
type="select"
|
||||||
|
v-model="formData.sync_status_kurzbz"
|
||||||
|
name="sync_status_kurzbz"
|
||||||
|
:label="$p.t('global/status')"
|
||||||
|
>
|
||||||
|
<option :value="null">-- {{ $p.t('fehlermonitoring', 'keineAuswahl') }} --</option>
|
||||||
|
<option
|
||||||
|
v-for="status in syncStati"
|
||||||
|
:key="status.status_kurzbz"
|
||||||
|
:value="status.status_kurzbz"
|
||||||
|
>
|
||||||
|
{{ status.bezeichnung }}
|
||||||
|
</option>
|
||||||
|
</form-input>
|
||||||
|
|
||||||
|
<form-input
|
||||||
|
container-class="mb-3"
|
||||||
|
type="checkbox"
|
||||||
|
v-model="formData.mail"
|
||||||
|
name="mail"
|
||||||
|
:label="$p.t('lehre/mail_benachrichtigung')"
|
||||||
|
>
|
||||||
|
</form-input>
|
||||||
|
|
||||||
|
</core-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-primary"
|
||||||
|
@click="save"
|
||||||
|
>
|
||||||
|
<template v-if="mode === 'start'">{{ $p.t('global', 'jetztStarten') }}</template>
|
||||||
|
<template v-else>{{ $p.t('ui', 'ok') }}</template>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</bs-modal>`
|
||||||
|
};
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
import ApiKalender from '../../api/factory/tempus/kalender.js';
|
||||||
|
import BsModal from '../../../js/components/Bootstrap/Modal.js';
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
BsModal,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
show: { type: Boolean, default: false },
|
||||||
|
lehreinheitId: { type: [String, Number], default: null },
|
||||||
|
ortKurzbz: { type: String, default: null },
|
||||||
|
startTime: { type: String, default: null },
|
||||||
|
endTime: { type: String, default: null },
|
||||||
|
},
|
||||||
|
emits: ['close', 'confirmed'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
plan: [],
|
||||||
|
skippedWeeks: [],
|
||||||
|
errors: [],
|
||||||
|
restOffen: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
show(val) {
|
||||||
|
if (val) {
|
||||||
|
this.loadPlan();
|
||||||
|
this.$refs.bsModal.show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.$refs.bsModal.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
countTermin() {
|
||||||
|
return (this.plan.filter(termin => termin.planen).length);
|
||||||
|
},
|
||||||
|
formattedSkippedWeeks() {
|
||||||
|
return this.skippedWeeks.map(week => luxon.DateTime.fromISO(week).toFormat('dd.MM.yyyy')).join('\n');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadPlan() {
|
||||||
|
this.loading = true;
|
||||||
|
this.plan = [];
|
||||||
|
this.errors = [];
|
||||||
|
this.skippedWeeks = [];
|
||||||
|
this.restOffen = 0;
|
||||||
|
|
||||||
|
await this.$api.call(
|
||||||
|
ApiKalender.calculateMultiWeekPlan(
|
||||||
|
this.lehreinheitId,
|
||||||
|
this.ortKurzbz,
|
||||||
|
this.startTime,
|
||||||
|
this.endTime
|
||||||
|
))
|
||||||
|
.then(result => result.data)
|
||||||
|
.then(result => {
|
||||||
|
this.plan = (result.plan || []).map(termin => ({
|
||||||
|
...termin,
|
||||||
|
selected_ort_kurzbz: termin.raum_vorschlaege?.[0]?.ort_kurzbz ?? termin.ort_kurzbz,
|
||||||
|
planen: (termin.collisions && termin.collisions.length > 0) ? false : true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.skippedWeeks = result.skipped_weeks || [];
|
||||||
|
this.errors = result.errors || [];
|
||||||
|
this.restOffen = result.rest_offen || 0;
|
||||||
|
this.loading = false;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.$fhcAlert.handleSystemError(error);
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async confirm() {
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
await this.$api.call(
|
||||||
|
ApiKalender.confirmMultiWeekPlan(this.plan.filter(termin => termin.planen), this.lehreinheitId)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.$emit('confirmed');
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.loading = false;
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
hasCollision(termin) {
|
||||||
|
return termin.collisions && termin.collisions.length > 0;
|
||||||
|
},
|
||||||
|
roomOptions(termin) {
|
||||||
|
return termin.raum_vorschlaege || [];
|
||||||
|
},
|
||||||
|
formatDate(date)
|
||||||
|
{
|
||||||
|
return luxon.DateTime.fromISO(date).toFormat('dd.MM.yyyy');
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<bs-modal
|
||||||
|
ref="bsModal"
|
||||||
|
class="bootstrap-prompt"
|
||||||
|
dialog-class="modal-xl"
|
||||||
|
@hidden-bs-modal="close"
|
||||||
|
>
|
||||||
|
<template v-slot:title>Multi-Week Verplanung Vorschau</template>
|
||||||
|
|
||||||
|
<div v-if="loading" class="text-center py-4">
|
||||||
|
<i class="fa fa-spinner fa-spin"></i> Wird berechnet...
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<div v-if="restOffen > 0" class="alert alert-warning small">
|
||||||
|
Achtung: {{ restOffen }} Stunden konnten nicht verplant werden (Semesterende erreicht).
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="skippedWeeks.length" class="alert alert-warning small">
|
||||||
|
Übersprungende Ferienwochen: <div style="white-space: pre-line;">{{ formattedSkippedWeeks }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="errors.length" class="alert alert-danger small">
|
||||||
|
<div v-for="(err, i) in errors" :key="i">{{ err.message }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table table-sm align-middle">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Datum</th>
|
||||||
|
<th>Zeit</th>
|
||||||
|
<th>Collisions</th>
|
||||||
|
<th>Raum</th>
|
||||||
|
<th>Planen?</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(termin, i) in plan" :key="i" :class="hasCollision(termin) ? 'table-danger' : 'table-success'">
|
||||||
|
<td>{{ formatDate(termin.datum) }}</td>
|
||||||
|
<td>{{ termin.start.split(' ')[1] }} - {{ termin.ende.split(' ')[1] }}</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="hasCollision(termin)" class="text-danger small">
|
||||||
|
<div style="white-space: pre-line;">{{ termin.collisions.map(c => c.message).join('\\n') }}</div>
|
||||||
|
</span>
|
||||||
|
<span v-else class="text-success small">
|
||||||
|
---
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select
|
||||||
|
v-if="roomOptions(termin).length"
|
||||||
|
class="form-select form-select-sm"
|
||||||
|
v-model="termin.selected_ort_kurzbz"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="raum in roomOptions(termin)"
|
||||||
|
:key="raum.ort_kurzbz"
|
||||||
|
:value="raum.ort_kurzbz"
|
||||||
|
>
|
||||||
|
{{ raum.ort_kurzbz }} (Score: {{ raum.score }})
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<span v-else class="text-muted small">Kein Raum verfügbar</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="form-check-input"
|
||||||
|
v-model="termin.planen"
|
||||||
|
:id="'override-' + i"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<button class="btn btn-outline-secondary" @click="close">Abbrechen</button>
|
||||||
|
<button class="btn btn-primary" :disabled="loading || !plan.length" @click="confirm">
|
||||||
|
{{ countTermin }} Termine anlegen
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</bs-modal>
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import EventCard from '../Calendar/Base/Grid/Line/EventCard.js';
|
||||||
|
import drop from '../../directives/drop.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ParkingSlot",
|
||||||
|
components: {
|
||||||
|
EventCard
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
drop
|
||||||
|
},
|
||||||
|
emits: ['update:parkedKeys'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
parked: [],
|
||||||
|
parkedKeys: new Set()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
park(evt, items) {
|
||||||
|
const list = Array.isArray(items) ? items : [items];
|
||||||
|
|
||||||
|
const stored = JSON.parse(localStorage.getItem('tempus_parking') || '[]');
|
||||||
|
|
||||||
|
list.forEach(item => {
|
||||||
|
const key = `${item.id}`;
|
||||||
|
if (this.parkedKeys.has(key))
|
||||||
|
return;
|
||||||
|
this.parkedKeys.add(key);
|
||||||
|
stored.push({
|
||||||
|
type: item.type,
|
||||||
|
id: item.id,
|
||||||
|
orig: item.orig
|
||||||
|
});
|
||||||
|
this.parked.push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
localStorage.setItem('tempus_parking', JSON.stringify(stored));
|
||||||
|
this.$emit('update:parkedKeys', this.parkedKeys);
|
||||||
|
},
|
||||||
|
unpark(event) {
|
||||||
|
const key = `${event.id}`;
|
||||||
|
this.parkedKeys.delete(key);
|
||||||
|
this.parked = this.parked.filter(parkedEvent => parkedEvent.id !== event.id);
|
||||||
|
|
||||||
|
const stored = JSON.parse(localStorage.getItem('tempus_parking') || '[]').filter(parkedEvent => parkedEvent.id !== event.id);
|
||||||
|
localStorage.setItem('tempus_parking', JSON.stringify(stored));
|
||||||
|
this.$emit('update:parkedKeys', this.parkedKeys);
|
||||||
|
},
|
||||||
|
isParked(id) {
|
||||||
|
return this.parkedKeys.has(`${id}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const stored = JSON.parse(localStorage.getItem('tempus_parking') || '[]');
|
||||||
|
this.parked = stored;
|
||||||
|
this.parkedKeys = new Set(stored.map(store => `${store.id}`));
|
||||||
|
this.$emit('update:parkedKeys', this.parkedKeys);
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div class="overflow-auto" tabindex="-1">
|
||||||
|
<div
|
||||||
|
id="parkingslot"
|
||||||
|
class="parkingslot"
|
||||||
|
v-drop:move.kalender-collection="(evt, item) => park(evt, item)"
|
||||||
|
>
|
||||||
|
<i v-if="!parked.length" class="fa-solid fa-square-parking"></i>
|
||||||
|
<event-card
|
||||||
|
class="parkingevent"
|
||||||
|
v-for="parkedEvent in parked"
|
||||||
|
:key="parkedEvent.id"
|
||||||
|
:event="parkedEvent"
|
||||||
|
parked
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
export default {
|
||||||
|
props:{
|
||||||
|
event: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed:{
|
||||||
|
classes() {
|
||||||
|
const classes = ['cis-renderer-lehreinheit-calendar-event', 'calendar-event-default', 'h-100', 'w-100', 'p-1'];
|
||||||
|
|
||||||
|
if (this.event.collisions) {
|
||||||
|
classes.push('calendar-event-collisions');
|
||||||
|
}
|
||||||
|
|
||||||
|
return classes;
|
||||||
|
},
|
||||||
|
topicString() {
|
||||||
|
return Array.isArray(this.event.topic) ? this.event.topic.join(', ') : this.event.topic;
|
||||||
|
},
|
||||||
|
ortString() {
|
||||||
|
return Array.isArray(this.event.ort_kurzbz) ? this.event.ort_kurzbz.join(', ') : this.event.ort_kurzbz;
|
||||||
|
},
|
||||||
|
gruppeString() {
|
||||||
|
return Array.isArray(this.event.gruppe)
|
||||||
|
? this.event.gruppe.map(gruppe => gruppe.bezeichnung).join(', ')
|
||||||
|
: this.event.gruppe;
|
||||||
|
},
|
||||||
|
tooltipString() {
|
||||||
|
const tooltipArray = [];
|
||||||
|
|
||||||
|
tooltipArray.push([
|
||||||
|
this.$p.t('global/uhrzeit'),
|
||||||
|
[this.start, this.end].join(' - ')
|
||||||
|
].join(": "));
|
||||||
|
|
||||||
|
tooltipArray.push([
|
||||||
|
this.$p.t('profilUpdate/topic'),
|
||||||
|
this.topicString
|
||||||
|
].join(": "));
|
||||||
|
|
||||||
|
tooltipArray.push([
|
||||||
|
this.$p.t('person/ort'),
|
||||||
|
this.ortString
|
||||||
|
].join(": "));
|
||||||
|
|
||||||
|
if (this.gruppeString) {
|
||||||
|
tooltipArray.push([
|
||||||
|
this.$p.t('lehre/gruppe'),
|
||||||
|
this.gruppeString
|
||||||
|
].join(": "));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(this.event.lektor) && this.event.lektor.length > 0) {
|
||||||
|
if (this.event.lektor.length > 3) {
|
||||||
|
tooltipArray.push([
|
||||||
|
this.$p.t('lehre/lektor'),
|
||||||
|
this.event.lektor.slice(0, 3).map(lektor => lektor.kurzbz).join("\n")
|
||||||
|
+ "\n" + this.$p.t('lehre/weitereLektoren', [this.event.lektor.length - 3])
|
||||||
|
].join(": "));
|
||||||
|
} else {
|
||||||
|
tooltipArray.push([
|
||||||
|
this.$p.t('lehre/lektor'),
|
||||||
|
this.event.lektor.map(lektor => lektor.kurzbz).join("\n")
|
||||||
|
].join(": "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tooltipArray.join("\n");
|
||||||
|
},
|
||||||
|
start() {
|
||||||
|
return luxon.Duration
|
||||||
|
.fromISOTime(this.event.beginn)
|
||||||
|
.toISOTime({ suppressSeconds: true });
|
||||||
|
},
|
||||||
|
end() {
|
||||||
|
return luxon.Duration
|
||||||
|
.fromISOTime(this.event.ende)
|
||||||
|
.toISOTime({ suppressSeconds: true });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: /*html*/`
|
||||||
|
<div
|
||||||
|
:class="classes"
|
||||||
|
@wheel.stop
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="!event.allDayEvent && event?.beginn && event?.ende"
|
||||||
|
class="event-time d-none d-xl-grid h-100"
|
||||||
|
>
|
||||||
|
<span>{{ start }}</span>
|
||||||
|
<span>{{ end }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="event-text" v-tooltip="tooltipString">
|
||||||
|
<span class="event-topic">{{ topicString }}</span>
|
||||||
|
<span class="event-place">{{ ortString }}</span>
|
||||||
|
<span v-if="gruppeString" class="event-gruppe">{{ gruppeString }}</span>
|
||||||
|
<span
|
||||||
|
v-for="(lektor,index) in event.lektor.slice(0, 3)"
|
||||||
|
class="event-lectors"
|
||||||
|
>
|
||||||
|
{{ lektor.kurzbz }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="event.lektor.length > 3"
|
||||||
|
class="event-lectors-plus"
|
||||||
|
>
|
||||||
|
... +{{ event.lektor.length - 3 }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
import { numberPadding, formatDate } from "../../../../helpers/DateHelpers.js"
|
||||||
|
import LvMenu from "../../../../components/Cis/Mylv/LvMenu.js";
|
||||||
|
|
||||||
|
import ApiLvPlan from '../../../../api/factory/lvPlan.js';
|
||||||
|
import ApiAddons from '../../../../api/factory/addons.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components:{
|
||||||
|
LvMenu,
|
||||||
|
},
|
||||||
|
props:{
|
||||||
|
event: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
lvMenu: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
getOrtContentLink: function () {
|
||||||
|
if (!this.event || !this.event.ort_content_id) return "a";
|
||||||
|
|
||||||
|
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + `/CisVue/Cms/content/${this.event.ort_content_id}`
|
||||||
|
},
|
||||||
|
start_time: function () {
|
||||||
|
if (!this.event.beginn)
|
||||||
|
return 'N/A';
|
||||||
|
if (!(this.event.beginn instanceof Date)) {
|
||||||
|
return this.event.beginn;
|
||||||
|
}
|
||||||
|
return numberPadding(this.event.beginn.getHours()) + ":" + numberPadding(this.event.beginn.getMinutes());
|
||||||
|
},
|
||||||
|
end_time: function () {
|
||||||
|
if (!this.event.ende)
|
||||||
|
return 'N/A';
|
||||||
|
if (!(this.event.ende instanceof Date)) {
|
||||||
|
return this.event.ende;
|
||||||
|
}
|
||||||
|
return numberPadding(this.event.ende.getHours()) + ":" + numberPadding(this.event.ende.getMinutes());
|
||||||
|
},
|
||||||
|
ortString() {
|
||||||
|
return Array.isArray(this.event.ort_kurzbz) ? this.event.ort_kurzbz.join(', ') : this.event.ort_kurzbz;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
mehtodNumberPadding: function (number) {
|
||||||
|
return numberPadding(number);
|
||||||
|
},
|
||||||
|
methodFormatDate: function (d) {
|
||||||
|
return formatDate(d);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
if (this.event.type == 'lehreinheit') {
|
||||||
|
this.$api
|
||||||
|
.call(ApiLvPlan.getLehreinheitStudiensemester(Array.isArray(this.event.lehreinheit_id) ? this.event.lehreinheit_id[0] : this.event.lehreinheit_id))
|
||||||
|
.then(res => res.data)
|
||||||
|
.then(studiensemester_kurzbz => this.$api.call(
|
||||||
|
ApiAddons.getLvMenu(
|
||||||
|
this.event.lehrveranstaltung_id,
|
||||||
|
studiensemester_kurzbz
|
||||||
|
)
|
||||||
|
))
|
||||||
|
.then(res => {
|
||||||
|
this.lvMenu = res.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<h5>
|
||||||
|
{{$p.t('lvinfo','lehrveranstaltungsinformationen')}}
|
||||||
|
</h5>
|
||||||
|
<table class="table table-hover mb-4">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>{{
|
||||||
|
$p.t('global','datum')?
|
||||||
|
$p.t('global','datum')+':'
|
||||||
|
:''
|
||||||
|
}}</th>
|
||||||
|
<td>{{methodFormatDate(event.datum)}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{{
|
||||||
|
$p.t('ui','zeitraum')?
|
||||||
|
$p.t('ui','zeitraum')+':'
|
||||||
|
:''
|
||||||
|
}}</th>
|
||||||
|
<td>{{start_time + ' - ' + end_time}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{{
|
||||||
|
$p.t('global','raum')?
|
||||||
|
$p.t('global','raum')+':'
|
||||||
|
:''
|
||||||
|
}}</th>
|
||||||
|
<td>
|
||||||
|
<a v-if="event.ort_content_id" :aria-label="$p.t('global','raum')" :title="$p.t('global','raum')" :href="getOrtContentLink"><i class="fa fa-arrow-up-right-from-square me-1" aria-hidden="true" style="color:#00649C"></i></a>
|
||||||
|
{{ortString}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{{
|
||||||
|
$p.t('lehre','lehrveranstaltung')?
|
||||||
|
$p.t('lehre','lehrveranstaltung')+':'
|
||||||
|
:''
|
||||||
|
}}</th>
|
||||||
|
<td>{{'('+event.lehrform+') ' + event.lehrfach_bez}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{{
|
||||||
|
$p.t('lehre','lektor')?
|
||||||
|
$p.t('lehre','lektor')+':'
|
||||||
|
:''
|
||||||
|
}}</th>
|
||||||
|
<td>
|
||||||
|
<div id="lektorenContainer">
|
||||||
|
<div v-for="lektor in event.lektor" class="d-block">
|
||||||
|
<a v-if="lektorenLinks[lektor.kurzbz]" :aria-label="$p.t('lehre','lektor')" :title="$p.t('lehre','lektor')" :href="lektorenLinks[lektor.kurzbz]"><i class="fa fa-arrow-up-right-from-square me-1" style="color:#00649C" aria-hidden="true"></i></a>
|
||||||
|
{{lektor.kurzbz}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{{
|
||||||
|
$p.t('lehre','organisationseinheit')?
|
||||||
|
$p.t('lehre','organisationseinheit')+':'
|
||||||
|
:''
|
||||||
|
}}</th>
|
||||||
|
<td>{{event.organisationseinheit}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<lv-menu :containerStyles="['p-0']" :rowStyles="['m-0']" v-if="lvMenu.length" :menu="lvMenu" />
|
||||||
|
</div>`,
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
export default {
|
||||||
|
props:{
|
||||||
|
event: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template:`
|
||||||
|
<div v-if="event.titel">{{ event.titel + ' - ' + event.lehrfach_bez + ' [' + event.ort_kurzbz+']'}}</div>
|
||||||
|
<div v-else>{{ event.lehrfach_bez + ' [' + event.ort_kurzbz+']'}}</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
event: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
classes() {
|
||||||
|
const classes = ['cis-renderer-reservierungen-calendar-event', 'calendar-event-default', 'h-100', 'w-100', 'p-1'];
|
||||||
|
|
||||||
|
if (this.event.collisions) {
|
||||||
|
classes.push('calendar-event-collisions');
|
||||||
|
}
|
||||||
|
|
||||||
|
return classes;
|
||||||
|
},
|
||||||
|
tooltipString() {
|
||||||
|
const tooltipArray = [];
|
||||||
|
|
||||||
|
tooltipArray.push([
|
||||||
|
this.$p.t('global/uhrzeit'),
|
||||||
|
[this.start, this.end].join(' - ')
|
||||||
|
].join(": "));
|
||||||
|
|
||||||
|
tooltipArray.push([
|
||||||
|
this.$p.t('profilUpdate/topic'),
|
||||||
|
this.topicString
|
||||||
|
].join(": "));
|
||||||
|
|
||||||
|
tooltipArray.push([
|
||||||
|
this.$p.t('person/ort'),
|
||||||
|
this.ortString
|
||||||
|
].join(": "));
|
||||||
|
|
||||||
|
if (this.gruppeString) {
|
||||||
|
tooltipArray.push([
|
||||||
|
this.$p.t('lehre/gruppe'),
|
||||||
|
this.gruppeString
|
||||||
|
].join(": "));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(this.event.lektor) && this.event.lektor.length > 0) {
|
||||||
|
if (this.event.lektor.length > 3) {
|
||||||
|
tooltipArray.push([
|
||||||
|
this.$p.t('lehre/lektor'),
|
||||||
|
this.event.lektor.slice(0, 3).map(lektor => lektor.kurzbz).join("\n")
|
||||||
|
+ "\n" + this.$p.t('lehre/weitereLektoren', [this.event.lektor.length - 3])
|
||||||
|
].join(": "));
|
||||||
|
} else {
|
||||||
|
tooltipArray.push([
|
||||||
|
this.$p.t('lehre/lektor'),
|
||||||
|
this.event.lektor.map(lektor => lektor.kurzbz).join("\n")
|
||||||
|
].join(": "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(this.event.teilnehmer_person) && this.event.teilnehmer_person.length > 0) {
|
||||||
|
if (this.event.teilnehmer_person.length > 3) {
|
||||||
|
tooltipArray.push([
|
||||||
|
this.$p.t('lehre/teilnehmer'),
|
||||||
|
this.event.teilnehmer_person.slice(0, 3).map(person => `${person.vorname} ${person.nachname}`).join("\n")
|
||||||
|
+ "\n" + this.$p.t('lehre/weitereTeilnehmer', [this.event.teilnehmer_person.length - 3])
|
||||||
|
].join(": "));
|
||||||
|
} else {
|
||||||
|
tooltipArray.push([
|
||||||
|
this.$p.t('lehre/teilnehmer'),
|
||||||
|
this.event.teilnehmer_person.map(person => `${person.vorname} ${person.nachname}`).join("\n")
|
||||||
|
].join(": "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tooltipArray.join("\n");
|
||||||
|
},
|
||||||
|
start() {
|
||||||
|
return luxon.Duration
|
||||||
|
.fromISOTime(this.event.beginn)
|
||||||
|
.toISOTime({ suppressSeconds: true });
|
||||||
|
},
|
||||||
|
end() {
|
||||||
|
return luxon.Duration
|
||||||
|
.fromISOTime(this.event.ende)
|
||||||
|
.toISOTime({ suppressSeconds: true });
|
||||||
|
},
|
||||||
|
ortString() {
|
||||||
|
return Array.isArray(this.event.ort_kurzbz) ? this.event.ort_kurzbz.join(', ') : this.event.ort_kurzbz;
|
||||||
|
},
|
||||||
|
topicString() {
|
||||||
|
return Array.isArray(this.event.topic) ? this.event.topic.join(', ') : this.event.topic;
|
||||||
|
},
|
||||||
|
gruppeString() {
|
||||||
|
return Array.isArray(this.event.teilnehmer_gruppe)
|
||||||
|
? this.event.teilnehmer_gruppe.map(gruppe => gruppe.gruppe_kurzbz).join(', ')
|
||||||
|
: this.event.teilnehmer_gruppe;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: /* html */`
|
||||||
|
<div
|
||||||
|
:class="classes"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="!event.allDayEvent && event?.beginn && event?.ende"
|
||||||
|
class="event-time d-grid h-100"
|
||||||
|
>
|
||||||
|
<span>{{ start }}</span>
|
||||||
|
<span>{{ end }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="event-text" v-tooltip="tooltipString">
|
||||||
|
<span class="event-topic">{{ topicString }}</span>
|
||||||
|
<span
|
||||||
|
v-for="lektor in event.lektor.slice(0, 3)"
|
||||||
|
class="event-lectors"
|
||||||
|
>
|
||||||
|
{{ lektor.kurzbz }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="event.lektor.length > 3"
|
||||||
|
class="event-lectors-plus"
|
||||||
|
>
|
||||||
|
... +{{ event.lektor.length - 3 }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-for="person in (event.teilnehmer_person || []).slice(0, 3)"
|
||||||
|
class="event-teilnehmer"
|
||||||
|
>
|
||||||
|
{{ person.vorname }} {{ person.nachname }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="event.teilnehmer_person && event.teilnehmer_person.length > 3"
|
||||||
|
class="event-teilnehmer-plus"
|
||||||
|
>
|
||||||
|
... +{{ event.teilnehmer_person.length - 3 }}
|
||||||
|
</span>
|
||||||
|
<span class="event-place">{{ ortString }}</span>
|
||||||
|
<span v-if="gruppeString" class="event-gruppe">{{ gruppeString }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import { numberPadding, formatDate } from "../../../../helpers/DateHelpers.js"
|
||||||
|
import LvMenu from "../../../Cis/Mylv/LvMenu.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props:{
|
||||||
|
event: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
lvMenu:{
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components:{
|
||||||
|
LvMenu,
|
||||||
|
},
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
getOrtContentLink: function () {
|
||||||
|
if (!this.event || !this.event.ort_content_id) return "a";
|
||||||
|
|
||||||
|
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + `/CisVue/Cms/content/${this.event.ort_content_id}`
|
||||||
|
},
|
||||||
|
start_time: function () {
|
||||||
|
if (!this.event.beginn)
|
||||||
|
return 'N/A';
|
||||||
|
if (!(this.event.beginn instanceof Date)) {
|
||||||
|
return this.event.beginn;
|
||||||
|
}
|
||||||
|
return numberPadding(this.event.beginn.getHours()) + ":" + numberPadding(this.event.beginn.getMinutes());
|
||||||
|
},
|
||||||
|
end_time: function () {
|
||||||
|
if (!this.event.ende)
|
||||||
|
return 'N/A';
|
||||||
|
if (!(this.event.ende instanceof Date)) {
|
||||||
|
return this.event.ende;
|
||||||
|
}
|
||||||
|
return numberPadding(this.event.ende.getHours()) + ":" + numberPadding(this.event.ende.getMinutes());
|
||||||
|
},
|
||||||
|
ortString() {
|
||||||
|
return Array.isArray(this.event.ort_kurzbz) ? this.event.ort_kurzbz.join(', ') : this.event.ort_kurzbz;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
mehtodNumberPadding: function (number) {
|
||||||
|
return numberPadding(number);
|
||||||
|
},
|
||||||
|
methodFormatDate: function (d) {
|
||||||
|
return formatDate(d);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<h6>
|
||||||
|
{{ event.beschreibung }}
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
<h5>
|
||||||
|
{{$p.t('ui','reservierungsinformationen')}}
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<table class="table table-hover mb-4">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>{{
|
||||||
|
$p.t('global','datum')?
|
||||||
|
$p.t('global','datum')+':'
|
||||||
|
:''
|
||||||
|
}}</th>
|
||||||
|
<td>{{methodFormatDate(event.datum)}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{{
|
||||||
|
$p.t('ui','zeitraum')?
|
||||||
|
$p.t('ui','zeitraum')+':'
|
||||||
|
:''
|
||||||
|
}}</th>
|
||||||
|
<td>{{start_time + ' - ' + end_time}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{{
|
||||||
|
$p.t('global','raum')?
|
||||||
|
$p.t('global','raum')+':'
|
||||||
|
:''
|
||||||
|
}}</th>
|
||||||
|
<td>
|
||||||
|
<a v-if="event.ort_content_id" :aria-label="$p.t('global','raum')" :title="$p.t('global','raum')" :href="getOrtContentLink"><i class="fa fa-arrow-up-right-from-square me-1" style="color:#00649C" aria-hidden="true"></i></a>
|
||||||
|
{{ortString}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{{
|
||||||
|
$p.t('ui','organisierende')?
|
||||||
|
$p.t('ui','organisierende')+':'
|
||||||
|
:''
|
||||||
|
}}</th>
|
||||||
|
<td>
|
||||||
|
<div v-for="lektor in event.lektor" class="d-block">
|
||||||
|
<a v-if="lektorenLinks[lektor.kurzbz]" :aria-label="$p.t('lehre','lektor')" :tooltip="$p.t('lehre','lektor')" :href="lektorenLinks[lektor.kurzbz]"><i class="fa fa-arrow-up-right-from-square me-1" aria-hidden="true" style="color:#00649C"></i></a>
|
||||||
|
{{lektor.kurzbz}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th>{{
|
||||||
|
$p.t('ui','teilnehmende')?
|
||||||
|
$p.t('ui','teilnehmende')+':'
|
||||||
|
:''
|
||||||
|
}}</th>
|
||||||
|
<td>
|
||||||
|
<div v-for="teilnehmer in event.teilnehmer_person" class="d-block">
|
||||||
|
{{teilnehmer.kurzbz}}
|
||||||
|
</div>
|
||||||
|
<div v-for="gruppe in event.teilnehmer_gruppe" class="d-block">
|
||||||
|
{{gruppe.gruppe_kurzbz}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>`,
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export default {
|
||||||
|
props:{
|
||||||
|
event: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template:`
|
||||||
|
<div >{{ event.titel }}</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -0,0 +1,304 @@
|
|||||||
|
import BsModal from '../Bootstrap/Modal.js';
|
||||||
|
import FormInput from '../Form/Input.js';
|
||||||
|
import ApiReservierung from '../../api/factory/tempus/reservierung.js';
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ReservierungModal',
|
||||||
|
components: {
|
||||||
|
BsModal,
|
||||||
|
FormInput,
|
||||||
|
Multiselect: primevue.multiselect,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
rooms: {
|
||||||
|
type: Array,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['saved'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
titel: '',
|
||||||
|
beschreibung: '',
|
||||||
|
start: null,
|
||||||
|
end: null,
|
||||||
|
selected_rooms: [],
|
||||||
|
raeume_array: [],
|
||||||
|
studiensemester_array: [],
|
||||||
|
teilnehmer: [],
|
||||||
|
specialFinalGroups: [],
|
||||||
|
rollen_array: [],
|
||||||
|
studiengaenge: [],
|
||||||
|
show_all_fields: false,
|
||||||
|
filteredUsers: [],
|
||||||
|
filteredGroups: [],
|
||||||
|
abortController: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created()
|
||||||
|
{
|
||||||
|
this.$api.call(ApiReservierung.getInformation())
|
||||||
|
.then(result => result.data)
|
||||||
|
.then(result => {
|
||||||
|
|
||||||
|
if (result.berechtigt)
|
||||||
|
{
|
||||||
|
this.studiengaenge = result.studiengaenge
|
||||||
|
}
|
||||||
|
this.show_all_fields = result.berechtigt;
|
||||||
|
|
||||||
|
this.raeume_array = result.raeume;
|
||||||
|
this.rollen_array = result.rollen;
|
||||||
|
this.studiensemester_array = result.studiensemester;
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(this.$fhcAlert.handleSystemError);
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
async searchGroup(event)
|
||||||
|
{
|
||||||
|
const query = event.query.trim();
|
||||||
|
|
||||||
|
if (query.length < 2)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
if (this.abortController)
|
||||||
|
this.abortController.abort();
|
||||||
|
|
||||||
|
this.abortController = new AbortController();
|
||||||
|
|
||||||
|
this.$api.call(ApiReservierung.searchGroup(query), { signal: this.abortController.signal })
|
||||||
|
.then(result => {
|
||||||
|
this.filteredGroups =result.data.map(gruppe => ({
|
||||||
|
label: gruppe.bezeichnung
|
||||||
|
? `${gruppe.gruppe_kurzbz.trim()} (${gruppe.bezeichnung})`
|
||||||
|
: gruppe.gruppe_kurzbz.trim(),
|
||||||
|
gid: gruppe.gid,
|
||||||
|
gruppe_kurzbz: gruppe.gruppe_kurzbz.trim(),
|
||||||
|
lehrverband: gruppe.lehrverband,
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
.catch(this.$fhcAlert.handleSystemError);
|
||||||
|
},
|
||||||
|
|
||||||
|
searchUser(event)
|
||||||
|
{
|
||||||
|
const query = event.query.trim();
|
||||||
|
|
||||||
|
if (!query || query.length < 2)
|
||||||
|
{
|
||||||
|
this.filteredUsers = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.abortController)
|
||||||
|
this.abortController.abort();
|
||||||
|
|
||||||
|
this.abortController = new AbortController();
|
||||||
|
|
||||||
|
this.$api.call(ApiReservierung.searchTeilnehmer(query), { signal: this.abortController.signal })
|
||||||
|
.then(result => {
|
||||||
|
this.filteredUsers = result.data.map(u => ({
|
||||||
|
label: `${u.nachname} ${u.vorname} (${u.uid})`,
|
||||||
|
uid: u.uid
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
.catch(this.$fhcAlert.handleSystemError);
|
||||||
|
},
|
||||||
|
selectUser(event, index)
|
||||||
|
{
|
||||||
|
this.teilnehmer[index].uid = event.value.uid;
|
||||||
|
this.teilnehmer[index].label = event.value.label;
|
||||||
|
},
|
||||||
|
selectFinalGroup(event, index)
|
||||||
|
{
|
||||||
|
this.specialFinalGroups[index].gid = event.value.gid;
|
||||||
|
this.specialFinalGroups[index].gruppe_kurzbz = event.value.gruppe_kurzbz;
|
||||||
|
this.specialFinalGroups[index].lehrverband = event.value.lehrverband;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
show(start, end)
|
||||||
|
{
|
||||||
|
this.titel = '';
|
||||||
|
this.beschreibung = '';
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
|
||||||
|
let prop_orte = (this.rooms ?? []).map(r => r.ort_kurzbz);
|
||||||
|
this.selected_rooms = this.raeume_array.filter(raum => prop_orte.includes(raum.ort_kurzbz));
|
||||||
|
|
||||||
|
this.teilnehmer = [{ uid: null, rolle: null }];
|
||||||
|
this.specialFinalGroups = [{ gid: null, studiensemester_kurzbz: null, lehrverband: null, gruppe_kurzbz: null, rolle: null }];
|
||||||
|
this.$refs.modal.show();
|
||||||
|
},
|
||||||
|
hide()
|
||||||
|
{
|
||||||
|
this.$refs.modal.hide();
|
||||||
|
},
|
||||||
|
save()
|
||||||
|
{
|
||||||
|
this.$api.call(
|
||||||
|
ApiReservierung.addReservierung(
|
||||||
|
this.titel,
|
||||||
|
this.beschreibung,
|
||||||
|
this.selected_rooms.map(r => r.ort_kurzbz),
|
||||||
|
luxon.DateTime.fromFormat(this.start, 'yyyy-MM-dd HH:mm').toISO(),
|
||||||
|
luxon.DateTime.fromFormat(this.end, 'yyyy-MM-dd HH:mm').toISO(),
|
||||||
|
this.teilnehmer.filter(t => t.uid && t.rolle).map(nehmer => ({ uid: nehmer.uid, rolle: nehmer.rolle })),
|
||||||
|
this.specialFinalGroups.filter(group => group.gid && group.rolle && group.studiensemester_kurzbz).map(group => ({ gid: group.gid, lehrverband: group.lehrverband, gruppe_kurzbz: group.gruppe_kurzbz, rolle: group.rolle, studiensemester_kurzbz: group.studiensemester_kurzbz })),
|
||||||
|
)
|
||||||
|
).then(() => {
|
||||||
|
this.$refs.modal.hide();
|
||||||
|
this.$emit('saved');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// language=HTML
|
||||||
|
template: `
|
||||||
|
<bs-modal ref="modal" class="bootstrap-prompt" dialogClass="modal-xl">
|
||||||
|
<template #title>Neue Reservierung</template>
|
||||||
|
<template #default>
|
||||||
|
<div class="row g-3">
|
||||||
|
<form-input
|
||||||
|
label="Titel"
|
||||||
|
type="text"
|
||||||
|
container-class="col-12"
|
||||||
|
name="titel"
|
||||||
|
v-model="titel"
|
||||||
|
></form-input>
|
||||||
|
<form-input
|
||||||
|
label="Beschreibung"
|
||||||
|
type="textarea"
|
||||||
|
container-class="col-12"
|
||||||
|
name="beschreibung"
|
||||||
|
v-model="beschreibung"
|
||||||
|
></form-input>
|
||||||
|
<div class="col-6">
|
||||||
|
<form-input
|
||||||
|
type="datepicker"
|
||||||
|
v-model="start"
|
||||||
|
name="star_date"
|
||||||
|
format="dd.MM.yyyy HH:mm"
|
||||||
|
auto-apply
|
||||||
|
:enable-time-picker="true"
|
||||||
|
preview-format="dd.MM.yyyy HH:mm"
|
||||||
|
model-type="yyyy-MM-dd HH:mm"
|
||||||
|
:label="$p.t('ui', 'von')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<form-input
|
||||||
|
type="datepicker"
|
||||||
|
v-model="end"
|
||||||
|
name="end_time"
|
||||||
|
format="dd.MM.yyyy HH:mm"
|
||||||
|
auto-apply
|
||||||
|
:enable-time-picker="true"
|
||||||
|
preview-format="dd.MM.yyyy HH:mm"
|
||||||
|
model-type="yyyy-MM-dd HH:mm"
|
||||||
|
:label="$p.t('global', 'bis')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="form-label">{{ $p.t('global', 'raum') }}</label>
|
||||||
|
<Multiselect
|
||||||
|
v-model="selected_rooms"
|
||||||
|
option-label="ort_kurzbz"
|
||||||
|
:options="raeume_array"
|
||||||
|
placeholder="Raum"
|
||||||
|
display="chip"
|
||||||
|
:hide-selected="false"
|
||||||
|
class="w-100"
|
||||||
|
:selectionLimit="3"
|
||||||
|
>
|
||||||
|
<template #option="{ option }">
|
||||||
|
{{ option.ort_kurzbz }} {{ option.bezeichnung }}
|
||||||
|
</template>
|
||||||
|
</Multiselect>
|
||||||
|
</div>
|
||||||
|
<div class="col-12" v-if="show_all_fields">
|
||||||
|
<div v-for="(nehmer, i) in teilnehmer" :key="i" class="d-flex gap-2 mb-2 align-items-end">
|
||||||
|
<form-input
|
||||||
|
type="autocomplete"
|
||||||
|
:label="$p.t('ui', 'teilnehmende')"
|
||||||
|
:suggestions="filteredUsers"
|
||||||
|
v-model="nehmer.label"
|
||||||
|
field="label"
|
||||||
|
container-class="flex-grow-1"
|
||||||
|
:name="'user_' + i"
|
||||||
|
@complete="searchUser($event, i)"
|
||||||
|
@item-select="selectUser($event, i)"
|
||||||
|
></form-input>
|
||||||
|
<form-input
|
||||||
|
type="select"
|
||||||
|
:label="$p.t('lehre', 'status_rolle')"
|
||||||
|
v-model="nehmer.rolle"
|
||||||
|
:name="'rolle_' + i"
|
||||||
|
|
||||||
|
>
|
||||||
|
<option v-for="rolle in rollen_array" :value="rolle.rolle_kurzbz" :key="rolle.rolle_kurzbz">
|
||||||
|
{{ rolle.bezeichnung }}
|
||||||
|
</option>
|
||||||
|
</form-input>
|
||||||
|
<button type="button" class="btn btn-outline-danger" @click="teilnehmer.splice(i, 1)">
|
||||||
|
<i class="fa-solid fa-xmark"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm" @click="teilnehmer.push({ uid: null, label: '', rolle: null })">
|
||||||
|
<i class="fa-solid fa-plus me-1"></i>{{$capitalize( $p.t('global', 'hinzufuegen') )}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12" v-if="show_all_fields">
|
||||||
|
<div v-for="(group, i) in specialFinalGroups" :key="i" class="d-flex gap-3 mb-3 align-items-end">
|
||||||
|
<form-input
|
||||||
|
type="autocomplete"
|
||||||
|
:label="$p.t('lehre', 'gruppe')"
|
||||||
|
:suggestions="filteredGroups"
|
||||||
|
v-model="group.label"
|
||||||
|
field="label"
|
||||||
|
container-class="flex-grow-1"
|
||||||
|
:name="'group_' + i"
|
||||||
|
@complete="searchGroup($event, i)"
|
||||||
|
@item-select="selectFinalGroup($event, i)"
|
||||||
|
></form-input>
|
||||||
|
<form-input
|
||||||
|
type="select"
|
||||||
|
:label="$p.t('lehre', 'studiensemester')"
|
||||||
|
v-model="group.studiensemester_kurzbz"
|
||||||
|
:name="'studiensemester_' + i"
|
||||||
|
>
|
||||||
|
<option v-for="studiensemester in studiensemester_array" :value="studiensemester.studiensemester_kurzbz" :key="studiensemester.studiensemester_kurzbz">
|
||||||
|
{{ studiensemester.studiensemester_kurzbz }}
|
||||||
|
</option>
|
||||||
|
</form-input>
|
||||||
|
|
||||||
|
<form-input
|
||||||
|
type="select"
|
||||||
|
:label="$p.t('lehre', 'status_rolle')"
|
||||||
|
v-model="group.rolle"
|
||||||
|
:name="'rolle_' + i"
|
||||||
|
>
|
||||||
|
<option v-for="rolle in rollen_array" :value="rolle.rolle_kurzbz" :key="rolle.rolle_kurzbz">
|
||||||
|
{{ rolle.bezeichnung }}
|
||||||
|
</option>
|
||||||
|
</form-input>
|
||||||
|
<button type="button" class="btn btn-outline-danger" @click="specialFinalGroups.splice(i, 1)">
|
||||||
|
<i class="fa-solid fa-xmark"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm" @click="specialFinalGroups.push({ gruppe_kurzbz: null, studiensemester_kurzbz: null, label: '', rolle: null })">
|
||||||
|
<i class="fa-solid fa-plus me-1"></i>{{$p.t('lehre', 'gruppe')}} {{$p.t('global', 'hinzufuegen')}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<button type="button" class="btn btn-primary" @click="save">Speichern</button>
|
||||||
|
</template>
|
||||||
|
</bs-modal>`
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,196 @@
|
|||||||
|
import {CoreFilterCmpt} from '../filter/Filter.js';
|
||||||
|
import {CoreNavigationCmpt} from '../navigation/Navigation.js';
|
||||||
|
import FormInput from "../Form/Input.js";
|
||||||
|
|
||||||
|
import CoreBaseLayout from '../../components/layout/BaseLayout.js';
|
||||||
|
import ApiTempusSync from '../../api/factory/tempus/sync.js';
|
||||||
|
import TempusSyncModal from './Modal.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
config: Object,
|
||||||
|
defaultSemester: String
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
CoreFilterCmpt,
|
||||||
|
CoreBaseLayout,
|
||||||
|
CoreNavigationCmpt,
|
||||||
|
FormInput,
|
||||||
|
TempusSyncModal
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
studiensemester_kurzbz: this.defaultSemester ?? null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
studiensemester_kurzbz()
|
||||||
|
{
|
||||||
|
this.reloadTable();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
tabulatorOptions() {
|
||||||
|
return {
|
||||||
|
index: "kalender_syncstatus_id",
|
||||||
|
ajaxURL: 'dummy',
|
||||||
|
ajaxRequestFunc: () => this.$api.call(ApiTempusSync.getSyncs(this.studiensemester_kurzbz)),
|
||||||
|
ajaxResponse: (url, params, response) => {
|
||||||
|
return response.data || [];
|
||||||
|
},
|
||||||
|
layout: 'fitDataStretch',
|
||||||
|
placeholder: "Keine Daten verfügbar",
|
||||||
|
persistenceID: "2026_07_21_tempus_sync_v1",
|
||||||
|
locale: true,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
formatter: 'rowSelection',
|
||||||
|
titleFormatter: 'rowSelection',
|
||||||
|
headerSort: false,
|
||||||
|
width: 40
|
||||||
|
},
|
||||||
|
{titlePhrase: 'lehre/organisationseinheit', field: 'oebezeichnung'},
|
||||||
|
{titlePhrase: 'lehre/studiensemester', field: 'studiensemester_kurzbz'},
|
||||||
|
{titlePhrase: 'lehre/bis_title', field: 'datum_bis',
|
||||||
|
formatter: (cell) => {
|
||||||
|
const value = cell.getValue();
|
||||||
|
if (!value) return ''
|
||||||
|
const date = luxon.DateTime.fromSQL(value)
|
||||||
|
return date.isValid ? date.toFormat('dd.MM.yyyy') : value
|
||||||
|
},
|
||||||
|
minWidth: 100
|
||||||
|
},
|
||||||
|
{titlePhrase: 'lehre/studienplan', field: 'studienplanbezeichnung', minWidth: 200},
|
||||||
|
{titlePhrase: 'lehre/ausbildungssemester', field: 'ausbildungssemester'},
|
||||||
|
{titlePhrase: 'global/status', field: 'sync_status_kurzbz'},
|
||||||
|
{titlePhrase: 'lehre/mail_benachrichtigung', field: 'mail', formatter: 'tickCross', hozAlign: 'center', minWidth: 80},
|
||||||
|
{titlePhrase:'global/insertamum', field: 'insertamum',
|
||||||
|
formatter: (cell) => {
|
||||||
|
const value = cell.getValue();
|
||||||
|
if (!value) return ''
|
||||||
|
const date = luxon.DateTime.fromSQL(value)
|
||||||
|
return date.isValid ? date.toFormat('dd.MM.yyyy HH:mm') : value
|
||||||
|
},
|
||||||
|
visible: false
|
||||||
|
},
|
||||||
|
{titlePhrase: 'global/insertvon', field: 'insertvon', visible: false},
|
||||||
|
{titlePhrase: 'global/updateamum', field: 'updateamum',
|
||||||
|
formatter: (cell) => {
|
||||||
|
const value = cell.getValue();
|
||||||
|
if (!value) return ''
|
||||||
|
const date = luxon.DateTime.fromSQL(value)
|
||||||
|
return date.isValid ? date.toFormat('dd.MM.yyyy HH:mm') : value
|
||||||
|
},
|
||||||
|
visible: false
|
||||||
|
},
|
||||||
|
{titlePhrase: 'global/updatevon', field: 'updatevon', visible: false},
|
||||||
|
{
|
||||||
|
titlePhrase: 'global/aktionen',
|
||||||
|
field: 'actions',
|
||||||
|
headerSort: false,
|
||||||
|
hozAlign: 'center',
|
||||||
|
width: 120,
|
||||||
|
formatter: (cell) => {
|
||||||
|
let container = document.createElement('div');
|
||||||
|
container.className = "d-flex gap-2";
|
||||||
|
let button = document.createElement('button');
|
||||||
|
button.className = 'btn btn-outline-secondary';
|
||||||
|
button.innerHTML = '<i class="fa fa-edit"></i>';
|
||||||
|
button.addEventListener('click', () =>
|
||||||
|
this.$refs.syncModal.openEdit(cell.getRow().getData())
|
||||||
|
);
|
||||||
|
|
||||||
|
container.append(button);
|
||||||
|
|
||||||
|
button = document.createElement('button');
|
||||||
|
button.className = 'btn btn-outline-secondary';
|
||||||
|
button.innerHTML = '<i class="fa fa-trash"></i>';
|
||||||
|
button.addEventListener('click', evt => {
|
||||||
|
evt.stopPropagation();
|
||||||
|
this.$fhcAlert
|
||||||
|
.confirmDelete()
|
||||||
|
.then(result => result ? cell.getData().kalender_syncstatus_id : Promise.reject({handled:true}))
|
||||||
|
.then(kalender_syncstatus_id => this.$api.call(ApiTempusSync.delete(kalender_syncstatus_id)))
|
||||||
|
.then(() => {
|
||||||
|
this.reloadTable();
|
||||||
|
})
|
||||||
|
.catch(this.$fhcAlert.handleSystemError);
|
||||||
|
});
|
||||||
|
container.append(button);
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
openNewModal()
|
||||||
|
{
|
||||||
|
this.$refs.syncModal.openNew();
|
||||||
|
},
|
||||||
|
reloadTable()
|
||||||
|
{
|
||||||
|
this.$refs.syncTable.reloadTable();
|
||||||
|
},
|
||||||
|
openStartModal()
|
||||||
|
{
|
||||||
|
this.$refs.syncModal.openStart();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<core-navigation-cmpt></core-navigation-cmpt>
|
||||||
|
<core-base-layout>
|
||||||
|
<template #main>
|
||||||
|
|
||||||
|
<core-filter-cmpt
|
||||||
|
ref="syncTable"
|
||||||
|
:tabulator-options="tabulatorOptions"
|
||||||
|
:table-only=true
|
||||||
|
:side-menu="false"
|
||||||
|
:reload="true"
|
||||||
|
:useSelectionSpan="false"
|
||||||
|
new-btn-label="Hinzufügen"
|
||||||
|
new-btn-show
|
||||||
|
@click:new="openNewModal">
|
||||||
|
|
||||||
|
<template #actions>
|
||||||
|
<form-input
|
||||||
|
type="select"
|
||||||
|
v-model="studiensemester_kurzbz"
|
||||||
|
name="studiensemester_kurzbz"
|
||||||
|
>
|
||||||
|
<option :value="null" disabled>-- {{ $p.t('lehre', 'studiensemester') }} --</option>
|
||||||
|
<option
|
||||||
|
v-for="studiensemester in config.studiensemestern"
|
||||||
|
:key="studiensemester.studiensemester_kurzbz"
|
||||||
|
:value="studiensemester.studiensemester_kurzbz"
|
||||||
|
>
|
||||||
|
{{ studiensemester.studiensemester_kurzbz }}
|
||||||
|
</option>
|
||||||
|
</form-input>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
@click="openStartModal"
|
||||||
|
>
|
||||||
|
{{ $p.t('global', 'jetztStarten') }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</core-filter-cmpt>
|
||||||
|
|
||||||
|
<tempus-sync-modal
|
||||||
|
ref="syncModal"
|
||||||
|
:config="config"
|
||||||
|
:stsem_kurzbz="studiensemester_kurzbz"
|
||||||
|
@saved="reloadTable"
|
||||||
|
>
|
||||||
|
</tempus-sync-modal>
|
||||||
|
</template>
|
||||||
|
</core-base-layout>
|
||||||
|
|
||||||
|
`
|
||||||
|
};
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
export function getTempusShortcuts(self) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: 'F',
|
||||||
|
ctrl: true,
|
||||||
|
skipWhenTyping: false,
|
||||||
|
handler: () => self.focusSearchbar()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'f',
|
||||||
|
ctrl: true,
|
||||||
|
skipWhenTyping: false,
|
||||||
|
handler: () => self.focusSearchbar()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'F9',
|
||||||
|
ctrl: false,
|
||||||
|
skipWhenTyping: false,
|
||||||
|
handler: () => self.updateCollision('month')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
ctrl: true,
|
||||||
|
skipWhenTyping: false,
|
||||||
|
handler: () => self.handleChangeMode('month')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
ctrl: true,
|
||||||
|
skipWhenTyping: false,
|
||||||
|
handler: () => self.handleChangeMode('week')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3',
|
||||||
|
ctrl: true,
|
||||||
|
skipWhenTyping: false,
|
||||||
|
handler: () => self.handleChangeMode('tableList')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ArrowLeft',
|
||||||
|
ctrl: true,
|
||||||
|
skipWhenTyping: false,
|
||||||
|
handler: () => self.$refs.calendar.navigatePrev()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ArrowRight',
|
||||||
|
ctrl: true,
|
||||||
|
skipWhenTyping: false,
|
||||||
|
handler: () => self.$refs.calendar.navigateNext()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'p',
|
||||||
|
skipWhenTyping: true,
|
||||||
|
handler: () => self.parkHoveredEvent()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'P',
|
||||||
|
skipWhenTyping: true,
|
||||||
|
handler: () => self.parkHoveredEvent()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Delete',
|
||||||
|
skipWhenTyping: true,
|
||||||
|
handler: () => self.deleteHoveredEvent()
|
||||||
|
},
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -135,7 +135,7 @@ export function useEventLoader(rangeInterval, getPromiseFunc) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Vue.watchEffect(reload);
|
Vue.watchEffect(reload);
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
loading_id = 0;
|
loading_id = 0;
|
||||||
events.value = [];
|
events.value = [];
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
export function useContextMenuActions(handlers)
|
||||||
|
{
|
||||||
|
return Vue.computed(() => ({
|
||||||
|
lehreinheit: [
|
||||||
|
{
|
||||||
|
label: 'Raumauswahl',
|
||||||
|
icon: 'fa-solid fa-door-open',
|
||||||
|
action: handlers.openRaumauswahl
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Freischalten für Voransicht',
|
||||||
|
icon: 'fa-solid fa-chalkboard-user',
|
||||||
|
action: handlers.syncToLecturer
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Freischalten für Live',
|
||||||
|
icon: 'fa-solid fa-user-graduate',
|
||||||
|
action: handlers.syncToStudent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'History',
|
||||||
|
icon: 'fa-solid fa-clock-rotate-left',
|
||||||
|
action: handlers.openHistory
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Delete',
|
||||||
|
icon: 'fa-solid fa-calendar-xmark',
|
||||||
|
action: handlers.deleteEntry
|
||||||
|
},
|
||||||
|
],
|
||||||
|
reservierung: [
|
||||||
|
{
|
||||||
|
label: 'Delete',
|
||||||
|
icon: 'fa-solid fa-calendar-xmark',
|
||||||
|
action: handlers.deleteEntry
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
}
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
/**
|
|
||||||
* TODO(chris): This needs serious rework!!!
|
|
||||||
*/
|
|
||||||
export default {
|
|
||||||
mounted(el, binding, vnode) {
|
|
||||||
if (binding.arg == 'draggable') {
|
|
||||||
el.addEventListener('update-my-value', evt => {
|
|
||||||
evt.preventDefault();
|
|
||||||
binding.value = evt.detail.item;
|
|
||||||
});
|
|
||||||
el.addEventListener('dragstart', evt => {
|
|
||||||
el.dispatchEvent(new CustomEvent('calendar-dragstart', {
|
|
||||||
cancelable: true,
|
|
||||||
bubbles: true,
|
|
||||||
detail: {
|
|
||||||
item: binding.value,
|
|
||||||
x: evt.offsetX / el.offsetWidth,
|
|
||||||
y: evt.offsetY / el.offsetHeight,
|
|
||||||
originalEvent: evt
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
el.addEventListener('dragend', evt => {
|
|
||||||
el.dispatchEvent(new CustomEvent('calendar-dragend', {
|
|
||||||
cancelable: true,
|
|
||||||
bubbles: true,
|
|
||||||
detail: {
|
|
||||||
item: binding.value,
|
|
||||||
originalEvent: evt
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
} else if (binding.arg == 'dropcage') {
|
|
||||||
let hitbox = null;
|
|
||||||
el.addEventListener('dragover', evt => {
|
|
||||||
if (hitbox)
|
|
||||||
return;
|
|
||||||
hitbox = el.getBoundingClientRect();
|
|
||||||
return el.dispatchEvent(new CustomEvent('calendar-dragenter', {
|
|
||||||
detail: { originalEvent: evt }
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
window.addEventListener('dragleave', evt => {
|
|
||||||
if (!hitbox)
|
|
||||||
return;
|
|
||||||
let pos;
|
|
||||||
if (typeof evt.clientX === 'undefined')
|
|
||||||
pos = {
|
|
||||||
x: evt.pageX + document.documentElement.scrollLeft,
|
|
||||||
y: evt.pageY + document.documentElement.scrollTop
|
|
||||||
};
|
|
||||||
else
|
|
||||||
pos = {
|
|
||||||
x: evt.clientX + document.body.scrollLeft + document.documentElement.scrollLeft,
|
|
||||||
y: evt.clientY + document.body.scrollTop + document.documentElement.scrollTop
|
|
||||||
};
|
|
||||||
if (pos.x > hitbox.left + hitbox.width - 1 || pos.x < hitbox.left || pos.y > hitbox.top + hitbox.height - 1 || pos.y < hitbox.top) {
|
|
||||||
hitbox = null;
|
|
||||||
return el.dispatchEvent(new CustomEvent('calendar-dragleave', {
|
|
||||||
detail: { originalEvent: evt }
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.addEventListener('drop', evt => {
|
|
||||||
if (!hitbox)
|
|
||||||
return;
|
|
||||||
|
|
||||||
hitbox = null;
|
|
||||||
return el.dispatchEvent(new CustomEvent('calendar-dragleave', {
|
|
||||||
detail: { originalEvent: evt }
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
} else if (binding.arg == 'dropzone') {
|
|
||||||
el.addEventListener(
|
|
||||||
binding.modifiers.once ? 'dragenter' : 'dragover',
|
|
||||||
evt => {
|
|
||||||
const timestamp = binding.value instanceof Function
|
|
||||||
? binding.value(evt)
|
|
||||||
: binding.value;
|
|
||||||
const detail = timestamp.timestamp ? timestamp : { timestamp };
|
|
||||||
el.dispatchEvent(new CustomEvent('calendar-dragchange', {
|
|
||||||
cancelable: true,
|
|
||||||
bubbles: true,
|
|
||||||
detail
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updated(el, binding, vnode, prevVnode) {
|
|
||||||
if (binding.arg == 'draggable') {
|
|
||||||
el.dispatchEvent(new CustomEvent('update-my-value', {
|
|
||||||
cancelable: true,
|
|
||||||
detail: {
|
|
||||||
item: binding.value
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -37,7 +37,8 @@ export default {
|
|||||||
function onStart(evt) {
|
function onStart(evt) {
|
||||||
const value = el.dataset.fhcDraggableValue;
|
const value = el.dataset.fhcDraggableValue;
|
||||||
if (value) {
|
if (value) {
|
||||||
setTransferData(evt, JSON.parse(value), true);
|
let disableImage = binding.modifiers?.noimage === true;
|
||||||
|
setTransferData(evt, JSON.parse(value), !disableImage);
|
||||||
if (el.dataset.fhcEffectAllowed)
|
if (el.dataset.fhcEffectAllowed)
|
||||||
evt.dataTransfer.effectAllowed = el.dataset.fhcEffectAllowed;
|
evt.dataTransfer.effectAllowed = el.dataset.fhcEffectAllowed;
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export default {
|
|||||||
result = [result];
|
result = [result];
|
||||||
|
|
||||||
const res = binding.value(evt, result);
|
const res = binding.value(evt, result);
|
||||||
|
|
||||||
if (res instanceof Promise) {
|
if (res instanceof Promise) {
|
||||||
res.then(r => {
|
res.then(r => {
|
||||||
bcc.postMessage('release');
|
bcc.postMessage('release');
|
||||||
|
|||||||
@@ -5,11 +5,16 @@
|
|||||||
const TYPE_DEFINITION = {
|
const TYPE_DEFINITION = {
|
||||||
lehreinheit: {
|
lehreinheit: {
|
||||||
id: "lehreinheit_id",
|
id: "lehreinheit_id",
|
||||||
dragIcon: "fa-solid fa-chalkboard-user",
|
|
||||||
extras: [
|
extras: [
|
||||||
"stundenblockung"
|
"stundenblockung"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
kalender: {
|
||||||
|
id: "kalender_id",
|
||||||
|
},
|
||||||
|
reservierung: {
|
||||||
|
id: "kalender_id",
|
||||||
|
},
|
||||||
vevent: {
|
vevent: {
|
||||||
id: "uid",
|
id: "uid",
|
||||||
dragIcon: "fa-solid fa-calendar",
|
dragIcon: "fa-solid fa-calendar",
|
||||||
@@ -53,12 +58,16 @@ function isValidDragObject(value) {
|
|||||||
if (!Object.prototype.hasOwnProperty.call(value, 'values'))
|
if (!Object.prototype.hasOwnProperty.call(value, 'values'))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
if (!VALID_TYPES.includes(value.type.substr(0, value.type.length-11)))
|
if (!VALID_TYPES.includes(value.type.substr(0, value.type.length-11)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(value, 'id'))
|
if (!Object.prototype.hasOwnProperty.call(value, 'id'))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
if (!VALID_TYPES.includes(value.type))
|
if (!VALID_TYPES.includes(value.type))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -182,7 +191,8 @@ function convertToValidDragObject(data, strict) {
|
|||||||
const found = Object.entries(TYPE_DEFINITION).find(([ , typedef ]) => {
|
const found = Object.entries(TYPE_DEFINITION).find(([ , typedef ]) => {
|
||||||
if (!Object.prototype.hasOwnProperty.call(data, typedef.id))
|
if (!Object.prototype.hasOwnProperty.call(data, typedef.id))
|
||||||
return false;
|
return false;
|
||||||
if (typedef.extras) {
|
if (typedef.extras)
|
||||||
|
{
|
||||||
if (!typedef.extras.every(extra => Object.prototype.hasOwnProperty.call(data, extra)))
|
if (!typedef.extras.every(extra => Object.prototype.hasOwnProperty.call(data, extra)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -198,6 +208,7 @@ function convertToValidDragObject(data, strict) {
|
|||||||
const newData = {};
|
const newData = {};
|
||||||
newData.type = type;
|
newData.type = type;
|
||||||
newData.id = data[typedef.id];
|
newData.id = data[typedef.id];
|
||||||
|
|
||||||
if (typedef.extras)
|
if (typedef.extras)
|
||||||
typedef.extras.forEach(extra => newData[extra] = data[extra]);
|
typedef.extras.forEach(extra => newData[extra] = data[extra]);
|
||||||
|
|
||||||
@@ -205,6 +216,8 @@ function convertToValidDragObject(data, strict) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setTransferData(event, validDragObject, setDragImage = false) {
|
function setTransferData(event, validDragObject, setDragImage = false) {
|
||||||
|
|
||||||
|
|
||||||
if (setDragImage) {
|
if (setDragImage) {
|
||||||
const dragItems = Array.isArray(validDragObject) ? validDragObject : [ validDragObject ];
|
const dragItems = Array.isArray(validDragObject) ? validDragObject : [ validDragObject ];
|
||||||
const dragElements = dragItems.map(item => {
|
const dragElements = dragItems.map(item => {
|
||||||
@@ -246,9 +259,9 @@ function setTransferData(event, validDragObject, setDragImage = false) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (Array.isArray(validDragObject)) {
|
if (Array.isArray(validDragObject)) {
|
||||||
|
|
||||||
return validDragObject.forEach(data => setTransferData(event, data));
|
return validDragObject.forEach(data => setTransferData(event, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
event.dataTransfer.setData('application/fhc-' + validDragObject.type, JSON.stringify(validDragObject));
|
event.dataTransfer.setData('application/fhc-' + validDragObject.type, JSON.stringify(validDragObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,16 +280,16 @@ function eventHasTypes(event, allowedTypes, strict) {
|
|||||||
allowedTypes = allowedTypes.map(type => 'application/fhc-' + type);
|
allowedTypes = allowedTypes.map(type => 'application/fhc-' + type);
|
||||||
|
|
||||||
const dataTypes = [...event.dataTransfer.types];
|
const dataTypes = [...event.dataTransfer.types];
|
||||||
|
|
||||||
// NOTE(chris): if dragging across browsers the dataTransfer object is
|
// NOTE(chris): if dragging across browsers the dataTransfer object is
|
||||||
// set to a default one without data. Since we do not support dragging
|
// set to a default one without data. Since we do not support dragging
|
||||||
// across browsers (yet) we return false which will disallow dropping.
|
// across browsers (yet) we return false which will disallow dropping.
|
||||||
if (!dataTypes.length)
|
if (!dataTypes.length)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!strict)
|
if (!strict)
|
||||||
return allowedTypes.some(type => [...event.dataTransfer.types].includes(type));
|
return allowedTypes.some(type => [...event.dataTransfer.types].includes(type));
|
||||||
|
|
||||||
return [...event.dataTransfer.types].every(type => allowedTypes.includes(type));
|
return [...event.dataTransfer.types].every(type => allowedTypes.includes(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
export function useResizeGhost() {
|
||||||
|
let ghostEl = null;
|
||||||
|
let labelEl = null;
|
||||||
|
|
||||||
|
function create(gridEl, eventEl, edge)
|
||||||
|
{
|
||||||
|
const gridRect = gridEl.getBoundingClientRect();
|
||||||
|
const eventRect = eventEl.getBoundingClientRect();
|
||||||
|
const scrollTop = gridEl.scrollTop;
|
||||||
|
|
||||||
|
const topInGrid = (eventRect.top - gridRect.top) + scrollTop;
|
||||||
|
const leftInGrid = eventRect.left - gridRect.left;
|
||||||
|
|
||||||
|
ghostEl = document.createElement('div');
|
||||||
|
ghostEl.className = 'fhc-event-ghost';
|
||||||
|
ghostEl.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
left: ${leftInGrid}px;
|
||||||
|
width: ${eventRect.width}px;
|
||||||
|
top: ${topInGrid}px;
|
||||||
|
height: ${eventRect.height}px;
|
||||||
|
z-index: 9999;
|
||||||
|
pointer-events: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 6px;
|
||||||
|
outline: 2px dashed currentColor;
|
||||||
|
opacity: 0.9;
|
||||||
|
`;
|
||||||
|
|
||||||
|
labelEl = document.createElement('div');
|
||||||
|
labelEl.className = 'fhc-resize-preview';
|
||||||
|
labelEl.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
right: 6px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
background: rgba(0,0,0,0.75);
|
||||||
|
color: white;
|
||||||
|
white-space: nowrap;
|
||||||
|
pointer-events: none;
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (edge === 'start')
|
||||||
|
{
|
||||||
|
labelEl.style.top = '6px';
|
||||||
|
labelEl.style.bottom = 'auto';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
labelEl.style.top = 'auto';
|
||||||
|
labelEl.style.bottom = '6px';
|
||||||
|
}
|
||||||
|
|
||||||
|
ghostEl.appendChild(labelEl);
|
||||||
|
|
||||||
|
gridEl.style.position = 'relative';
|
||||||
|
gridEl.appendChild(ghostEl);
|
||||||
|
|
||||||
|
return {
|
||||||
|
startTop: topInGrid,
|
||||||
|
startHeight: eventRect.height
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLabel(text)
|
||||||
|
{
|
||||||
|
if (labelEl)
|
||||||
|
labelEl.textContent = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePosition(top, height)
|
||||||
|
{
|
||||||
|
if (!ghostEl)
|
||||||
|
return;
|
||||||
|
if (top !== null)
|
||||||
|
ghostEl.style.top = `${top}px`;
|
||||||
|
if (height !== null)
|
||||||
|
ghostEl.style.height = `${height}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPosition()
|
||||||
|
{
|
||||||
|
if (!ghostEl)
|
||||||
|
return { top: 0, height: 0 };
|
||||||
|
return {
|
||||||
|
top: parseFloat(ghostEl.style.top),
|
||||||
|
height: parseFloat(ghostEl.style.height)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove()
|
||||||
|
{
|
||||||
|
if (ghostEl?.parentNode)
|
||||||
|
ghostEl.parentNode.removeChild(ghostEl);
|
||||||
|
|
||||||
|
ghostEl = null;
|
||||||
|
labelEl = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { create, updateLabel, updatePosition, getPosition, remove };
|
||||||
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
import { useResizeGhost } from './ResizeGhost.js';
|
||||||
|
|
||||||
|
const MIN_HEIGHT_PX = 20;
|
||||||
|
const MIN_DURATION_MIN = 5;
|
||||||
|
const SNAP_MINUTES = 5;
|
||||||
|
|
||||||
|
function snapToGrid(minutes, edge)
|
||||||
|
{
|
||||||
|
if (edge === 'start')
|
||||||
|
{
|
||||||
|
return minutes >= 0 ? Math.floor(minutes / SNAP_MINUTES) * SNAP_MINUTES : Math.ceil(minutes / SNAP_MINUTES) * SNAP_MINUTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
return minutes >= 0 ? Math.ceil(minutes / SNAP_MINUTES) * SNAP_MINUTES : Math.floor(minutes / SNAP_MINUTES) * SNAP_MINUTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSnapTimes(timeGrid, dayISO, zoneName)
|
||||||
|
{
|
||||||
|
const parseTime = (time) =>
|
||||||
|
{
|
||||||
|
if (!time)
|
||||||
|
return null;
|
||||||
|
let dt = luxon.DateTime.fromFormat(`${dayISO} ${time}`, 'yyyy-MM-dd HH:mm:ss', { zone: zoneName });
|
||||||
|
|
||||||
|
if (!dt.isValid)
|
||||||
|
dt = luxon.DateTime.fromFormat(`${dayISO} ${time}`, 'yyyy-MM-dd HH:mm', { zone: zoneName });
|
||||||
|
|
||||||
|
return dt.isValid ? dt : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const startTimes = timeGrid.map(s => parseTime(s?.start)).filter(Boolean);
|
||||||
|
const endTimes = timeGrid.map(s => parseTime(s?.end)).filter(Boolean);
|
||||||
|
|
||||||
|
const sort = arr => arr.sort((a, b) => a.toMillis() - b.toMillis());
|
||||||
|
|
||||||
|
return {
|
||||||
|
start: sort(startTimes),
|
||||||
|
end: sort(endTimes),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateNewTimes(activeResize, ghostPosition)
|
||||||
|
{
|
||||||
|
const { edge, event, timeGrid, startTop, startHeight } = activeResize;
|
||||||
|
const { start, end } = event;
|
||||||
|
|
||||||
|
const durationMinutes = end.diff(start, 'minutes').minutes;
|
||||||
|
if (!durationMinutes || durationMinutes <= 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const pxPerMinute = startHeight / durationMinutes;
|
||||||
|
|
||||||
|
let draggedPx = 0;
|
||||||
|
if (edge === 'end')
|
||||||
|
draggedPx = ghostPosition.height - startHeight;
|
||||||
|
if (edge === 'start')
|
||||||
|
draggedPx = ghostPosition.top - startTop;
|
||||||
|
|
||||||
|
const draggedMinutes = snapToGrid(draggedPx / pxPerMinute, edge);
|
||||||
|
|
||||||
|
let newStart = start;
|
||||||
|
let newEnd = end;
|
||||||
|
|
||||||
|
if (edge === 'start')
|
||||||
|
newStart = start.plus({ minutes: draggedMinutes });
|
||||||
|
if (edge === 'end')
|
||||||
|
newEnd = end.plus({ minutes: draggedMinutes });
|
||||||
|
|
||||||
|
if (Array.isArray(timeGrid) && timeGrid.length)
|
||||||
|
{
|
||||||
|
const snapTimes = getSnapTimes(timeGrid, start.toISODate(), start.zoneName);
|
||||||
|
|
||||||
|
if (edge === 'start')
|
||||||
|
{
|
||||||
|
const targets = snapTimes.start;
|
||||||
|
newStart = [...targets].reverse().find(t => t <= newStart) || targets[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const targets = snapTimes.end;
|
||||||
|
newEnd = targets.find(t => t >= newEnd) || targets[targets.length - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { newStart, newEnd };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function useResizeHandler() {
|
||||||
|
const ghost = useResizeGhost();
|
||||||
|
|
||||||
|
let activeResize = null;
|
||||||
|
|
||||||
|
function getPointerYInGrid(evt)
|
||||||
|
{
|
||||||
|
const gridRect = activeResize.gridEl.getBoundingClientRect();
|
||||||
|
return (evt.clientY - gridRect.top) + activeResize.gridEl.scrollTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGhostLabel()
|
||||||
|
{
|
||||||
|
const result = calculateNewTimes(activeResize, ghost.getPosition());
|
||||||
|
if (!result)
|
||||||
|
return;
|
||||||
|
ghost.updateLabel(`${result.newStart.toFormat('HH:mm')}–${result.newEnd.toFormat('HH:mm')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPointerMove(evt)
|
||||||
|
{
|
||||||
|
if (!activeResize || evt.pointerId !== activeResize.pointerId)
|
||||||
|
return;
|
||||||
|
evt.preventDefault();
|
||||||
|
|
||||||
|
const maxBottom = activeResize.gridEl.scrollHeight;
|
||||||
|
const pointerY = getPointerYInGrid(evt);
|
||||||
|
const draggedPx = pointerY - activeResize.dragStartY;
|
||||||
|
|
||||||
|
if (activeResize.edge === 'end')
|
||||||
|
{
|
||||||
|
let newHeight = Math.max(MIN_HEIGHT_PX, activeResize.startHeight + draggedPx);
|
||||||
|
if (activeResize.startTop + newHeight > maxBottom)
|
||||||
|
newHeight = maxBottom - activeResize.startTop;
|
||||||
|
|
||||||
|
ghost.updatePosition(null, newHeight);
|
||||||
|
}
|
||||||
|
else if (activeResize.edge === 'start')
|
||||||
|
{
|
||||||
|
let newTop = activeResize.startTop + draggedPx;
|
||||||
|
let newHeight = activeResize.startHeight - draggedPx;
|
||||||
|
|
||||||
|
if (newTop < 0)
|
||||||
|
{
|
||||||
|
newHeight -= (0 - newTop);
|
||||||
|
newTop = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newHeight < MIN_HEIGHT_PX)
|
||||||
|
{
|
||||||
|
newTop = (activeResize.startTop + activeResize.startHeight) - MIN_HEIGHT_PX;
|
||||||
|
newHeight = MIN_HEIGHT_PX;
|
||||||
|
}
|
||||||
|
|
||||||
|
ghost.updatePosition(newTop, newHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGhostLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPointerUp(evt)
|
||||||
|
{
|
||||||
|
if (!activeResize || evt.pointerId !== activeResize.pointerId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
window.removeEventListener('pointermove', onPointerMove);
|
||||||
|
window.removeEventListener('pointerup', onPointerUp);
|
||||||
|
|
||||||
|
if (activeResize.eventEl)
|
||||||
|
activeResize.eventEl.style.opacity = activeResize.originalOpacity ?? '';
|
||||||
|
|
||||||
|
const result = calculateNewTimes(activeResize, ghost.getPosition());
|
||||||
|
|
||||||
|
ghost.remove();
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
if ((activeResize.event.start.toISO() !== result.newStart.toISO()) || (activeResize.event.end.toISO() !== result.newEnd.toISO()))
|
||||||
|
{
|
||||||
|
activeResize.onEnd({
|
||||||
|
event: activeResize.event,
|
||||||
|
newStart: result.newStart.toISO(),
|
||||||
|
newEnd: result.newEnd.toISO()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activeResize = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startResize(edge, evt, { el, gridEl, event, timeGrid, onEnd })
|
||||||
|
{
|
||||||
|
const { startTop, startHeight } = ghost.create(gridEl, el, edge);
|
||||||
|
|
||||||
|
activeResize = {
|
||||||
|
edge,
|
||||||
|
pointerId: evt.pointerId,
|
||||||
|
eventEl: el,
|
||||||
|
gridEl,
|
||||||
|
event,
|
||||||
|
timeGrid,
|
||||||
|
onEnd,
|
||||||
|
dragStartY: (evt.clientY - gridEl.getBoundingClientRect().top) + gridEl.scrollTop,
|
||||||
|
startTop,
|
||||||
|
startHeight,
|
||||||
|
originalOpacity: el.style.opacity,
|
||||||
|
};
|
||||||
|
|
||||||
|
el.style.opacity = '0.35';
|
||||||
|
ghost.updateLabel(`${event.start.toFormat('HH:mm')}–${event.end.toFormat('HH:mm')}`);
|
||||||
|
|
||||||
|
evt.currentTarget.setPointerCapture(evt.pointerId);
|
||||||
|
window.addEventListener('pointermove', onPointerMove, { passive: false });
|
||||||
|
window.addEventListener('pointerup', onPointerUp, { passive: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup()
|
||||||
|
{
|
||||||
|
if (!activeResize)
|
||||||
|
return;
|
||||||
|
|
||||||
|
window.removeEventListener('pointermove', onPointerMove);
|
||||||
|
window.removeEventListener('pointerup', onPointerUp);
|
||||||
|
ghost.remove();
|
||||||
|
activeResize = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { startResize, cleanup };
|
||||||
|
}
|
||||||
@@ -84,6 +84,7 @@ require_once('dbupdate_3.4/60882_lehrfaecherverteilung_favorites.php');
|
|||||||
require_once('dbupdate_3.4/66982_berufsschule.php');
|
require_once('dbupdate_3.4/66982_berufsschule.php');
|
||||||
require_once('dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php');
|
require_once('dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php');
|
||||||
require_once('dbupdate_3.4/47972_pruefungsverwaltung_ects_angabe.php');
|
require_once('dbupdate_3.4/47972_pruefungsverwaltung_ects_angabe.php');
|
||||||
|
require_once('dbupdate_3.4/46975_tempus.php');
|
||||||
require_once('dbupdate_3.4/62063_lv_evaluierung.php');
|
require_once('dbupdate_3.4/62063_lv_evaluierung.php');
|
||||||
require_once('dbupdate_3.4/67490_studstatus_suche_abort_controller_haengt.php');
|
require_once('dbupdate_3.4/67490_studstatus_suche_abort_controller_haengt.php');
|
||||||
require_once('dbupdate_3.4/61164_abgabetool_quality_gates.php');
|
require_once('dbupdate_3.4/61164_abgabetool_quality_gates.php');
|
||||||
@@ -98,6 +99,7 @@ require_once('dbupdate_3.4/75888_reihungstest_mehrfachdurchfuehrung.php');
|
|||||||
require_once('dbupdate_3.4/76150_perm_other_lv_plan.php');
|
require_once('dbupdate_3.4/76150_perm_other_lv_plan.php');
|
||||||
require_once('dbupdate_3.4/68957_dashboard_bookmark_neue_Spalte_sort.php');
|
require_once('dbupdate_3.4/68957_dashboard_bookmark_neue_Spalte_sort.php');
|
||||||
require_once('dbupdate_3.4/68530_Dashboard_Cleanup.php');
|
require_once('dbupdate_3.4/68530_Dashboard_Cleanup.php');
|
||||||
|
require_once('dbupdate_3.4/77949_tempus_synchronisation.php');
|
||||||
|
|
||||||
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
|
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
|
||||||
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
|
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
|
||||||
|
|||||||
@@ -0,0 +1,251 @@
|
|||||||
|
<?php
|
||||||
|
if (! defined('DB_NAME')) exit('No direct script access allowed');
|
||||||
|
|
||||||
|
// Kalender Tabelle fuer neues Tempus
|
||||||
|
if(!$result = @$db->db_query("SELECT kalender_id FROM lehre.tbl_kalender LIMIT 1"))
|
||||||
|
{
|
||||||
|
$qry = "CREATE TABLE lehre.tbl_kalender (
|
||||||
|
kalender_id bigserial NOT NULL,
|
||||||
|
von timestamp NOT NULL,
|
||||||
|
bis timestamp NOT NULL,
|
||||||
|
typ character varying(32),
|
||||||
|
status_kurzbz character varying(32),
|
||||||
|
vorgaenger_kalender_id bigint,
|
||||||
|
insertamum timestamp DEFAULT now(),
|
||||||
|
insertvon character varying(32),
|
||||||
|
updateamum timestamp DEFAULT now(),
|
||||||
|
updatevon character varying(32),
|
||||||
|
CONSTRAINT tbl_kalender_pk PRIMARY KEY (kalender_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE lehre.tbl_kalender IS 'Schedule Calendar Events';
|
||||||
|
|
||||||
|
CREATE TABLE lehre.tbl_kalender_typ (
|
||||||
|
typ character varying(32) NOT NULL,
|
||||||
|
CONSTRAINT tbl_kalender_typ_pk PRIMARY KEY (typ)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE lehre.tbl_kalender_typ IS 'Type of Calendar Events';
|
||||||
|
|
||||||
|
INSERT INTO lehre.tbl_kalender_typ (typ) VALUES (E'lehreinheit');
|
||||||
|
INSERT INTO lehre.tbl_kalender_typ (typ) VALUES (E'reservierung');
|
||||||
|
INSERT INTO lehre.tbl_kalender_typ (typ) VALUES (E'event');
|
||||||
|
|
||||||
|
CREATE TABLE lehre.tbl_kalender_lehreinheit (
|
||||||
|
lehreinheit_id integer NOT NULL,
|
||||||
|
kalender_id bigint NOT NULL,
|
||||||
|
CONSTRAINT tbl_kalender_lehreinheit_pk PRIMARY KEY (lehreinheit_id,kalender_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE lehre.tbl_kalender_lehreinheit IS 'Connects Calender Events to Courses';
|
||||||
|
|
||||||
|
ALTER TABLE lehre.tbl_kalender_lehreinheit ADD CONSTRAINT tbl_lehreinheit_fk FOREIGN KEY (lehreinheit_id)
|
||||||
|
REFERENCES lehre.tbl_lehreinheit (lehreinheit_id) MATCH FULL
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
CREATE TABLE lehre.tbl_kalender_ort (
|
||||||
|
kalender_ort_id bigserial NOT NULL,
|
||||||
|
location text,
|
||||||
|
ort_kurzbz character varying(32),
|
||||||
|
kalender_id bigint,
|
||||||
|
CONSTRAINT tbl_kalender_ort_pk PRIMARY KEY (kalender_ort_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE lehre.tbl_kalender_ort IS E'Connects one Calendar Entry to multiple Rooms';
|
||||||
|
|
||||||
|
COMMENT ON COLUMN lehre.tbl_kalender_ort.location IS E'Text Description if not a physical inhouse Room (External Location, Conference Link, etc)';
|
||||||
|
|
||||||
|
ALTER TABLE lehre.tbl_kalender_ort ADD CONSTRAINT tbl_kalender_fk FOREIGN KEY (kalender_id)
|
||||||
|
REFERENCES lehre.tbl_kalender (kalender_id) MATCH FULL
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE lehre.tbl_kalender_ort ADD CONSTRAINT tbl_ort_fk FOREIGN KEY (ort_kurzbz)
|
||||||
|
REFERENCES public.tbl_ort (ort_kurzbz) MATCH FULL
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE lehre.tbl_kalender_lehreinheit ADD CONSTRAINT tbl_kalender_fk FOREIGN KEY (kalender_id)
|
||||||
|
REFERENCES lehre.tbl_kalender (kalender_id) MATCH FULL
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
CREATE TABLE lehre.tbl_kalender_status (
|
||||||
|
status_kurzbz character varying(32) NOT NULL,
|
||||||
|
bezeichnung text,
|
||||||
|
bezeichnung_mehrsprachig character varying(255)[] NOT NULL,
|
||||||
|
sort smallint,
|
||||||
|
CONSTRAINT tbl_kalender_status_pk PRIMARY KEY (status_kurzbz)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE lehre.tbl_kalender_status IS 'Calender visibility Status';
|
||||||
|
|
||||||
|
INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung, bezeichnung_mehrsprachig, sort) VALUES (E'planning', E'planning', E'{\"In Planung\", \"Planning\"}', 1);
|
||||||
|
INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung, bezeichnung_mehrsprachig, sort) VALUES (E'sync_preview', E'sync_preview', E'{\"Synchronisierung für Voransicht\", \"Sync for Preview\"}', 2);
|
||||||
|
INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung, bezeichnung_mehrsprachig, sort) VALUES (E'preview', E'preview', E'{\"Voransicht\", \"Preview\"}', 3);
|
||||||
|
INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung, bezeichnung_mehrsprachig, sort) VALUES (E'sync_live', E'sync_live', E'{\"Synchronisierung für Live\", \"Sync for Live\"}', 4);
|
||||||
|
INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung, bezeichnung_mehrsprachig, sort) VALUES (E'live', E'Sichtbar für Studierende', E'{\"Live\", \"Live\"}', 5);
|
||||||
|
INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung, bezeichnung_mehrsprachig, sort) VALUES (E'todelete', E'todelete', E'{\"Zu löschen\", \"To delete\"}', 6);
|
||||||
|
INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung, bezeichnung_mehrsprachig, sort) VALUES (E'deleted', E'deleted', E'{\"Gelöscht\", \"Deleted\"}', 7);
|
||||||
|
INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung, bezeichnung_mehrsprachig, sort) VALUES (E'archived', E'archived', E'{\"Archiviert\", \"Archived\"}', 8);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE lehre.tbl_kalender ADD CONSTRAINT tbl_kalender_status_fk FOREIGN KEY (status_kurzbz)
|
||||||
|
REFERENCES lehre.tbl_kalender_status (status_kurzbz) MATCH FULL
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE lehre.tbl_kalender ADD CONSTRAINT tbl_kalender_typ_fk FOREIGN KEY (typ)
|
||||||
|
REFERENCES lehre.tbl_kalender_typ (typ) MATCH FULL
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender to vilesci;
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender to web;
|
||||||
|
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_status to vilesci;
|
||||||
|
GRANT SELECT ON lehre.tbl_kalender_status to web;
|
||||||
|
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_lehreinheit to vilesci;
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_lehreinheit to web;
|
||||||
|
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_ort to vilesci;
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_ort to web;
|
||||||
|
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_typ to vilesci;
|
||||||
|
GRANT SELECT ON lehre.tbl_kalender_typ to web;
|
||||||
|
|
||||||
|
CREATE TABLE sync.tbl_stundenplandev_kalender(
|
||||||
|
stundenplandev_kalender_id bigserial NOT NULL,
|
||||||
|
stundenplandev_id integer NOT NULL,
|
||||||
|
kalender_id bigint NOT NULL,
|
||||||
|
lastupdate timestamp,
|
||||||
|
CONSTRAINT tbl_stundenplandev_kalender_pk PRIMARY KEY (stundenplandev_kalender_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON sync.tbl_stundenplandev_kalender to vilesci;
|
||||||
|
COMMENT ON TABLE sync.tbl_stundenplandev_kalender IS 'Migration from old Stundenplan to new Kalender Table';
|
||||||
|
|
||||||
|
GRANT USAGE ON lehre.tbl_kalender_kalender_id_seq TO vilesci;
|
||||||
|
GRANT USAGE ON lehre.tbl_kalender_kalender_id_seq TO web;
|
||||||
|
GRANT USAGE ON sync.tbl_stundenplandev_kalender_stundenplandev_kalender_id_seq TO vilesci;
|
||||||
|
GRANT USAGE ON lehre.tbl_kalender_ort_kalender_ort_id_seq TO vilesci;
|
||||||
|
|
||||||
|
CREATE INDEX idx_kalender_ort_kalender_id ON lehre.tbl_kalender_ort USING btree (kalender_id);
|
||||||
|
CREATE INDEX idx_kalender_ort_kalender_id_ort_kurzbz ON lehre.tbl_kalender_ort USING btree (ort_kurzbz, kalender_id);
|
||||||
|
CREATE INDEX idx_kalender_von ON lehre.tbl_kalender USING btree (von);
|
||||||
|
";
|
||||||
|
|
||||||
|
if(!$db->db_query($qry))
|
||||||
|
echo '<strong>lehre.tbl_kalender: '.$db->db_last_error().'</strong><br>';
|
||||||
|
else
|
||||||
|
echo '<br>lehre.tbl_kalender: neue Tabellen hinzugefuegt';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$result = @$db->db_query("SELECT kalender_id FROM lehre.tbl_kalender_event LIMIT 1"))
|
||||||
|
{
|
||||||
|
$qry = "CREATE TABLE lehre.tbl_kalender_event (
|
||||||
|
kalender_id bigint NOT NULL,
|
||||||
|
titel character varying(255),
|
||||||
|
beschreibung text,
|
||||||
|
CONSTRAINT tbl_kalender_event_pk PRIMARY KEY (kalender_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE lehre.tbl_kalender_event ADD CONSTRAINT tbl_kalender_event_fk FOREIGN KEY (kalender_id)
|
||||||
|
REFERENCES lehre.tbl_kalender (kalender_id)
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_event to vilesci;
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_event to web;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE lehre.tbl_kalender_event_rolle (
|
||||||
|
rolle_kurzbz character varying(32) NOT NULL,
|
||||||
|
bezeichnung text,
|
||||||
|
bezeichnung_mehrsprachig character varying(255)[] NOT NULL,
|
||||||
|
sort smallint,
|
||||||
|
CONSTRAINT tbl_kalender_event_rolle_pk PRIMARY KEY (rolle_kurzbz)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO lehre.tbl_kalender_event_rolle (rolle_kurzbz, bezeichnung, bezeichnung_mehrsprachig, sort) VALUES (E'organisator', E'Organisierende', E'{\"Organisierende\", \"Organizer\"}', 1);
|
||||||
|
INSERT INTO lehre.tbl_kalender_event_rolle (rolle_kurzbz, bezeichnung, bezeichnung_mehrsprachig, sort) VALUES (E'teilnehmer', E'Teilnehmende', E'{\"Teilnehmende\", \"Participant\"}', 2);
|
||||||
|
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_event_rolle to vilesci;
|
||||||
|
GRANT SELECT ON lehre.tbl_kalender_event_rolle to web;
|
||||||
|
|
||||||
|
CREATE TABLE lehre.tbl_kalender_event_teilnehmer (
|
||||||
|
kalender_event_teilnehmer_id bigserial NOT NULL,
|
||||||
|
kalender_id bigint NOT NULL,
|
||||||
|
rolle_kurzbz character varying(32),
|
||||||
|
uid character varying(32),
|
||||||
|
studiensemester_kurzbz character varying(32),
|
||||||
|
gruppe_kurzbz character varying(32),
|
||||||
|
studiengang_kz integer,
|
||||||
|
semester smallint,
|
||||||
|
verband character(1),
|
||||||
|
gruppe character(1),
|
||||||
|
studentenlehrverband_id integer,
|
||||||
|
CONSTRAINT tbl_kalender_event_teilnehmer_pk PRIMARY KEY (kalender_event_teilnehmer_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE lehre.tbl_kalender_event_teilnehmer ADD CONSTRAINT tbl_kalender_event_fk FOREIGN KEY (kalender_id)
|
||||||
|
REFERENCES lehre.tbl_kalender (kalender_id)
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE lehre.tbl_kalender_event_teilnehmer ADD CONSTRAINT tbl_kalender_event_uid_fk FOREIGN KEY (uid)
|
||||||
|
REFERENCES public.tbl_benutzer (uid)
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE lehre.tbl_kalender_event_teilnehmer ADD CONSTRAINT tbl_kalender_event_rolle_fk FOREIGN KEY (rolle_kurzbz)
|
||||||
|
REFERENCES lehre.tbl_kalender_event_rolle (rolle_kurzbz)
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE lehre.tbl_kalender_event_teilnehmer ADD CONSTRAINT tbl_gruppe_fk FOREIGN KEY (gruppe_kurzbz)
|
||||||
|
REFERENCES public.tbl_gruppe (gruppe_kurzbz)
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE lehre.tbl_kalender_event_teilnehmer ADD CONSTRAINT tbl_lehrverband_fk FOREIGN KEY (studiengang_kz, semester, verband, gruppe)
|
||||||
|
REFERENCES public.tbl_lehrverband(studiengang_kz, semester, verband, gruppe)
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_event_teilnehmer to vilesci;
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_event_teilnehmer to web;
|
||||||
|
|
||||||
|
|
||||||
|
GRANT USAGE ON lehre.tbl_kalender_event_teilnehmer_kalender_event_teilnehmer_id_seq TO vilesci;
|
||||||
|
GRANT USAGE ON lehre.tbl_kalender_event_teilnehmer_kalender_event_teilnehmer_id_seq TO web;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE sync.tbl_reservierung_kalender(
|
||||||
|
reservierung_kalender_id bigserial NOT NULL,
|
||||||
|
reservierung_id integer NOT NULL,
|
||||||
|
kalender_id bigint NOT NULL,
|
||||||
|
lastupdate timestamp,
|
||||||
|
CONSTRAINT tbl_reservierung_kalender_pk PRIMARY KEY (reservierung_kalender_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON sync.tbl_reservierung_kalender to vilesci;
|
||||||
|
COMMENT ON TABLE sync.tbl_reservierung_kalender IS 'Migration from old Reservierung to new Kalender Table';
|
||||||
|
|
||||||
|
GRANT USAGE ON sync.tbl_reservierung_kalender_reservierung_kalender_id_seq TO vilesci;
|
||||||
|
|
||||||
|
";
|
||||||
|
|
||||||
|
if(!$db->db_query($qry))
|
||||||
|
echo '<strong>lehre.tbl_kalender_event: '.$db->db_last_error().'</strong><br>';
|
||||||
|
else
|
||||||
|
echo '<br>lehre.tbl_kalender_event: neue Tabellen hinzugefuegt';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new name type in public.tbl_variablenname
|
||||||
|
if ($result = @$db->db_query("SELECT 1 FROM public.tbl_variablenname WHERE name = 'tempus_favorites';"))
|
||||||
|
{
|
||||||
|
if ($db->db_num_rows($result) == 0)
|
||||||
|
{
|
||||||
|
$qry = "INSERT INTO public.tbl_variablenname(name, defaultwert) VALUES('tempus_favorites', null);";
|
||||||
|
|
||||||
|
if (!$db->db_query($qry))
|
||||||
|
echo '<strong>public.tbl_variablenname '.$db->db_last_error().'</strong><br>';
|
||||||
|
else
|
||||||
|
echo 'public.tbl_variablenname: Added name "tempus_favorites"<br>';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
if (! defined('DB_NAME')) exit('No direct script access allowed');
|
||||||
|
|
||||||
|
if(!$result = @$db->db_query("SELECT kalender_syncstatus_id FROM lehre.tbl_kalender_syncstatus LIMIT 1"))
|
||||||
|
{
|
||||||
|
$qry = "CREATE TABLE lehre.tbl_kalender_syncstatus
|
||||||
|
(
|
||||||
|
kalender_syncstatus_id bigserial NOT NULL,
|
||||||
|
oe_kurzbz character varying(32),
|
||||||
|
studiensemester_kurzbz character varying(32),
|
||||||
|
datum_bis date,
|
||||||
|
studienplan_id integer,
|
||||||
|
ausbildungssemester smallint,
|
||||||
|
sync_status_kurzbz character varying(32),
|
||||||
|
mail boolean DEFAULT false NOT NULL,
|
||||||
|
insertamum timestamp DEFAULT now(),
|
||||||
|
insertvon character varying(32),
|
||||||
|
updateamum timestamp,
|
||||||
|
updatevon character varying(32),
|
||||||
|
CONSTRAINT tbl_kalender_syncstatus_pk PRIMARY KEY (kalender_syncstatus_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE lehre.tbl_kalender_syncstatus ADD CONSTRAINT tbl_kalender_syncstatus_oe_kurzbz_fk FOREIGN KEY (oe_kurzbz) REFERENCES public.tbl_organisationseinheit (oe_kurzbz) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
ALTER TABLE lehre.tbl_kalender_syncstatus ADD CONSTRAINT tbl_kalender_syncstatus_studiensemester_kurzbz_fk FOREIGN KEY (studiensemester_kurzbz) REFERENCES public.tbl_studiensemester(studiensemester_kurzbz) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
ALTER TABLE lehre.tbl_kalender_syncstatus ADD CONSTRAINT tbl_kalender_syncstatus_studienplan_id_fk FOREIGN KEY (studienplan_id) REFERENCES lehre.tbl_studienplan (studienplan_id) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
ALTER TABLE lehre.tbl_kalender_syncstatus ADD CONSTRAINT tbl_kalender_syncstatus_status_sync_status_kurzbz_fk FOREIGN KEY (sync_status_kurzbz) REFERENCES lehre.tbl_kalender_status (status_kurzbz) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
GRANT SELECT, UPDATE ON lehre.tbl_kalender_syncstatus_kalender_syncstatus_id_seq TO vilesci;
|
||||||
|
GRANT SELECT ON lehre.tbl_kalender_syncstatus_kalender_syncstatus_id_seq TO web;
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_syncstatus to vilesci;
|
||||||
|
GRANT SELECT ON lehre.tbl_kalender_typ to web;";
|
||||||
|
|
||||||
|
if(!$db->db_query($qry))
|
||||||
|
echo '<strong>lehre.tbl_kalender_syncstatus: '.$db->db_last_error().'</strong><br>';
|
||||||
|
else
|
||||||
|
echo ' lehre.tbl_kalender_syncstatus: Tabelle hinzugefuegt<br>';
|
||||||
|
|
||||||
|
}
|
||||||
@@ -488,6 +488,66 @@ $phrases = array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'organisierende',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Organisierende',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Organizer',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'teilnehmende',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Teilnehmende',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Participant',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'reservierungsinformationen',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => "Reservierungsinformationen",
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => "Reservation Details",
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'global',
|
'category' => 'global',
|
||||||
@@ -3303,6 +3363,126 @@ $phrases = array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'lehre',
|
||||||
|
'phrase' => 'mail_benachrichtigung',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Mail-Benachrichtigung',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Email notification',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'lehre',
|
||||||
|
'phrase' => 'bis_title',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Bis',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Until',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'lehre',
|
||||||
|
'phrase' => 'errorDeleting',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Fehler beim Löschen.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Error deleting.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'lehre',
|
||||||
|
'phrase' => 'tempus_sync_start',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Synchronisierung manuell starten',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Start synchronization manually',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'lehre',
|
||||||
|
'phrase' => 'tempus_sync_edit',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Sync-Eintrag bearbeiten',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Edit sync entry',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'lehre',
|
||||||
|
'phrase' => 'tempus_sync_new',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Neuer Sync-Eintrag',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'New sync entry',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'lehre',
|
'category' => 'lehre',
|
||||||
@@ -3703,6 +3883,66 @@ $phrases = array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'lehre',
|
||||||
|
'phrase' => 'cpempty',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Wähle einen Studiengang aus, um den LV-Teil anzuzeigen.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Select a degree program to display the teaching unit.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'lehre',
|
||||||
|
'phrase' => 'cptitleempty',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Kein LV-Teil',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'No teaching unit',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'lehre',
|
||||||
|
'phrase' => 'cpmore',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'weitere...',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'more...',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'lehre',
|
'category' => 'lehre',
|
||||||
@@ -48883,6 +49123,206 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'ma_le_kollision',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'LektorIn Kollision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Lector Collision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'reservierung_kollision',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Reservierung Kollision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Reservation Collision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'ma_zeitsperre_kollision',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Zeitsperre Kollision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Time Block Collision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'raum_kollision',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Raum Kollision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Room Collision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'verband_kollision',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Verband Kollision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Verband Collision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'student_kollision',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Studierende Kollision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Student Collision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'ignore_kollision',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Kollision ignorieren',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Ignore scheduling collision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'ignore_reservierung',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Reservierungskollisionen ignorieren',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Ignore reservation collision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'ignore_zeitsperre',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Zeitsperrenkollisionen ignorieren',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Ignore time lock collision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'kollision_student',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Studentenkollision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Student collision',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'abschlusspruefung',
|
'category' => 'abschlusspruefung',
|
||||||
@@ -48903,6 +49343,186 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'status_change_not_allowed',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Statuswechsel nicht erlaubt',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Status change not allowed',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'already_reserved',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Bereits reserviert',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Already reserved',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'entry_not_editable',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Eintrag kann nicht mehr bearbeitet werden',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Entry can no longer be edited',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'lecturer_already_here',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'LektorIn bereits hier',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Lecturer already here',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'student_already_here',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Studierende bereits hier',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Students already here',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'lecturer_building_change',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Gebäudewechsel LektorIn',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Building change lecturer',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'student_building_change',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Gebäudewechsel Studierende',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Building change students',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'lecturer_floor_change',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Stockwerkwechsel LektorIn',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Floor change lecturer',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'student_floor_change',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Stockwerkwechsel Studierende',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Floor change students',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'abschlusspruefung',
|
'category' => 'abschlusspruefung',
|
||||||
|
|||||||
Reference in New Issue
Block a user