Conflicts resolved

This commit is contained in:
Ivymaster
2026-09-02 15:01:56 +02:00
60 changed files with 3763 additions and 988 deletions
+38 -9
View File
@@ -16,25 +16,54 @@ class Tempus extends Auth_Controller
// Load Libraries
$this->load->library('VariableLib', ['uid' => getAuthUID()]);
// Load Config
$this->load->config('calendar');
}
/**
* @return void
*/
public function _remap()
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->OrganisationseinheitModel->addJoin('tbl_studiengang', 'oe_kurzbz', 'LEFT');
$this->OrganisationseinheitModel->addSelect('public.tbl_organisationseinheit.organisationseinheittyp_kurzbz,
public.tbl_organisationseinheit.bezeichnung,
public.tbl_studiengang.studiengang_kz,
public.tbl_organisationseinheit.oe_kurzbz,
tbl_studiengang.typ,
COALESCE(public.tbl_studiengang.bezeichnung, \'\') as stgbezeichnung');
$this->OrganisationseinheitModel->addOrder('tbl_organisationseinheit.aktiv', 'DESC');
$this->OrganisationseinheitModel->addOrder('tbl_organisationseinheit.organisationseinheittyp_kurzbz');
$organisationen = getData($this->OrganisationseinheitModel->loadWhere(array("tbl_organisationseinheit.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')
'admin' => $this->permissionlib->isBerechtigt('admin'),
'stundenraster' => $this->permissionlib->isBerechtigt('basis/tempus_stundenraster'),
],
'variables' => [
'variables' => array_merge([
'semester_aktuell' => $this->variablelib->getVar('semester_aktuell'),
'timezone' => $this->config->item('timezone')
]
], $extraVariables)
]);
}
}
}
@@ -11,10 +11,10 @@ class Config extends FHCAPI_Controller
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'],
'get' => ['admin:r', 'assistenz:rw', 'lehre/lvplan:rw'],
'getHeader' => ['admin:r', 'assistenz:rw', 'lehre/lvplan:rw'],
'set' => ['admin:r', 'assistenz:rw', 'lehre/lvplan:rw'],
'updateCollision' => ['admin:r', 'assistenz:rw', 'lehre/lvplan:rw'],
]);
// Load Phrases
@@ -32,7 +32,17 @@ class Config extends FHCAPI_Controller
{
$config = [];
$result = $this->_ci->VariableModel->getVariables(getAuthUID(), ['ignore_kollision', 'kollision_student', 'ignore_reservierung', 'ignore_zeitsperre', 'ignore_resources_collisions']);
$var_array = array(
'ignore_kollision',
'kollision_student',
'ignore_reservierung',
'ignore_zeitsperre',
'ignore_resources_collisions',
'roomless_planning',
'priority_room_planning',
'dialog_room_planning'
);
$result = $this->_ci->VariableModel->getVariables(getAuthUID(), $var_array);
$data = $this->getDataOrTerminateWithError($result);
$config['ignore_kollision'] = [
@@ -67,6 +77,25 @@ class Config extends FHCAPI_Controller
"value" => ($data['ignore_resources_collisions'] ?? 'false') === 'true'
];
$config['roomless_planning'] = [
"type" => "checkbox",
"label" => $this->p->t('ui', 'roomless_planning'),
"value" => ($data['roomless_planning'] ?? 'false') === 'true'
];
$config['priority_room_planning'] = [
"type" => "checkbox",
"label" => $this->p->t('ui', 'priority_room_planning'),
"value" => ($data['priority_room_planning'] ?? 'false') === 'true'
];
$config['dialog_room_planning'] = [
"type" => "checkbox",
"label" => $this->p->t('ui', 'dialog_room_planning'),
"value" => ($data['dialog_room_planning'] ?? 'false') === 'true'
];
$this->terminateWithSuccess($config);
}
public function getHeader()
@@ -92,31 +121,26 @@ class Config extends FHCAPI_Controller
public function set()
{
$this->_ci->VariableModel->setVariable(
getAuthUID(),
$configs = array(
'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->_ci->VariableModel->setVariable(
getAuthUID(),
'ignore_resources_collisions',
$this->input->post('ignore_resources_collisions') === true ? 'true' : 'false'
'roomless_planning',
'priority_room_planning',
'dialog_room_planning'
);
foreach ($configs as $config)
{
$this->_ci->VariableModel->setVariable(
getAuthUID(),
$config,
$this->input->post($config) === true ? 'true' : 'false'
);
}
$this->terminateWithSuccess();
}
@@ -5,11 +5,13 @@ if (! defined('BASEPATH')) exit('No direct script access allowed');
class Coursepicker extends FHCAPI_Controller
{
private $_ci;
const ALLOWED_COURSE_FILTER = ['uid', 'stg'];
public function __construct()
{
parent::__construct([
'search' => self::PERM_LOGGED,
'getByStg' => self::PERM_LOGGED
'getCourses' => 'lehre/lvplan:rw',
]);
$this->_ci = &get_instance();
@@ -22,88 +24,40 @@ class Coursepicker extends FHCAPI_Controller
$this->loadPhrases(['ui']);
}
public function search()
{
$query = $this->input->get('query');
if (is_null($query))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$query_words = explode(' ', $query);
//TODO Where weiter anpassen z.B. Fachbereich
$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,
tbl_mitarbeiter.kurzbz AS lektor,
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 lehrfach', 'tbl_lehreinheit.lehrfach_id = lehrfach.lehrveranstaltung_id');
$this->_ci->LehreinheitModel->addJoin('public.tbl_mitarbeiter', 'lema.mitarbeiter_uid = tbl_mitarbeiter.mitarbeiter_uid');
$this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrform', 'tbl_lehrform.lehrform_kurzbz = tbl_lehreinheit.lehrform_kurzbz');
$this->_ci->MitarbeiterModel->db->group_start();
foreach ($query_words as $word)
{
$this->_ci->LehreinheitModel->db->group_start();
$this->_ci->LehreinheitModel->db->where('lema.mitarbeiter_uid ILIKE', "%" . $word . "%");
$this->_ci->LehreinheitModel->db->or_where('lvb.gruppe_kurzbz ILIKE', "%" . $word . "%");
$this->_ci->LehreinheitModel->db->or_where('tbl_studiengang.kurzbzlang ILIKE', "%" . $word . "%");
$this->_ci->LehreinheitModel->db->or_where('lvb.verband ILIKE', "%" . $word . "%");
$this->_ci->LehreinheitModel->db->or_where('lvb.gruppe ILIKE', "%" . $word . "%");
$this->_ci->LehreinheitModel->db->or_where('lehrfach.bezeichnung ILIKE', "%" . $word . "%");
if (is_numeric($word))
{
$this->_ci->LehreinheitModel->db->or_where('tbl_studiengang.studiengang_kz', $word);
$this->_ci->LehreinheitModel->db->or_where('lvb.semester', $word);
}
$this->_ci->LehreinheitModel->db->group_end();
}
$this->_ci->LehreinheitModel->db->group_end();
$this->_ci->LehreinheitModel->db->where('tbl_lehreinheit.studiensemester_kurzbz = \'SS2025\'');
$this->_ci->LehreinheitModel->db->where(array('tbl_lehrform.verplanen' => true));
$result = $this->_ci->LehreinheitModel->load();
$this->terminateWithSuccess(hasData($result) ? getData($result) : array());
}
public function getByStg()
public function getCourses()
{
$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');
$studiensemester_kurzbz = $this->_ci->input->post('studiensemester_kurzbz', TRUE);
$filter = $this->_checkFilter(self::ALLOWED_COURSE_FILTER);
$this->_getBasePlan($studiensemester_kurzbz);
$this->_ci->LehreinheitModel->db->group_start();
if (isset($filter->stg))
$this->_addStgWhere($filter->stg);
if (isset($filter->uid))
{
$uid_array = (array) $filter->uid;
$this->_ci->LehreinheitModel->db->or_where_in('tbl_benutzer.uid', $uid_array);
}
$this->_ci->LehreinheitModel->db->group_end();
$this->_ci->LehreinheitModel->addOrder('lehreinheit_id');
$result = $this->_ci->LehreinheitModel->load();
$result = hasData($result) ? getData($result) : array();
$this->terminateWithSuccess($this->_mapEvents($result));
}
private function _getBasePlan($studiensemester_kurzbz)
{
$this->_ci->LehreinheitModel->addSelect('
tbl_lehreinheit.lehreinheit_id,
tbl_lehreinheit.lvnr,
@@ -142,6 +96,15 @@ class Coursepicker extends FHCAPI_Controller
$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();
$this->_ci->LehreinheitModel->db->where('tbl_lehrform.verplanen', true);
$this->_ci->LehreinheitModel->db->where('tbl_lehreinheit.studiensemester_kurzbz', $studiensemester_kurzbz);
$this->_ci->LehreinheitModel->db->group_end();
}
private function _addStgWhere($studiengaenge)
{
$this->_ci->LehreinheitModel->db->group_start();
$first = true;
foreach ($studiengaenge as $studiengang)
@@ -165,14 +128,10 @@ class Coursepicker extends FHCAPI_Controller
$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();
private function _mapEvents($result)
{
$lehreinheit_ids = array_values(array_unique(array_column($result, 'lehreinheit_id')));
$offen_map = array();
@@ -281,10 +240,8 @@ class Coursepicker extends FHCAPI_Controller
$group->verplant = $this->_formatArr($group->verplant);
$group->offenestunden = $this->_formatArr($group->offenestunden);
}
$this->terminateWithSuccess(array_values($grouped));
return array_values($grouped);
}
private function _formatArr($arr)
{
$values = array_values(array_unique($arr));
@@ -296,5 +253,24 @@ class Coursepicker extends FHCAPI_Controller
return $formatted;
}
private function _checkFilter($filters)
{
$filter_valid = false;
$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;
}
}
@@ -7,8 +7,8 @@ class Favorites extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'index' => self::PERM_LOGGED,
'set' => self::PERM_LOGGED
'index' => 'lehre/lvplan:rw',
'set' => 'lehre/lvplan:rw'
]);
// Load models
@@ -12,7 +12,7 @@ class Info extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getStudiengaenge' => self::PERM_LOGGED,
'getStudiengaenge' => 'lehre/lvplan:rw',
]);
$this->_ci =& get_instance();
@@ -8,18 +8,19 @@ class Kalender extends FHCAPI_Controller
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'];
const ALLOWED_TO_UPDATE = ['start_time', 'end_time', 'orte'];
/**
* 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,
'getStunden' => 'lehre/lvplan:rw',
'getCalendarHours' => 'lehre/lvplan:rw',
'getPlan' => 'lehre/lvplan:rw',
'getPlanByOrt' => 'lehre/lvplan:rw',
'getRaumvorschlag' => 'lehre/lvplan:rw',
'getRaumvorschlagSlots' => 'lehre/lvplan:rw',
'getLehreinheiten' => 'lehre/lvplan:rw',
'getHistory' => 'lehre/lvplan:rw',
'deleteEntry' => 'lehre/lvplan:rw',
@@ -27,8 +28,8 @@ class Kalender extends FHCAPI_Controller
'syncToStudent' => 'lehre/lvplan:rw',
'getPlanLecturer' =>'lehre/lvplan:rw',
'getPlanStudent' => 'lehre/lvplan:rw',
'getZeitwuensche' => self::PERM_LOGGED,
'getZeitsperren' => self::PERM_LOGGED,
'getZeitwuensche' => 'lehre/lvplan:rw',
'getZeitsperren' => 'lehre/lvplan:rw',
'updateKalenderEvent' => 'lehre/lvplan:rw',
'deleteOrtEntry' => 'lehre/lvplan:rw',
'deleteFromKalenderEvent' => 'lehre/lvplan:rw',
@@ -44,6 +45,7 @@ class Kalender extends FHCAPI_Controller
$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'
@@ -222,7 +224,7 @@ class Kalender extends FHCAPI_Controller
$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);
$result = $this->_ci->kalenderlib->updateKalenderEvent($kalender_id, $updateFields->orte ?? null, $updateFields->start_time ?? null, $updateFields->end_time ?? null, $updateFields->location ?? null);
if (isError($result))
$this->terminateWithError(getError($result), $result->code);
@@ -255,6 +257,22 @@ class Kalender extends FHCAPI_Controller
$this->terminateWithSuccess($this->_ci->raumvorschlaglib->getVorschlaege($filter->kalender_id));
}
public function getRaumvorschlagSlots()
{
$this->_ci->form_validation->set_data($_GET);
$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->get('lehreinheit_id', TRUE);
$start_date = $this->_ci->input->get('start_date', TRUE);
$end_date = $this->_ci->input->get('end_date', TRUE);
$this->terminateWithSuccess(getData($this->_ci->raumvorschlaglib->getVorschlaegeSlots($lehreinheit_id, $start_date, $end_date)));
}
public function getLehreinheiten()
{
$this->_ci->form_validation->set_data($_GET);
@@ -306,7 +324,7 @@ class Kalender extends FHCAPI_Controller
public function sync()
{
$result = $this->_ci->kalenderlib->sync();
$result = $this->_ci->kalendersynclib->sync();
$this->terminateWithSuccess(getData($result));
}
public function syncToLecturer()
@@ -0,0 +1,304 @@
<?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, 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');
$this->_ci->KalenderStatusModel->addJoin('public.tbl_studiensemester', 'tbl_kalender_syncstatus.studiensemester_kurzbz = tbl_studiensemester.studiensemester_kurzbz');
$this->_ci->KalenderStatusModel->addOrder('public.tbl_studiensemester.start', 'DESC');
if ($studiensemester_kurzbz === 'all')
$data = $this->_ci->KalendersyncstatusModel->load();
else
$data = $this->_ci->KalendersyncstatusModel->loadWhere(array('tbl_kalender_syncstatus.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();
}
}
@@ -9,9 +9,9 @@ class OperationalResourceToCalenderAPI extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getAssignedResourcesByCalender' => ['admin:r', 'assistenz:r'],
'storeResourcesToCalendarRelationship' => ['admin:w', 'assistenz:w'],
'getSchedulableResourcesByCalendar' => ['admin:r', 'assistenz:r'],
'getAssignedResourcesByCalender' => ['admin:r', 'assistenz:r', 'lehre/lvplan:rw'],
'storeResourcesToCalendarRelationship' => ['admin:w', 'assistenz:w', 'lehre/lvplan:rw'],
'getSchedulableResourcesByCalendar' => ['admin:r', 'assistenz:r', 'lehre/lvplan:rw'],
]);
$this->load->model('ressource/BetriebsmittelKalender_model', 'BetriebsmittelKalenderModel');
@@ -162,9 +162,9 @@ class Reservierung extends FHCAPI_Controller
if ($this->_ci->form_validation->run() === FALSE)
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
$ort_kurzbz = $this->_ci->input->post('ort_kurzbz', TRUE);
$orte = $this->_ci->input->post('orte', TRUE);
if (empty($ort_kurzbz) || !is_array($ort_kurzbz))
if (empty($orte) || (empty($orte['ort_kurzbz']) && empty($orte['location'])))
{
$this->terminateWithValidationErrors(['ort_kurzbz' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Raum'])]);
}
@@ -189,7 +189,7 @@ class Reservierung extends FHCAPI_Controller
$teilnehmer[] = array('uid' => getAuthUID(), 'rolle' => 'organisator');
$result = $this->_ci->kalenderlib->addReservierung($titel, $beschreibung, $ort_kurzbz, $start_date, $end_date, $teilnehmer, $specialFinalGroups, $specialGroups, $groups);
$result = $this->_ci->kalenderlib->addReservierung($titel, $beschreibung, $orte, $start_date, $end_date, $teilnehmer, $specialFinalGroups, $specialGroups, $groups);
if (isError($result))
@@ -121,7 +121,14 @@ class MigrateKalender extends CLI_Controller
$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);
$this->SyncModel->db->group_start();
$where_ids_chunks = array_chunk($ids,25);
foreach($where_ids_chunks as $where_ids)
{
$this->SyncModel->db->or_where_in('stundenplandev_id', $where_ids);
}
$this->SyncModel->db->group_end();
$sync_result = $this->SyncModel->load();
if (!hasData($sync_result))
@@ -230,7 +237,14 @@ class MigrateKalender extends CLI_Controller
$ids = is_array($block->reservierung_ids) ? $block->reservierung_ids : explode(',', $block->reservierung_ids);
$this->SyncReservierungModel->db->where_in('reservierung_id', $ids);
$this->SyncReservierungModel->db->group_start();
$where_ids_chunks = array_chunk($ids,25);
foreach($where_ids_chunks as $where_ids)
{
$this->SyncReservierungModel->db->or_where_in('reservierung_id', $where_ids);
}
$this->SyncReservierungModel->db->group_end();
$sync_result = $this->SyncReservierungModel->load();
if (!hasData($sync_result))
+317 -170
View File
@@ -17,7 +17,6 @@ class KalenderLib
$this->_ci->load->model('ressource/BetriebsmittelKalender_model', 'BetriebsmittelKalenderModel');
$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');
@@ -48,6 +47,7 @@ class KalenderLib
tbl_kalender.typ,
tbl_kalender.von,
tbl_kalender.bis,
tbl_kalender_ort.location,
tbl_kalender_ort.ort_kurzbz,
tbl_lehreinheit.lehreinheit_id,
tbl_lehreinheit.lehrveranstaltung_id,
@@ -91,7 +91,19 @@ class KalenderLib
tbl_lehreinheitgruppe.gruppe as le_gruppe,
le_gruppe.direktinskription as le_direktinskription,
tag_data_agg.tags as tags,
resource_data_agg.resources as resources
resource_data_agg.resources as resources,
CASE
WHEN vonstunde.stunde IS NOT NULL AND bisstunde.stunde IS NOT NULL
THEN bisstunde.stunde - vonstunde.stunde + 1
ELSE ROUND(
COALESCE(
(
SELECT SUM(EXTRACT(EPOCH FROM (LEAST(stunde.ende, tbl_kalender.bis::time) - GREATEST(stunde.beginn, tbl_kalender.von::time))) / 60) / 45
FROM lehre.tbl_stunde stunde
WHERE stunde.beginn < tbl_kalender.bis::time AND stunde.ende > tbl_kalender.von::time)
,0)
,2
) END AS verplante_stunden
');
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit', 'tbl_kalender.kalender_id = tbl_kalender_lehreinheit.kalender_id', 'LEFT');
@@ -201,6 +213,9 @@ class KalenderLib
// End of operational resources inclusion
$this->_ci->KalenderModel->db->join('lehre.tbl_stunde vonstunde', 'tbl_kalender.von::time = vonstunde.beginn', 'LEFT', FALSE);
$this->_ci->KalenderModel->db->join('lehre.tbl_stunde bisstunde', 'tbl_kalender.bis::time = bisstunde.ende', 'LEFT', FALSE);
$this->_ci->KalenderModel->db->where('tbl_kalender.von >=', $start_date);
$this->_ci->KalenderModel->db->where('tbl_kalender.bis <', $end_date);
@@ -238,11 +253,13 @@ class KalenderLib
'tooltip' => 'tip',
'status_kurzbz' => $row->status_kurzbz,
'ort_kurzbz' => [],
'location' => null,
'lehrform' => isset($row->lehrform_kurzbz) ? $row->lehrform_kurzbz : '',
'lehrfach' => isset($row->lehrfach_kurzbz) ? $row->lehrfach_kurzbz : '',
'lehrfach_bez' => isset($row->lehrfach_bezeichnung) ? $row->lehrfach_bezeichnung : '',
'farbe' => isset($row->farbe) ? $row->farbe : '',
'lehrveranstaltung_id' => $row->lehrveranstaltung_id,
'verplante_stunden' => $row->verplante_stunden,
'organisationseinheit' => isset($row->oe_kurzbz) ? $row->oe_kurzbz : '',
'kalender_id' => $id,
'lehreinheit_id' => [],
@@ -266,6 +283,9 @@ class KalenderLib
if (isset($row->ort_kurzbz) && $row->ort_kurzbz !== '' && !in_array($row->ort_kurzbz, $events[$id]->ort_kurzbz))
$events[$id]->ort_kurzbz[] = $row->ort_kurzbz;
if (isset($row->location) && $row->location !== '' && is_null($events[$id]->location))
$events[$id]->location = $row->location;
$topic = trim((isset($row->lehrfach_kurzbz) ? $row->lehrfach_kurzbz : '').' '.(isset($row->lehrform_kurzbz) ? $row->lehrform_kurzbz : ''));
if ($topic !== '' && !in_array($topic, $events[$id]->topic))
$events[$id]->topic[] = $topic;
@@ -357,8 +377,6 @@ class KalenderLib
return $this->_mapEvents($data);
}
public function getForRaumvorschlag($start_date, $end_date, $lektor_uids = [], $gruppen_kurzbz = [], $lehrverband_gruppen = [])
{
$start_date = date('Y-m-d', strtotime($start_date));
@@ -384,6 +402,7 @@ class KalenderLib
foreach ($lehrverband_gruppen as $lvg)
{
$lvg = (array) $lvg;
$this->_ci->KalenderModel->db->or_group_start();
$this->_ci->KalenderModel->db->where('tbl_lehreinheitgruppe.studiengang_kz', $lvg['studiengang_kz']);
$this->_ci->KalenderModel->db->where('tbl_lehreinheitgruppe.semester', $lvg['semester']);
@@ -530,7 +549,7 @@ class KalenderLib
{
$this->_getBasePlan($start_date, $end_date);
$this->_ci->KalenderModel->db->where('status_kurzbz', 'live');
$this->_ci->KalenderModel->db->where_in('status_kurzbz', array('live', 'to_delete_live'));
$data = $this->_ci->KalenderModel->load();
@@ -662,6 +681,17 @@ class KalenderLib
}
private function _checkCollisionForLehreinheit($start_date, $end_date, $lehreinheit_id, $ort_kurzbz)
{
$checkerData = new stdClass();
$checkerData->von = $start_date;
$checkerData->bis = $end_date;
$checkerData->lehreinheit_id = $lehreinheit_id;
$checkerData->ort_kurzbz = $ort_kurzbz;
return $this->_ci->collisionchecker->run($checkerData);
}
public function addToKalenderEvent($target_kalender_id, $lehreinheit_id)
{
$entryResult = $this->_loadKalenderEntry($target_kalender_id);
@@ -669,7 +699,15 @@ class KalenderLib
$kalender_entry = getData($entryResult);
if (in_array($kalender_entry->status_kurzbz, array('todelete', 'deleted')))
if (!$this->_checkPermission($kalender_entry->lehreinheit_id))
{
return error([
'message' => $this->_ci->phraseslib->t('ui', 'keineBerechtigung'),
'errorCode' => 'not_allowed'
]);
}
if (in_array($kalender_entry->status_kurzbz, array('to_delete', 'to_delete_live', 'to_delete_preview', 'deleted')))
{
return error([
'message' => $this->_ci->phraseslib->t('ui', 'entry_not_editable'),
@@ -712,7 +750,15 @@ class KalenderLib
$kalender_entry = getData($entryResult);
if (in_array($kalender_entry->status_kurzbz, array('todelete', 'deleted')))
if (!$this->_checkPermission($kalender_entry->lehreinheit_id))
{
return error([
'message' => $this->_ci->phraseslib->t('ui', 'keineBerechtigung'),
'errorCode' => 'not_allowed'
]);
}
if (in_array($kalender_entry->status_kurzbz, array('to_delete', 'to_delete_live', 'to_delete_preview', 'deleted')))
{
return error([
'message' => $this->_ci->phraseslib->t('ui', 'entry_not_editable'),
@@ -804,16 +850,6 @@ class KalenderLib
return ['kalender_id' => $kalender_id, 'result' => $ortresult];
}
$entryResult = $this->_loadKalenderEntry($kalender_id);
if (isError($entryResult))
return ['kalender_id' => $kalender_id, 'result' => $entryResult];
$kalender_entry = getData($entryResult);
$errors = $this->_ci->collisionchecker->run($kalender_entry);
if (!empty($errors))
return ['kalender_id' => $kalender_id, 'result' => error($errors)];
return ['kalender_id' => $kalender_id, 'result' => $kalenderlehreinheitresult];
}
@@ -878,8 +914,6 @@ class KalenderLib
'false'
);
$this->_ci->KalenderModel->db->trans_start();
while ($rest > 0 && new DateTime($current_date) < $ende)
{
$max_skip = 52;
@@ -920,18 +954,9 @@ class KalenderLib
$start_str = $current_date . ' ' . $start_time;
$end_str = $current_date . ' ' . $end_time;
$insert = $this->_insertKalenderEventRaw($start_str, $end_str, $lehreinheit_id, $ort_kurzbz);
$kalender_id = $insert['kalender_id'];
$insert_result = $insert['result'];
$collisions = $this->_checkCollisionForLehreinheit($start_str, $end_str, $lehreinheit_id, $ort_kurzbz);
$collisions = isError($insert_result) ? getError($insert_result) : [];
$raum_vorschlaege = [];
if ($kalender_id)
{
$vorschlaege = $this->_ci->raumvorschlaglib->getVorschlaege($kalender_id);
$raum_vorschlaege = is_array($vorschlaege) ? $vorschlaege : [];
}
$raum_vorschlaege = $this->_ci->raumvorschlaglib->getVorschlaegeByLehreinheit($lehreinheit_id, $start_str, $end_str);
$plan[] = [
'datum' => $current_date,
@@ -940,14 +965,13 @@ class KalenderLib
'block' => $current_block,
'ort_kurzbz' => $ort_kurzbz,
'collisions' => $collisions,
'raum_vorschlaege' => $raum_vorschlaege,
'raum_vorschlaege' => is_array($raum_vorschlaege) ? $raum_vorschlaege : [],
];
$rest -= $current_block;
$current_date = $this->_addWeeks($current_date, $wochenrythmus);
}
$this->_ci->KalenderModel->db->trans_rollback();
$this->_ci->VariableModel->setVariable(
getAuthUID(),
@@ -1010,6 +1034,49 @@ class KalenderLib
public function addKalenderEvent($start_date, $end_date, $lehreinheit_id, $ort_kurzbz)
{
if (!$this->_checkPermission((array)$lehreinheit_id))
{
return error([
'message' => $this->_ci->phraseslib->t('ui', 'keineBerechtigung'),
'errorCode' => 'not_allowed'
]);
}
if (isEmptyString($ort_kurzbz))
{
$errors = $this->_checkCollisionForLehreinheit($start_date, $end_date, $lehreinheit_id, $ort_kurzbz);
if (!empty($errors))
return error($errors);
$raum_vorschlaege = $this->_ci->raumvorschlaglib->getVorschlaegeByLehreinheit($lehreinheit_id, $start_date, $end_date);
if (($this->_ci->variablelib->getVar('dialog_room_planning') ?? 'false') === 'true')
{
return success([
'needs_room_selection' => true,
'raum_vorschlaege' => is_array($raum_vorschlaege) ? $raum_vorschlaege : [],
]);
}
if (($this->_ci->variablelib->getVar('priority_room_planning') ?? 'false') === 'true')
{
$ort_kurzbz = (is_array($raum_vorschlaege)) ? $raum_vorschlaege[0]['ort_kurzbz'] : null;
}
else if (!(($this->_ci->variablelib->getVar('roomless_planning') ?? 'false') === 'true'))
{
return error([
'message' => $this->_ci->phraseslib->t('ui', 'roomless_planning_error'),
'errorCode' => 'roomless_planning_error'
]);
}
}
$errors = $this->_checkCollisionForLehreinheit($start_date, $end_date, $lehreinheit_id, $ort_kurzbz);
if (!empty($errors))
return error($errors);
$this->_ci->KalenderModel->db->trans_start();
$insert = $this->_insertKalenderEventRaw($start_date, $end_date, $lehreinheit_id, $ort_kurzbz);
@@ -1025,14 +1092,14 @@ class KalenderLib
return $result;
}
public function addReservierung($titel, $beschreibung, $ort_kurzbz, $start_date, $end_date, $teilnehmer, $specialFinalGroups, $specialGroups, $groups)
public function addReservierung($titel, $beschreibung, $orte, $start_date, $end_date, $teilnehmer, $specialFinalGroups, $specialGroups, $groups)
{
if (!is_null($ort_kurzbz))
if (!is_null($orte) && !isEmptyArray($orte['ort_kurzbz']))
{
$this->_ci->KalenderModel->addSelect('1');
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'kalender_id');
$this->_ci->KalenderModel->db->where_in('ort_kurzbz', $ort_kurzbz);
$this->_ci->KalenderModel->db->where_in('ort_kurzbz', $orte['ort_kurzbz']);
$reservierung_vorhanden = $this->_ci->KalenderModel->load(array (
'von <=' => $end_date,
'bis >=' => $start_date,
@@ -1093,9 +1160,25 @@ class KalenderLib
}
}
if (isSuccess($kalendereventresult) && !is_null($ort_kurzbz))
if (isSuccess($kalendereventresult) && !isEmptyArray($orte['ort_kurzbz']))
{
$ortresult = $this->_addKalenderOrt($kalender_id, $ort_kurzbz);
$ortresult = $this->_addKalenderOrt($kalender_id, $orte['ort_kurzbz']);
if (isError($ortresult))
{
$this->_ci->KalenderModel->db->trans_rollback();
return $ortresult;
}
}
if (isSuccess($kalendereventresult) && !is_null($orte['location']))
{
$ortresult = $this->_ci->KalenderOrtModel->insert(
array (
'kalender_id' => $kalender_id,
'location' => $orte['location']
)
);
if (isError($ortresult))
{
$this->_ci->KalenderModel->db->trans_rollback();
@@ -1295,26 +1378,39 @@ class KalenderLib
return success();
}
public function updateKalenderEvent($kalender_id, $ort_kurzbz = null, $start_time = null, $end_time = null)
public function updateKalenderEvent($kalender_id, $orte = null, $start_time = null, $end_time = null)
{
$entryResult = $this->_loadKalenderEntry($kalender_id);
if (isError($entryResult)) return $entryResult;
$kalender_entry = getData($entryResult);
if (!$this->_checkPermission($kalender_entry->lehreinheit_id))
{
return error([
'message' => $this->_ci->phraseslib->t('ui', 'keineBerechtigung'),
'errorCode' => 'not_allowed'
]);
}
$calendarResources = $this->_ci->BetriebsmittelKalenderModel->loadWhere(['eindeutige_kalender_gruppen_id' => $kalender_entry->eindeutige_kalender_gruppen_id]);
if (isError($calendarResources)) return $calendarResources;
$calendarResourcesItems = getData($calendarResources);
$calendarResourcesIDs = [];
if (is_array($calendarResourcesItems)) {
if (is_array($calendarResourcesItems)) {
$calendarResourcesIDs = array_map(function($resource) {
return $resource->betriebsmittel_id;
}, getData($calendarResources));
}
}
$kalender_entry->betriebsmittel_ids = $calendarResourcesIDs;
$kalender_entry->ort_kurzbz = !is_null($ort_kurzbz) ? (array) $ort_kurzbz : $kalender_entry->ort_kurzbz;
if (!is_null($orte))
{
$kalender_entry->ort_kurbz = !isEmptyArray($orte['ort_kurzbz'] ?? null) ? (array) $orte['ort_kurzbz'] : $kalender_entry->ort_kurzbz;
$kalender_entry->location = array_key_exists('location', $orte) && !is_null($orte['location']) ? $orte['location'] : $kalender_entry->location;
}
$kalender_entry->von = $start_time ?? $kalender_entry->von;
$kalender_entry->bis = $end_time ?? $kalender_entry->bis;
@@ -1324,9 +1420,9 @@ class KalenderLib
$result = $kalender_entry;
if (!is_null($ort_kurzbz))
if (!is_null($orte))
{
$result = $this->updateOrt($kalender_id, $ort_kurzbz);
$result = $this->updateOrt($kalender_id, $orte);
if (isError($result)) return $result;
if (hasData($result))
@@ -1353,7 +1449,7 @@ class KalenderLib
if ($kalender_entry->typ === 'lehreinheit')
{
if (in_array($kalender_entry->status_kurzbz, array('todelete', 'deleted')))
if (in_array($kalender_entry->status_kurzbz, array('to_delete', 'to_delete_live', 'to_delete_preview', 'deleted')))
{
return error([
'message' => $this->_ci->phraseslib->t('ui', 'entry_not_editable'),
@@ -1409,9 +1505,11 @@ class KalenderLib
return success($result);
}
public function updateOrt($kalender_id, $ort_kurzbz_array)
public function updateOrt($kalender_id, $orte_array)
{
$ort_kurzbz_array = (array)$ort_kurzbz_array;
$ort_kurzbz_neu = (array)($orte_array['ort_kurzbz'] ?? []);
$location_neu = $orte_array['location'] ?? null;
$entryResult = $this->_loadKalenderEntry($kalender_id);
if (isError($entryResult)) return $entryResult;
@@ -1419,7 +1517,15 @@ class KalenderLib
if ($kalender_entry->typ === 'lehreinheit')
{
if (in_array($kalender_entry->status_kurzbz, array('todelete', 'deleted')))
if (!$this->_checkPermission($kalender_entry->lehreinheit_id))
{
return error([
'message' => $this->_ci->phraseslib->t('ui', 'keineBerechtigung'),
'errorCode' => 'not_allowed'
]);
}
if (in_array($kalender_entry->status_kurzbz, array('to_delete', 'to_delete_live', 'to_delete_preview', 'deleted')))
{
return error([
'message' => $this->_ci->phraseslib->t('ui', 'entry_not_editable'),
@@ -1428,14 +1534,30 @@ class KalenderLib
}
if (in_array($kalender_entry->status_kurzbz, array('live', 'preview')))
return $this->_createHistoryEntry($kalender_entry, null, null, $ort_kurzbz_array);
return $this->_createHistoryEntry($kalender_entry, null, null, $orte_array);
}
$bestehende = $this->_ci->KalenderOrtModel->load(array('kalender_id' => $kalender_id));
$alte_orte = hasData($bestehende) ? array_column(getData($bestehende), 'ort_kurzbz') : [];
$alte_orte = hasData($bestehende) ? getData($bestehende) : [];
$zu_loeschen = array_diff($alte_orte, $ort_kurzbz_array);
$neu_anzulegen = array_diff($ort_kurzbz_array, $alte_orte);
$alte_orte_kurzbz = array_values(array_filter(array_map(function ($ort) {
return $ort->ort_kurzbz;
}, $alte_orte)));
$alte_location = null;
foreach ($alte_orte as $ort)
{
if (!is_null($ort->location))
{
$alte_location = $ort->location;
break;
}
}
$zu_loeschen = array_diff($alte_orte_kurzbz, $ort_kurzbz_neu);
$neu_anzulegen = array_diff($ort_kurzbz_neu, $alte_orte_kurzbz);
if (!empty($zu_loeschen))
{
@@ -1444,12 +1566,34 @@ class KalenderLib
$this->_ci->KalenderOrtModel->db->delete('lehre.tbl_kalender_ort');
}
foreach ($neu_anzulegen as $ort_kurzbz)
foreach ($neu_anzulegen as $ort)
{
$result = $this->_addKalenderOrt($kalender_id, $ort_kurzbz);
$result = $this->_addKalenderOrt($kalender_id, $ort);
if (isError($result)) return $result;
}
if ($alte_location !== $location_neu)
{
if (!is_null($alte_location))
{
$this->_ci->KalenderOrtModel->db->where('kalender_id', $kalender_id);
$this->_ci->KalenderOrtModel->db->where('ort_kurzbz IS NULL', null, false);
$this->_ci->KalenderOrtModel->db->delete('lehre.tbl_kalender_ort');
}
if (!isEmptyString($location_neu))
{
$ortresult = $this->_ci->KalenderOrtModel->insert(
array (
'kalender_id' => $kalender_id,
'location' => $location_neu
)
);
if (isError($ortresult)) return $ortresult;
}
}
return success();
}
@@ -1460,6 +1604,14 @@ class KalenderLib
$kalender_entry = getData($entryResult);
if (!$this->_checkPermission($kalender_entry->lehreinheit_id))
{
return error([
'message' => $this->_ci->phraseslib->t('ui', 'keineBerechtigung'),
'errorCode' => 'not_allowed'
]);
}
$allowed = array(
'planning' => array('sync_preview', 'sync_live'),
'sync_preview' => array('sync_live'),
@@ -1524,22 +1676,21 @@ class KalenderLib
if (!is_null($ort_kurzbz_array))
{
$new_orte = (array) $ort_kurzbz_array;
$new_locations = array_fill(0, count($new_orte), null);
$new_orte = (array) ($ort_kurzbz_array['ort_kurzbz'] ?? []);
$new_locations = $ort_kurzbz_array['location'] ?? null;
}
else
{
$new_orte = (array) $kalender_entry->ort_kurzbz;
$new_locations = (array) $kalender_entry->location;
$new_locations = $kalender_entry->location;
}
foreach ($new_orte as $index => $ort)
foreach ($new_orte as $ort)
{
$ortresult = $this->_ci->KalenderOrtModel->insert(
array(
'kalender_id' => $new_kalender_id,
'ort_kurzbz' => $ort,
'location' => $new_locations[$index] ?? null
'ort_kurzbz' => $ort
)
);
@@ -1547,6 +1698,19 @@ class KalenderLib
return $ortresult;
}
if (!is_null($new_locations))
{
$locresult = $this->_ci->KalenderOrtModel->insert(
array(
'kalender_id' => $new_kalender_id,
'location' => $new_locations
)
);
if (!isSuccess($locresult))
return $locresult;
}
return success($new_kalender_id);
}
private function _loadKalenderEntry($kalender_id)
@@ -1569,8 +1733,8 @@ class KalenderLib
$this->_ci->KalenderModel->addJoin(
'(SELECT kalender_id,
array_agg(ort_kurzbz ORDER BY ort_kurzbz) AS ort_kurzbz,
array_agg(location ORDER BY ort_kurzbz) AS location
array_agg(ort_kurzbz ORDER BY ort_kurzbz) FILTER(WHERE ort_kurzbz is NOT NULL) AS ort_kurzbz,
MAX(location) AS location
FROM lehre.tbl_kalender_ort
GROUP BY kalender_id) orte',
'tbl_kalender.kalender_id = orte.kalender_id',
@@ -1586,7 +1750,7 @@ class KalenderLib
$entry = getData($result)[0];
$entry->lehreinheit_id = $entry->lehreinheit_id ?? [];
$entry->ort_kurzbz = $entry->ort_kurzbz ?? [];
$entry->location = $entry->location ?? [];
$entry->location = $entry->location ?? null;
return success($entry);
}
@@ -1641,44 +1805,76 @@ class KalenderLib
private function _deleteTypLehreinheit($entry)
{
$result_lehreinheit = $this->_ci->KalenderLehreinheitModel->loadWhere(array('kalender_id' => $entry->kalender_id));
if (isError($result_lehreinheit)) return $result_lehreinheit;
$has_lehreinheit = hasData($result_lehreinheit) ? getData($result_lehreinheit)[0] : false;
$entryResult = $this->_loadKalenderEntry($entry->kalender_id);
$result_ort = $this->_ci->KalenderOrtModel->loadWhere(array('kalender_id' => $entry->kalender_id));
if (isError($result_ort)) return $result_ort;
$has_ort = hasData($result_ort) ? getData($result_ort)[0] : false;
if ($entry->status_kurzbz === 'planning')
if (isError($entryResult)) return $entryResult;
$kalender_entry = getData($entryResult);
if (!$this->_checkPermission($kalender_entry->lehreinheit_id))
{
if ($has_ort)
$this->_deleteOrtEntry($has_ort);
if ($has_lehreinheit)
$this->_deleteLehreinheitEntry($has_lehreinheit);
$this->_ci->KalenderModel->delete(array('tbl_kalender.kalender_id' => $entry->kalender_id));
return error([
'message' => $this->_ci->phraseslib->t('ui', 'keineBerechtigung'),
'errorCode' => 'not_allowed'
]);
}
else if ($entry->status_kurzbz === 'sync_preview' || $entry->status_kurzbz === 'sync_live')
if ($kalender_entry->status_kurzbz === 'sync_preview' || $kalender_entry->status_kurzbz === 'sync_live' || $kalender_entry->status_kurzbz === 'planning')
{
$history = $this->getHistory($entry->kalender_id);
$history = hasData($history) ? getData($history) : false;
$history = $this->getHistory($kalender_entry->kalender_id, false, false);
if ($has_ort)
$result = $this->_deleteOrtEntry($has_ort);
$history = hasData($history) && getData($history);
if ($has_lehreinheit)
$result = $this->_deleteLehreinheitEntry($has_lehreinheit);
if ($history)
{
$result = $this->_ci->KalenderModel->update(
array('kalender_id' => $kalender_entry->kalender_id),
array('status_kurzbz' => 'to_delete','status_kurzbz_max' => 'planning')
);
$this->_ci->KalenderModel->delete(array('tbl_kalender.kalender_id' => $entry->kalender_id));
if (isError($result))
return error($result);
}
else
{
if (!isEmptyArray($kalender_entry->ort_kurzbz))
{
$result = $this->_deleteOrtEntry($kalender_entry);
if (isError($result))
return error($result);
}
if (!isEmptyArray($kalender_entry->lehreinheit_id))
{
$result = $this->_deleteLehreinheitEntry($kalender_entry);
if (isError($result))
return error($result);
}
$result = $this->_ci->KalenderModel->delete(array('tbl_kalender.kalender_id' => $kalender_entry->kalender_id));
if (isError($result))
return error($result);
}
}
else if ($entry->status_kurzbz === 'preview' || $entry->status_kurzbz === 'live')
else if ($kalender_entry->status_kurzbz === 'preview' || $kalender_entry->status_kurzbz === 'live')
{
//TODO (david) überprüfen ob sinnvoll, verschwindet direkt in den ansichten (student, lecturer)
if ($kalender_entry->status_kurzbz === 'preview')
$new_status = 'to_delete_preview';
else
$new_status = 'to_delete_live';
$result = $this->_ci->KalenderModel->update(
array('kalender_id' => $entry->kalender_id),
array('status_kurzbz' => 'todelete')
array('kalender_id' => $kalender_entry->kalender_id),
array('status_kurzbz' => $new_status)
);
if (isError($result))
return error($result);
}
return success();
@@ -1703,11 +1899,11 @@ class KalenderLib
return $this->_ci->KalenderEventModel->delete(array('kalender_id' => $entry->kalender_id));
}
public function getHistory($kalender_id, $only_previous = false)
public function getHistory($kalender_id, $only_previous = false, $include_self = true)
{
$dbModel = new DB_Model();
$history_entries = $dbModel->execReadOnlyQuery("
WITH RECURSIVE
$history_sql ="
WITH RECURSIVE
vorgaenger(kalender_id, vorgaenger_kalender_id) AS (
SELECT kalender_id, vorgaenger_kalender_id
FROM lehre.tbl_kalender
@@ -1746,97 +1942,48 @@ class KalenderLib
SELECT kalender_id FROM nachfolger
) combined
JOIN lehre.tbl_kalender USING(kalender_id)
LEFT JOIN orte USING(kalender_id)
ORDER BY kalender_id DESC
" . ($only_previous ? ' LIMIT 1' : ''), array($kalender_id, $kalender_id));
LEFT JOIN orte USING(kalender_id)";
return $history_entries;
$params = array($kalender_id, $kalender_id);
if (!$include_self)
{
$history_sql .= " WHERE kalender_id != ?";
$params[] = $kalender_id;
}
$history_sql .= " ORDER BY kalender_id DESC" . ($only_previous ? ' LIMIT 1' : '');
return $dbModel->execReadOnlyQuery($history_sql, $params);
}
//TODO in eine eigen Lib?
public function sync()
private function _checkPermission($lehreinheit_ids)
{
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
if (isEmptyArray($lehreinheit_ids))
return true;
$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', 'planning', 'preview'));
$this->_ci->KalenderModel->addOrder('tbl_kalender.kalender_id', 'DESC');
$to_update = $this->_ci->KalenderModel->load();
$allowed = false;
if (!hasData($to_update))
return success();
$this->_ci->LehreinheitModel->addSelect('studiengang_kz');
$this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrveranstaltung', 'tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id');
$this->_ci->LehreinheitModel->db->where_in('lehreinheit_id', $lehreinheit_ids);
$mail_infos = [];
$lehreinheit = $this->_ci->LehreinheitModel->load();
if (!hasData($lehreinheit))
return error('Lehreinheit nicht gefunden');
foreach (getData($to_update) as $entry)
$studiengaenge = array_unique(array_column(getData($lehreinheit), 'studiengang_kz'));
foreach ($studiengaenge as $studiengang_kz)
{
if ($entry->status_kurzbz === 'todelete')
if ($this->_ci->permissionlib->isBerechtigt('lehre/lvplan', 'suid', $studiengang_kz))
{
$this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'deleted'));
$this->_archiveVorgaenger($entry->vorgaenger_kalender_id, false);
$mail_infos[] = array('entry' => $entry, 'new_status' => 'deleted', 'notify' => array('lektor', 'student'));
}
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'));
}
if ($entry->status_kurzbz === 'sync_live' || $entry->status_kurzbz === 'planning')
{
$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'));
}
if ($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'));
$allowed = true;
break;
}
}
$this->_ci->load->library('KalenderNotificationLib');
$this->_ci->kalendernotificationlib->sendMails($mail_infos);
return success();
}
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;
}
return $allowed;
}
private function getLanguageIndex()
@@ -10,7 +10,7 @@ class KalenderNotificationLib
$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)
@@ -90,6 +90,7 @@ class KalenderNotificationLib
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('', '[email protected]', 'Lektor Tempus Update', $lektor_entries);
if (!empty($student_entries))
+429
View File
@@ -0,0 +1,429 @@
<?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');
$this->_ci->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
$this->_ci->load->library('KalenderLib');
}
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('to_delete', 'to_delete_live', 'to_delete_preview'));
$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('to_delete', 'to_delete_live', 'to_delete_preview'));
$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()
{
$mail_infos_gesamt = [];
$synced_gesamt = 0;
$todelete_ids_result = $this->_getManuallySetStatus(array('to_delete', 'to_delete_live', 'to_delete_preview'));
if (isError($todelete_ids_result))
return $todelete_ids_result;
$todelete_result = $this->_syncKalenderIds(getData($todelete_ids_result), null);
if (isError($todelete_result))
return $todelete_result;
$todelete_data = getData($todelete_result);
$mail_infos_gesamt = array_merge($mail_infos_gesamt, $todelete_data['mail_infos']);
$synced_gesamt += $todelete_data['synced'];
$preview_ids_result = $this->_getManuallySetStatus(array('sync_preview'));
if (isError($preview_ids_result))
return $preview_ids_result;
$preview_result = $this->_syncKalenderIds(getData($preview_ids_result), 'preview');
if (isError($preview_result))
return $preview_result;
$preview_data = getData($preview_result);
$mail_infos_gesamt = array_merge($mail_infos_gesamt, $preview_data['mail_infos']);
$synced_gesamt += $preview_data['synced'];
$live_ids_result = $this->_getManuallySetStatus(array('sync_live'));
if (isError($live_ids_result))
return $live_ids_result;
$live_result = $this->_syncKalenderIds(getData($live_ids_result), 'live');
if (isError($live_result))
return $live_result;
$live_data = getData($live_result);
$mail_infos_gesamt = array_merge($mail_infos_gesamt, $live_data['mail_infos']);
$synced_gesamt += $live_data['synced'];
if (!empty($mail_infos_gesamt))
{
$this->_ci->load->library('KalenderNotificationLib');
$this->_ci->kalendernotificationlib->sendMails($mail_infos_gesamt);
}
return success(['synced' => $synced_gesamt]);
}
private function _getManuallySetStatus($status_kurzbz_list)
{
if (empty($status_kurzbz_list))
return success([]);
$this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id');
$this->_ci->KalenderModel->db->where_in('status_kurzbz', $status_kurzbz_list);
$result = $this->_ci->KalenderModel->load();
if (isError($result))
return $result;
if (!hasData($result))
return success([]);
return success(array_column(getData($result), 'kalender_id'));
}
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);
if (!hasData($studiengang_result))
{
$organisationseinheiten = $this->_ci->OrganisationseinheitModel->getChilds($oe_kurzbz);
$oe_kurzbz = hasData($organisationseinheiten) ? array_column(getData($organisationseinheiten), 'oe_kurzbz') : [];
}
$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');
if (!is_null($studiengang_kz))
{
$this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.studiengang_kz', $studiengang_kz);
}
else
{
$this->_ci->KalenderModel->db->where_in('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)
{
$return_array = array();
if (!$vorgaenger_kalender_id) return success($return_array);
$current_id = $vorgaenger_kalender_id;
while ($current_id)
{
$vorgaenger = $this->_ci->KalenderModel->load(array('kalender_id' => $current_id));
if (!hasData($vorgaenger)) return success($return_array);
$vorgaenger = getData($vorgaenger)[0];
if ($stop_at_live && $vorgaenger->status_kurzbz === 'live')
return success($return_array);
if (in_array($vorgaenger->status_kurzbz, array('preview', 'live')))
{
$this->_ci->KalenderModel->update(
array('kalender_id' => $current_id),
array('status_kurzbz' => 'archived', 'status_kurzbz_max' => $vorgaenger->status_kurzbz)
);
$return_array[] = $vorgaenger;
}
$current_id = $vorgaenger->vorgaenger_kalender_id;
}
return success($return_array);
}
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 (in_array($entry->status_kurzbz, array('to_delete', 'to_delete_live', 'to_delete_preview')))
{
$notify = array();
if ($entry->status_kurzbz === 'to_delete_live')
{
$status_max = 'live';
$notify = array('lektor', 'student');
}
else if ($entry->status_kurzbz === 'to_delete_preview')
{
$status_max = 'preview';
$notify = array('lektor');
}
else if ($entry->status_kurzbz === 'to_delete')
{
$status_max = 'planning';
}
$this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'deleted', 'status_kurzbz_max' => $status_max));
$mail_infos[] = array('entry' => $entry, 'new_status' => 'deleted', 'notify' => $notify);
$vorgaenger_array = $this->_archiveVorgaenger($entry->vorgaenger_kalender_id);
if (hasData($vorgaenger_array))
{
foreach(getData($vorgaenger_array) as $vorgaenger)
{
$archive_notify = array();
if ($vorgaenger->status_kurzbz === 'preview')
$archive_notify = array('lektor');
else if ($vorgaenger->status_kurzbz === 'live')
{
$archive_notify = array('student', 'lektor');
$history = $this->_ci->kalenderlib->getHistory($vorgaenger->kalender_id, false, false);
if (hasData($history))
{
$history = getData($history);
foreach ($history as $entry)
{
if ($entry->status_kurzbz === 'deleted' && $entry->status_kurzbz_max === 'preview')
{
$archive_notify = array('student');
break;
}
}
}
}
$mail_infos[] = array('entry' => $vorgaenger, 'new_status' => 'deleted', 'notify' => $archive_notify);
}
}
$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);
}
}
+291 -28
View File
@@ -22,13 +22,14 @@ class RaumvorschlagLib
$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('ressource/Zeitsperre_model', 'ZeitsperreModel');
$this->_ci->load->model('ressource/Stunde_model', 'StundeModel');
$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'));
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
}
@@ -36,47 +37,295 @@ class RaumvorschlagLib
{
$event = $this->_ci->kalenderlib->getByKalenderId($kalender_id);
$event = $event[0];
return $this->_getVorschlaegeForEvent($event);
}
$lektor_uids = array_column($event->lektor, 'mitarbeiter_uid');
$gruppen_kurzbz = array_values(array_filter(array_column($event->gruppe, 'gruppe_kurzbz')));
public function getVorschlaegeByLehreinheit($lehreinheit_id, $von, $bis)
{
$teilnehmer = $this->_getLehreinheitTeilnehmer($lehreinheit_id);
$lehrverband_gruppen = array_values(array_filter($event->gruppe, function($gruppe)
$event = (object) [
'kalender_id' => null,
'lehreinheit_id' => [$lehreinheit_id],
'isostart' => (new DateTime($von))->format('c'),
'isoend' => (new DateTime($bis))->format('c'),
'datum' => (new DateTime($von))->format('Y-m-d'),
'lektor' => $teilnehmer->lektor_data,
'gruppe' => $teilnehmer->gruppen_data,
];
return $this->_getVorschlaegeForEvent($event);
}
public function getVorschlaegeSlots($lehreinheit_id, $start_date, $end_date)
{
$lehreinheit_result = $this->_ci->LehreinheitModel->load($lehreinheit_id);
if (!hasData($lehreinheit_result))
return error('Lehreinheit nicht gefunden');
$lehreinheit = getData($lehreinheit_result)[0];
$block = $lehreinheit->stundenblockung;
if ($block <= 0)
return error('Stundenblockung ist ungültig');
$this->_ci->StundeModel->addOrder('stunde', 'ASC');
$stunden_result = $this->_ci->StundeModel->load();
$stunden = hasData($stunden_result) ? array_values(getData($stunden_result)) : [];
if (empty($stunden))
return error('Kein Stunden-Raster gefunden');
$teilnehmer = $this->_getLehreinheitTeilnehmer($lehreinheit_id);
$grp = $this->_splitGruppen($teilnehmer->gruppen_data);
$lektor_uids = array_column($teilnehmer->lektor_data, 'mitarbeiter_uid');
$verplante_events = $this->_ci->kalenderlib->getForRaumvorschlag(
$start_date,
$end_date,
$lektor_uids,
$grp->gruppen_kurzbz,
$grp->lehrverband_gruppen
);
$start_day = strtotime($start_date);
$end_day = strtotime($end_date);
$moegliche_slots = array();
$verplante_zeitsperren = $this->_getZeitsperen($start_date, $end_date, $lektor_uids);
while ($start_day <= $end_day)
{
$tag = date('Y-m-d', $start_day);
$verplante_tages_events = array_values(array_filter($verplante_events, function($event) use ($tag)
{
return $event->datum === $tag;
}));
foreach ($stunden as $index => $start_stunde)
{
if (!isset($stunden[$index + $block - 1]))
continue;
$end_stunde = $stunden[$index + $block - 1];
$slot_start = $tag . ' ' . $start_stunde->beginn;
$slot_end = $tag . ' ' . $end_stunde->ende;
if (!$this->_isFrei($slot_start, $slot_end, $verplante_zeitsperren, $lektor_uids, 'lektor'))
continue;
if (!$this->_isFrei($slot_start, $slot_end, $verplante_tages_events, $lektor_uids, 'lektor'))
continue;
if (!$this->_isFrei($slot_start, $slot_end, $verplante_tages_events, $grp->gruppen_kurzbz, 'gruppe'))
continue;
if (!$this->_isFrei($slot_start, $slot_end, $verplante_tages_events, $grp->lehrverband_gruppen_bezeichnung, 'lehrverband'))
continue;
$moegliche_slots[] = [
'isostart' => (new DateTime($slot_start))->format('c'),
'isoend' => (new DateTime($slot_end))->format('c'),
'marker_isoend' => (new DateTime($tag . ' ' . $start_stunde->ende))->format('c'),
];
}
$start_day = strtotime('+1 day', $start_day);
}
$slots = [];
foreach ($moegliche_slots as $slot)
{
$event_obj = (object) [
'kalender_id' => null,
'lehreinheit_id' => [$lehreinheit_id],
'isostart' => $slot['isostart'],
'isoend' => $slot['isoend'],
];
$raumkandidaten = $this->_getRaumkandidaten($event_obj);
if (empty($raumkandidaten))
continue;
$ratings = $this->_getRatings($verplante_events, $slot, $raumkandidaten, $lektor_uids, $grp->gruppen_kurzbz, $grp->lehrverband_gruppen_bezeichnung);
$best = $ratings[0];
$anzahl_weitere = count($ratings) - 1;
$slots[] = [
'isostart' => $slot['isostart'],
'isoend' => $slot['isoend'],
'marker_isoend' => $slot['marker_isoend'],
'rating' => $this->_scoreToRating($best['score']),
'label' => $anzahl_weitere > 0 ? $best['ort_kurzbz'] . ' (' . $best['score'] . ') + ' . $anzahl_weitere . ' weitere' : $best['ort_kurzbz'] . ' (' . $best['score'] . ')',
'raeume' => $ratings,
];
}
return success($slots);
}
private function _getZeitsperen($start_date, $end_date, $uids)
{
if (empty($uids)) return [];
if (($this->_ci->variablelib->getVar('ignore_kollision') === 'true') || ($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 <', $end_date);
$this->_ci->ZeitsperreModel->db->where('(tbl_zeitsperre.bisdatum + COALESCE(bisstunde_z.ende, \'23:59\'))::timestamp >', $start_date);
$this->_ci->ZeitsperreModel->db->where_in('mitarbeiter_uid', $uids);
$result = $this->_ci->ZeitsperreModel->load();
if (!hasData($result)) return [];
$events = [];
foreach (getData($result) as $row)
{
$von = new DateTime($row->vondatum . ' ' . ($row->von_beginn ?? '00:00'));
$bis = new DateTime($row->bisdatum . ' ' . ($row->bis_ende ?? '23:59'));
$events[] = (object)[
'isostart' => $von->format('c'),
'isoend' => $bis->format('c'),
'lektor' => [['mitarbeiter_uid' => $row->mitarbeiter_uid]]
];
}
return $events;
}
private function _getLehreinheitTeilnehmer($lehreinheit_id)
{
$lektoren_result = $this->_ci->LehreinheitMitarbeiterModel->loadWhere(['lehreinheit_id' => $lehreinheit_id]);
$lektor_data = [];
if (hasData($lektoren_result))
{
$lektor_data = getData($lektoren_result);
}
$gruppen_result = $this->_ci->LehreinheitgruppeModel->getByLehreinheit($lehreinheit_id);
$gruppen_data = [];
if (hasData($gruppen_result))
{
$gruppen_rows = getData($gruppen_result);
foreach ($gruppen_rows as $gruppe)
{
$gruppen_data[] = (array) $gruppe;
}
}
return (object)[
'lektor_data' => $lektor_data,
'gruppen_data' => $gruppen_data,
];
}
private function _splitGruppen($gruppen_data)
{
$gruppen_kurzbz = array_values(array_filter(array_column($gruppen_data, 'gruppe_kurzbz')));
$lehrverband_gruppen = array_values(array_filter($gruppen_data, function($gruppe)
{
return empty($gruppe['gruppe_kurzbz']);
}));
$lehrverband_gruppen_bezeichnung = array_column($lehrverband_gruppen, 'bezeichnung');
return (object)[
'gruppen_kurzbz' => $gruppen_kurzbz,
'lehrverband_gruppen' => $lehrverband_gruppen,
'lehrverband_gruppen_bezeichnung' => $lehrverband_gruppen_bezeichnung
];
}
//TODO (david) umbauen auf CollisionChecks
private function _isFrei($slot_start, $slot_end, $events, $check_array, $type)
{
if (empty($check_array)) return true;
$slot_start_ts = strtotime($slot_start);
$slot_end_ts = strtotime($slot_end);
foreach ($events as $event)
{
$event_start_ts = strtotime($event->isostart);
$event_end_ts = strtotime($event->isoend);
if ($event_end_ts <= $slot_start_ts || $event_start_ts >= $slot_end_ts)
continue;
$event_uids = $this->_getIds($event, $type);
if (!empty(array_intersect($event_uids, $check_array)))
return false;
}
return true;
}
private function _scoreToRating($score)
{
if ($score >= 90)
return 'good';
if ($score >= 60)
return 'mid';
return 'bad';
}
private function _getVorschlaegeForEvent($event)
{
$raumkandidaten = $this->_getRaumkandidaten($event);
if (empty($raumkandidaten)) return [];
$lektor_uids = array_column($event->lektor, 'mitarbeiter_uid');
$grp = $this->_splitGruppen($event->gruppe);
$tages_events = $this->_ci->kalenderlib->getForRaumvorschlag(
$event->datum,
$event->datum,
$lektor_uids,
$gruppen_kurzbz,
$lehrverband_gruppen
$grp->gruppen_kurzbz,
$grp->lehrverband_gruppen
);
$lektor_davor = $this->_getEventDavor($tages_events, $event->isostart, $lektor_uids, 'lektor');
$gruppen_davor = $this->_getEventDavor($tages_events, $event->isostart, $gruppen_kurzbz, 'gruppe');
return $this->_getRatings($tages_events, $event, $raumkandidaten, $lektor_uids, $grp->gruppen_kurzbz, $grp->lehrverband_gruppen_bezeichnung);
}
private function _getRatings($events, $event, $raumkandidaten, $lektor_uids, $gruppen_kurzbz, $lehrverband_gruppen_bezeichnung)
{
$event = (object)$event;
$lektor_davor = $this->_getEventDavor($events, $event->isostart, $lektor_uids, 'lektor');
$gruppen_davor = $this->_getEventDavor($events, $event->isostart, $gruppen_kurzbz, 'gruppe');
$lehrverband_davor = $this->_getEventDavor($events, $event->isostart, $lehrverband_gruppen_bezeichnung, 'lehrverband');
$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 [];
$lehrverband_davor_ort = $lehrverband_davor ? $this->_getOrtDetails($lehrverband_davor->ort_kurzbz) : null;
$unique = [];
foreach ($kandidaten as $raum)
foreach ($raumkandidaten as $raum)
{
$unique[$raum->ort_kurzbz] = $raum;
}
$kandidaten = array_values($unique);
$ratings = [];
foreach ($kandidaten as $raum)
{
$rating = ['ort_kurzbz' => $raum->ort_kurzbz, 'score' => 100, 'details' => []];
$rating = ['ort_kurzbz' => $raum->ort_kurzbz, 'score' => 100, 'details' => [],
'raumtyp_kurzbz' => $raum->raumtyp_kurzbz, 'max_person' => $raum->max_person];
$this->_rateLektor($rating, $raum, $lektor_davor_ort);
$this->_rateGruppen($rating, $raum, $gruppen_davor_ort);
$this->_rateGruppen($rating, $raum, $lehrverband_davor_ort);
Events::trigger('room_rating',
function & () use (&$rating) {
@@ -204,10 +453,7 @@ class RaumvorschlagLib
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');
$event_uids = $this->_getIds($event, $type);
if (empty(array_intersect($event_uids, $uids)))
continue;
@@ -219,6 +465,20 @@ class RaumvorschlagLib
return $kandidat;
}
private function _getIds($event, $type)
{
$event_uids = array();
if ($type === 'lektor')
$event_uids = array_column($event->lektor, 'mitarbeiter_uid');
else if ($type === 'gruppe')
$event_uids = array_column($event->gruppe, 'gruppe_kurzbz');
else if ($type === 'lehrverband')
$event_uids = array_column($event->gruppe, 'bezeichnung');
return $event_uids;
}
private function _getRaumkandidaten($event)
{
$raumtyp = null;
@@ -240,9 +500,19 @@ class RaumvorschlagLib
$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('tbl_kalender.kalender_id !=', $event->kalender_id);
$this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', ['deleted']);
if (!is_null($event->kalender_id))
{
$this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $event->kalender_id);
}
$this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', ['deleted', 'to_delete', 'to_delete_live', 'to_delete_preview', 'archived']);
$this->_ci->KalenderModel->db->where('tbl_kalender_ort.ort_kurzbz IS NOT NULL', null, false);
$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
);
$belegte = $this->_ci->KalenderModel->load();
$belegte_orte = hasData($belegte) ? array_column(getData($belegte), 'ort_kurzbz') : [];
@@ -273,7 +543,7 @@ class RaumvorschlagLib
private function _getFreieRaeume($raumtyp, $belegte_orte)
{
$this->_ci->OrtModel->addSelect('ort_kurzbz, stockwerk, standort_id');
$this->_ci->OrtModel->addSelect('ort_kurzbz, stockwerk, standort_id, max_person, raumtyp_kurzbz');
$this->_ci->OrtModel->addJoin('public.tbl_ortraumtyp', 'ort_kurzbz');
if (!empty($raumtyp))
@@ -290,11 +560,4 @@ class RaumvorschlagLib
return $this->_ci->OrtModel->load();
}
}
@@ -10,6 +10,7 @@ class LectureCollisionCheck implements ICollisionCheck
$this->_ci =& get_instance();
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
$this->_ci->load->model('ressource/Zeitsperre_model', 'ZeitsperreModel');
$this->_ci->load->model('education/Lehreinheitmitarbeiter_model', 'LehreinheitmitarbeiterModel');
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
$this->_ci->load->library('PhrasesLib', array('ui'));
@@ -22,11 +23,17 @@ class LectureCollisionCheck implements ICollisionCheck
public function check($data)
{
if (!isset($data->von, $data->bis, $data->kalender_id)) return [];
if (!isset($data->von, $data->bis)) return [];
if (empty($data->kalender_id) && empty($data->lehreinheit_id) && empty($data->uids)) return [];
if ($this->_ci->variablelib->getVar('ignore_kollision') === 'true') return [];
$uids = $this->_getUids($data->kalender_id);
if (!empty($data->kalender_id))
$uids = $this->_getUids($data->kalender_id);
else if (!empty($data->lehreinheit_id))
$uids = $this->_getUidsFromLehreinheit($data->lehreinheit_id);
else
$uids = $data->uids;
if (empty($uids)) return [];
@@ -58,9 +65,17 @@ class LectureCollisionCheck implements ICollisionCheck
$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('other_kalender.status_kurzbz', array('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview'));
$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->group_start();
$where_ids_chunks = array_chunk($kalender_ids,25);
foreach($where_ids_chunks as $where_ids)
{
$this->_ci->KalenderModel->db->or_where_in('tbl_kalender.kalender_id', $where_ids);
}
$this->_ci->KalenderModel->db->group_end();
$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
@@ -85,9 +100,17 @@ class LectureCollisionCheck implements ICollisionCheck
$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('other_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview']);
$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->group_start();
$where_ids_chunks = array_chunk($kalender_ids,25);
foreach($where_ids_chunks as $where_ids)
{
$this->_ci->KalenderModel->db->or_where_in('tbl_kalender.kalender_id', $where_ids);
}
$this->_ci->KalenderModel->db->group_end();
$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
@@ -114,7 +137,13 @@ class LectureCollisionCheck implements ICollisionCheck
$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);
$this->_ci->KalenderModel->db->group_start();
$where_ids_chunks = array_chunk($kalender_ids,25);
foreach($where_ids_chunks as $where_ids)
{
$this->_ci->KalenderModel->db->or_where_in('tbl_kalender.kalender_id', $where_ids);
}
$this->_ci->KalenderModel->db->group_end();
$result = $this->_ci->KalenderModel->load();
@@ -159,6 +188,20 @@ class LectureCollisionCheck implements ICollisionCheck
return array_unique(array_merge($mitarbeiter_uids, $event_teilnehmer));
}
private function _getUidsFromLehreinheit($lehreinheit_id)
{
$kollisionsfreie_user = unserialize(KOLLISIONSFREIE_USER);
$result = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id));
if (isError($result) || !hasData($result)) return [];
$uids = array_column(getData($result), 'mitarbeiter_uid');
$uids = array_diff(array_filter($uids), $kollisionsfreie_user);
return array_values(array_unique($uids));
}
private function _checkLehreinheit($uids, $data)
{
$kollisionsfreie_user = unserialize(KOLLISIONSFREIE_USER);
@@ -170,8 +213,9 @@ class LectureCollisionCheck implements ICollisionCheck
$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'));
if (!empty($data->kalender_id))
$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', 'to_delete_live', 'to_delete_preview'));
$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)',
@@ -207,8 +251,9 @@ class LectureCollisionCheck implements ICollisionCheck
$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'));
if (!empty($data->kalender_id))
$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', 'to_delete_live', 'to_delete_preview'));
$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)',
@@ -20,7 +20,7 @@ class ResourcesCollisionCheck implements ICollisionCheck
public function check($data)
{
if (!isset($data->betriebsmittel_ids, $data->von, $data->bis, $data->kalender_id)) return [];
if (!isset($data->betriebsmittel_ids, $data->von, $data->bis)) return [];
if (empty($data->betriebsmittel_ids)) return [];
@@ -31,9 +31,10 @@ class ResourcesCollisionCheck implements ICollisionCheck
$this->_ci->KalenderModel->addJoin('lehre.tbl_betriebsmittel_kalender bk', 'bk.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_kalender_gruppen_id');
$this->_ci->KalenderModel->addJoin('wawi.tbl_betriebsmittel bm', 'bm.betriebsmittel_id = bk.betriebsmittel_id');
$this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $data->kalender_id);
if (!empty($data->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_not_in('tbl_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview']);
$this->_ci->KalenderModel->db->where_in('bk.betriebsmittel_id', $data->betriebsmittel_ids);
@@ -66,8 +67,16 @@ class ResourcesCollisionCheck implements ICollisionCheck
$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_not_in('other_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview']);
$this->_ci->KalenderModel->db->group_start();
$where_ids_chunks = array_chunk($kalender_ids,25);
foreach($where_ids_chunks as $where_ids)
{
$this->_ci->KalenderModel->db->or_where_in('tbl_kalender.kalender_id', $where_ids);
}
$this->_ci->KalenderModel->db->group_end();
$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();
@@ -20,16 +20,17 @@ class RoomCollisionCheck implements ICollisionCheck
public function check($data)
{
if (!isset($data->ort_kurzbz, $data->von, $data->bis, $data->kalender_id) || isEmptyArray($data->ort_kurzbz)) return [];
if (!isset($data->ort_kurzbz, $data->von, $data->bis) || 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);
if (!empty($data->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_not_in('tbl_kalender.status_kurzbz', array('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview'));
$this->_ci->KalenderModel->db->where_in('ort_kurzbz', $data->ort_kurzbz);
$result = $this->_ci->KalenderModel->loadWhere(array(
@@ -61,8 +62,16 @@ class RoomCollisionCheck implements ICollisionCheck
$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_not_in('other_kalender.status_kurzbz', array('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview'));
$this->_ci->KalenderModel->db->group_start();
$where_ids_chunks = array_chunk($kalender_ids,25);
foreach($where_ids_chunks as $where_ids)
{
$this->_ci->KalenderModel->db->or_where_in('tbl_kalender.kalender_id', $where_ids);
}
$this->_ci->KalenderModel->db->group_end();
$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();
@@ -19,7 +19,8 @@ class StudentCollisionCheck implements ICollisionCheck
public function check($data)
{
if (!isset($data->von, $data->bis, $data->kalender_id)) return [];
if (!isset($data->von, $data->bis)) return [];
if (!isset($data->kalender_id) && !isset($data->lehreinheit_id)) return [];
if ($this->_ci->variablelib->getVar('ignore_kollision') === 'true') return [];
if ($this->_ci->variablelib->getVar('kollision_student') !== 'true') return [];
@@ -30,7 +31,9 @@ class StudentCollisionCheck implements ICollisionCheck
$dbModel = new DB_Model();
$qry1 = "
if (isset($data->kalender_id))
{
$qry1 = "
SELECT DISTINCT tbl_benutzergruppe.uid
FROM lehre.tbl_kalender
JOIN lehre.tbl_kalender_lehreinheit USING(kalender_id)
@@ -60,13 +63,48 @@ class StudentCollisionCheck implements ICollisionCheck
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)
";
";
$qry_1_param = $data->kalender_id;
}
else
{
$qry1 = "
SELECT DISTINCT tbl_benutzergruppe.uid
FROM lehre.tbl_lehreinheit
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_lehreinheit.lehreinheit_id = ?
AND tbl_benutzergruppe.uid NOT IN ($placeholders)
UNION ALL
SELECT DISTINCT tbl_studentlehrverband.student_uid AS uid
FROM lehre.tbl_lehreinheit
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_lehreinheit.lehreinheit_id = ?
AND tbl_studentlehrverband.student_uid NOT IN ($placeholders)
";
$qry_1_param = $data->lehreinheit_id;
}
$result1 = $dbModel->execReadOnlyQuery($qry1, array_merge(
[$data->kalender_id],
[$qry_1_param],
$kollisionsfreie_user,
[$data->kalender_id],
[$qry_1_param],
$kollisionsfreie_user
));
@@ -74,6 +112,9 @@ class StudentCollisionCheck implements ICollisionCheck
$curUids = array_flip(array_column(getData($result1), 'uid'));
$exclude_kalender = isset($data->kalender_id) ? "AND tbl_kalender.kalender_id != ?" : "";
$exclude_kalender_param = isset($data->kalender_id) ? [$data->kalender_id] : [];
$qry2 = "
SELECT DISTINCT tbl_kalender.kalender_id, tbl_kalender.von, tbl_kalender.bis, tbl_benutzergruppe.uid
FROM lehre.tbl_kalender
@@ -88,8 +129,8 @@ class StudentCollisionCheck implements ICollisionCheck
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')
$exclude_kalender
AND tbl_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview')
AND tbl_benutzergruppe.uid NOT IN ($placeholders)
AND NOT EXISTS (
SELECT 1 FROM lehre.tbl_kalender vorgaenger
@@ -97,7 +138,7 @@ class StudentCollisionCheck implements ICollisionCheck
)
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
@@ -111,8 +152,8 @@ class StudentCollisionCheck implements ICollisionCheck
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')
$exclude_kalender
AND tbl_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview')
AND tbl_studentlehrverband.student_uid NOT IN ($placeholders)
AND NOT EXISTS (
SELECT 1 FROM lehre.tbl_kalender vorgaenger
@@ -121,10 +162,8 @@ class StudentCollisionCheck implements ICollisionCheck
";
$result2 = $dbModel->execReadOnlyQuery($qry2, array_merge(
[$data->bis, $data->von, $data->kalender_id],
$kollisionsfreie_user,
[$data->bis, $data->von, $data->kalender_id],
$kollisionsfreie_user
[$data->bis, $data->von], $exclude_kalender_param, $kollisionsfreie_user,
[$data->bis, $data->von], $exclude_kalender_param, $kollisionsfreie_user
));
if (isError($result2) || !hasData($result2)) return [];
@@ -175,7 +214,7 @@ class StudentCollisionCheck implements ICollisionCheck
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 other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview')
AND NOT EXISTS (
SELECT 1 FROM lehre.tbl_kalender vorgaenger
WHERE vorgaenger.vorgaenger_kalender_id = other_kalender.kalender_id
@@ -213,7 +252,7 @@ class StudentCollisionCheck implements ICollisionCheck
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 other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview')
AND NOT EXISTS (
SELECT 1 FROM lehre.tbl_kalender vorgaenger
WHERE vorgaenger.vorgaenger_kalender_id = other_kalender.kalender_id
@@ -21,18 +21,58 @@ class VerbandCollisionCheck implements ICollisionCheck
public function check($data)
{
if (!isset($data->von, $data->bis, $data->kalender_id)) return [];
if (!isset($data->von, $data->bis)) return [];
if (!isset($data->kalender_id) && !isset($data->lehreinheit_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 (!isset($data->kalender_id)) $kollision_reservierung = false;
if (!$kollision_student && !$kollision_reservierung) return [];
$dbModel = new DB_Model();
$collisions = [];
$group_matching_qry = "
(
(
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
)
)
)
";
if ($kollision_student)
{
$union_event = "";
@@ -47,100 +87,111 @@ class VerbandCollisionCheck implements ICollisionCheck
";
}
$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
$other_lehreinheitgruppe_subquery = "
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 ."
";
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)
)
)
if (isset($data->kalender_id))
{
$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 (
". $other_lehreinheitgruppe_subquery ."
) 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 (
". $other_lehreinheitgruppe_subquery ."
) 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', 'to_delete_live', 'to_delete_preview')
AND current_lehreinheitguppe.studiengang_kz = other_lehreinheitguppe.studiengang_kz
AND current_lehreinheitguppe.semester = other_lehreinheitguppe.semester
AND ". $group_matching_qry ."
AND other_kalender.kalender_id NOT IN (
SELECT vorgaenger_kalender_id
FROM lehre.tbl_kalender
WHERE vorgaenger_kalender_id IS NOT NULL
)
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,
]);
$params = [$data->bis, $data->von, $data->kalender_id];
}
else
{
$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_lehreinheitgruppe current_lehreinheitguppe
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.von < ?
AND other_kalender.bis > ?
JOIN (
". $other_lehreinheitgruppe_subquery ."
) 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_lehreinheitguppe.lehreinheit_id = ?
AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview')
AND current_lehreinheitguppe.studiengang_kz = other_lehreinheitguppe.studiengang_kz
AND current_lehreinheitguppe.semester = other_lehreinheitguppe.semester
AND ". $group_matching_qry ."
AND other_kalender.kalender_id NOT IN (
SELECT vorgaenger_kalender_id
FROM lehre.tbl_kalender
WHERE vorgaenger_kalender_id IS NOT NULL
)
";
$params = [$data->bis, $data->von, $data->lehreinheit_id];
}
$result = $dbModel->execReadOnlyQuery($sql_gruppen, $params);
if (!isError($result) && hasData($result))
{
@@ -187,7 +238,7 @@ class VerbandCollisionCheck implements ICollisionCheck
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 other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview')
AND current_event_teilnehmer.studiengang_kz = other_event_teilnehmer.studiengang_kz
AND current_event_teilnehmer.semester = other_event_teilnehmer.semester
AND (
@@ -301,7 +352,7 @@ class VerbandCollisionCheck implements ICollisionCheck
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 other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview')
AND current_lehreinheitguppe.studiengang_kz = other_lehreinheitguppe.studiengang_kz
AND current_lehreinheitguppe.semester = other_lehreinheitguppe.semester
AND (
@@ -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';
}
}
+3 -1
View File
@@ -7,12 +7,14 @@
'vue3' => true,
'primevue3' => true,
'tabulator5' => true,
'filtercomponent' => true,
'navigationcomponent' => true,
'vuedatepicker11' => true,
'tags' => true,
'phrases' => array(
'global',
'ui',
'notiz',
'lehre'
),
'customCSSs' => [
'public/css/components/vue-datepicker.css',
+2
View File
@@ -8,6 +8,8 @@
@import './components/Notiz.css';
@import './components/Messages.css';
@import './components/AppMenu.css';
@import './components/horizontalsplit.css';
html {
font-size: .875em;
+34 -5
View File
@@ -110,15 +110,23 @@ body {
background-color: var(--event-bg);
}
.parkingevent {
.course-parker {
position: relative;
border: 1px dashed !important;
border-color: #AAAAAA;
padding: 0.5rem;
margin-bottom: 0.5rem;
margin-left: 0.5rem;
margin-right: 0.5rem;
margin: 0.5rem;
background-color: var(--event-bg);
color: var(--event-color, var(--fhc-calendar-color-event, #000));
}
.unpark-btn {
position: absolute;
top: 2px;
right: 2px;
padding: 0 4px;
border: none;
background: transparent;
}
:root{
--fhc-calendar-pane-height: calc(100vh - 120px);
}
@@ -171,6 +179,21 @@ body {
}
.bg-raumvorschlag-slot {
opacity: .70;
}
.bg-raumvorschlag-slot.rating-good {
background-color: var(--bs-success);
}
.bg-raumvorschlag-slot.rating-mid {
background-color: var(--bs-warning);
}
.bg-raumvorschlag-slot.rating-bad {
background-color: var(--bs-danger);
}
.wish-w--2 {
background-color: #FF2200;
}
@@ -185,3 +208,9 @@ body {
}
.room-option {
display: grid;
grid-template-columns: 20px minmax(80px, 110px) minmax(100px, 1fr) auto 20px 50px;
align-items: center;
width: 100%;
}
+7
View File
@@ -68,9 +68,16 @@
/* Grid */
.fhc-calendar-base-grid {
--fhc-calendar-axis-collapsible: .2fr;
--fhc-calendar-axis-collapsible-manual: .1fr;
background-color: var(--fhc-calendar-bg-gap);
grid-gap: var(--fhc-calendar-gap-header, calc(var(--fhc-calendar-gap, 1px) * 2));
}
.fhc-calendar-base-grid-line-collapsed {
overflow: hidden;
}
.fhc-calendar-base-grid .grid-header {
background-color: var(--fhc-calendar-bg-header-gap, var(--fhc-calendar-bg-gap));
}
+3 -10
View File
@@ -1,16 +1,9 @@
export default {
search(query) {
return {
method: 'get',
url: 'api/frontend/v1/tempus/coursepicker/search',
params: { query }
};
},
getByStg(studiengaenge, studiensemester_kurzbz) {
getCourses(filter, studiensemester_kurzbz) {
return {
method: 'post',
url: 'api/frontend/v1/tempus/coursepicker/getByStg',
params: { studiengaenge, studiensemester_kurzbz }
url: 'api/frontend/v1/tempus/coursepicker/getCourses',
params: { ...filter, studiensemester_kurzbz }
};
},
+8
View File
@@ -8,6 +8,14 @@ export default {
params: { ...filter, start_date, end_date }
};
},
getRaumvorschlagSlots(lehreinheit_id, start_date, end_date)
{
return {
method: 'get',
url: '/api/frontend/v1/tempus/Kalender/getRaumvorschlagSlots',
params: { lehreinheit_id, start_date, end_date }
};
},
getPlanLecturer(start_date, end_date)
{
return {
+2 -2
View File
@@ -48,11 +48,11 @@ export default {
url: '/api/frontend/v1/tempus/Reservierung/getRollen',
};
},
addReservierung(titel, beschreibung, ort_kurzbz, start_date, end_date, teilnehmer, specialFinalGroups, specialGroups, groups) {
addReservierung(titel, beschreibung, orte, 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}
params: { titel, beschreibung, orte, start_date, end_date, teilnehmer, specialFinalGroups, specialGroups, groups}
};
},
+83
View File
@@ -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 }
};
},
};
+2
View File
@@ -16,6 +16,7 @@
*/
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";
@@ -27,6 +28,7 @@ const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(),
routes: [
{ path: `/${ciPath}/Tempus`, component: FhcTempus },
{ path: `/${ciPath}/Tempus/sync`, component: FhcTempusSync },
]
});
+8
View File
@@ -137,12 +137,14 @@ export default {
"update:date",
"update:mode",
"update:range",
"update:date-range",
"drop"
],
data() {
return {
internalView: null,
internalDate: null,
pickedRangeEnd: null,
modalEvent: null
};
},
@@ -272,6 +274,10 @@ export default {
}
},
handleDateRange({ start, end }) {
this.pickedRangeEnd = end;
this.$emit('update:date-range', { start, end });
},
showEventModal(eventObj) {
this.modalEvent = eventObj;
this.$refs.modal.show();
@@ -300,6 +306,7 @@ export default {
class="card-header"
v-model:date="cDate"
v-model:mode="cMode"
@update:date-range="handleDateRange"
@prev="clickPrev"
@next="clickNext"
@click:mode="$emit('click:mode', $event)"
@@ -316,6 +323,7 @@ export default {
:is="modes ? modes[cMode] : null || 'div'"
ref="mode"
v-model:current-date="cDate"
:range-end="pickedRangeEnd"
@update:range="$emit('update:range', $event)"
@request-modal-open="showEventModal"
@request-modal-close="hideEventModal"
+28 -3
View File
@@ -61,7 +61,11 @@ export default {
flipAxis: Boolean,
allDayEvents: Boolean,
axisMainCollapsible: Boolean,
snapToGrid: Boolean
snapToGrid: Boolean,
dayVisibility: {
type: Array,
default: null
}
},
data() {
return {
@@ -160,7 +164,17 @@ export default {
hasValidEvents() {
return this.events.find(e => e.length);
},
styleGridCols() {
styleGridCols()
{
if (this.dayVisibility)
{
let anyVisible = this.dayVisibility.some(day => day);
if (anyVisible)
{
return this.axisMain.map((day, i) => this.isDayCollapsed(i) ? 'var(--fhc-calendar-axis-collapsible-manual, 0.1fr)' : '1fr').join(' ');
}
}
let cols = 'repeat(' + this.axisMain.length + ', 1fr)';
if (this.axisMainCollapsible) {
if (this.hasValidEvents)
@@ -479,6 +493,16 @@ export default {
}
});
},
isDayCollapsed(index)
{
if (this.dayVisibility)
{
let anyVisible = this.dayVisibility.some(day => day);
if (anyVisible)
return !this.dayVisibility[index];
}
return this.axisMainCollapsible && this.hasValidEvents && !this.events[index].length;
}
},
setup()
{
@@ -504,7 +528,7 @@ export default {
v-for="(date, index) in axisMain"
:key="index"
class="main-header"
:class="{'collapsed-header': axisMainCollapsible && hasValidEvents && !events[index].length}"
:class="{'collapsed-header': isDayCollapsed(index)}"
:style="'grid-' + axisCol + ':' + (2+index)"
>
<slot name="main-header" v-bind="{ index, date }" />
@@ -592,6 +616,7 @@ export default {
:date="date"
:events="eventsNormal[index]"
:backgrounds="backgrounds[index]"
:class="{ 'fhc-calendar-base-grid-line-collapsed': isDayCollapsed(index) }"
style="position:relative"
@resize-start="handleResizeStart"
:style="'grid-' + axisRow + ':1/-1;grid-' + axisCol + ':' + (1+index)"
@@ -9,6 +9,10 @@ export default {
event: { type: Object, required: true },
parked: Boolean
},
emits: [
'select-kw',
'unpark'
],
computed: {
dragKalenderCollection() {
return this.event
@@ -16,21 +20,48 @@ export default {
topicString() {
return Array.isArray(this.event.orig.topic) ? this.event.orig.topic.join(', ') : this.event.orig.topic;
},
datum() {
return luxon.DateTime.fromISO(this.event.orig.datum).toFormat('dd.MM.yyyy')
},
kw() {
return luxon.DateTime.fromISO(this.event.orig.datum).startOf('week', { useLocaleWeeks: true }).localWeekNumber
},
},
methods: {
eventStyle(event) {
if (!event.farbe)
return undefined;
return '--event-bg:#' + event.farbe;
},
jumpToKw()
{
this.$emit('select-kw', this.kw);
},
unpark(evt)
{
this.$emit('unpark', this.event);
}
},
template: `
<div
class="fhc-calendar-base-grid-line-event event"
class="course-parker event"
v-draggable:move.noimage="dragKalenderCollection"
style="border:1px"
:style="eventStyle(event.orig)"
data-cy="calendar-event"
>
<button v-if="parked" class="unpark-btn" type="button" @click="unpark">
<i class="fa-solid fa-xmark"></i>
</button>
<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>
<span class="text-decoration-underline" @click="jumpToKw">KW: {{ kw }}</span> {{ datum }}
</div>
<div>
{{ event.orig.beginn }}–{{ event.orig.ende }}
</div>
</div>
`
};
};
+6 -2
View File
@@ -32,7 +32,8 @@ export default {
"prev",
"click:mode",
"update:date",
"update:mode"
"update:mode",
"update:date-range"
],
data() {
return {
@@ -109,6 +110,7 @@ export default {
class="btn btn-outline-secondary border-0"
@click="$emit('prev')"
:disabled="open"
v-if="mode !== 'tableList'"
>
<i class="fa fa-chevron-left"></i>
</button>
@@ -116,6 +118,7 @@ export default {
:mode="mode"
:date="date"
@update:date="$emit('update:date', $event)"
@update:date-range="$emit('update:date-range', $event)"
@open="open = true"
@closed="open = false"
:list-length="modeOptions.length"
@@ -124,6 +127,7 @@ export default {
class="btn btn-outline-secondary border-0"
@click="$emit('next')"
:disabled="open"
v-if="mode !== 'tableList'"
>
<i class="fa fa-chevron-right"></i>
</button>
@@ -131,4 +135,4 @@ export default {
</div>
</div>
`
}
}
@@ -24,8 +24,14 @@ export default {
}
},
emits: [
"update:date"
"update:date",
"update:date-range"
],
data() {
return {
rangeEnd: null
};
},
computed: {
convertedDate() {
// convert to target TZ then strip TZ Information
@@ -34,34 +40,37 @@ export default {
},
current() {
switch (this.mode) {
case "month":
return {month: this.convertedDate.month-1, year: this.convertedDate.year};
case "list":
return [this.convertedDate.startOf('day').ts, this.convertedDate.startOf('day').plus({ days: this.listLength }).ts - 1];
case "week":
case "tableList":
return [this.convertedDate.startOf('week', { useLocaleWeeks: true }).ts, this.convertedDate.endOf('week', { useLocaleWeeks: true }).ts];
case "day":
return this.convertedDate;
default:
return null;
case "month":
return {month: this.convertedDate.month-1, year: this.convertedDate.year};
case "list":
return [this.convertedDate.startOf('day').ts, this.convertedDate.startOf('day').plus({ days: this.listLength }).ts - 1];
case "week":
return [this.convertedDate.startOf('week', { useLocaleWeeks: true }).ts, this.convertedDate.endOf('week', { useLocaleWeeks: true }).ts];
case "tableList":
return [this.convertedDate.startOf('day').ts, (this.rangeEnd || this.convertedDate).endOf('day').ts];
case "day":
return this.convertedDate;
default:
return null;
}
},
title() {
switch (this.mode) {
case "month":
return this.date.toLocaleString({ month: 'long', year: 'numeric' });
case "week":
case "tableList":
var year = this.date.localWeekYear;
var week = this.date.toFormat('nn');
return this.$p.t('calendar/year_kw', { year, week });
case "list":
return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + this.date.plus({ days: this.listLength - 1 }).toLocaleString(luxon.DateTime.DATE_FULL);
case "day":
return this.date.toLocaleString(luxon.DateTime.DATE_FULL);
default:
return 'View not Supported';
case "month":
return this.date.toLocaleString({ month: 'long', year: 'numeric' });
case "week":
var year = this.date.localWeekYear;
var week = this.date.toFormat('nn');
return this.$p.t('calendar/year_kw', { year, week });
case "tableList":
const end = this.rangeEnd ? this.rangeEnd.setZone(this.timezone, { keepLocalTime: true }) : this.date;
return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + end.toLocaleString(luxon.DateTime.DATE_FULL);
case "list":
return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + this.date.plus({ days: this.listLength - 1 }).toLocaleString(luxon.DateTime.DATE_FULL);
case "day":
return this.date.toLocaleString(luxon.DateTime.DATE_FULL);
default:
return 'View not Supported';
}
},
weekStart() {
@@ -72,20 +81,28 @@ export default {
update(value) {
let date;
switch (this.mode) {
case "month":
value.month++;
date = luxon.DateTime.fromObject(value).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
break;
case "list":
case "week":
case "tableList":
date = luxon.DateTime.fromJSDate(value[0]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
break;
case "day":
date = luxon.DateTime.fromJSDate(value).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
break;
default:
return; // Don't update if the value is invalid!
case "month":
value.month++;
date = luxon.DateTime.fromObject(value).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
break;
case "list":
case "week":
date = luxon.DateTime.fromJSDate(value[0]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
break;
case "tableList":
if (!Array.isArray(value) || !value[0] || !value[1])
return;
this.rangeEnd = luxon.DateTime.fromJSDate(value[1]);
const start = luxon.DateTime.fromJSDate(value[0]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
const end = this.rangeEnd.setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
this.$emit('update:date', start);
this.$emit('update:date-range', { start, end });
return;
case "day":
date = luxon.DateTime.fromJSDate(value).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
break;
default:
return; // Don't update if the value is invalid!
}
this.$emit("update:date", date);
},
@@ -99,8 +116,8 @@ export default {
@update:model-value="update"
:format="() => title"
:month-picker="mode == 'month'"
:week-picker="mode == 'week' || mode == 'tableList'"
:range="mode == 'list' ? { autoRange: listLength - 1 } : false"
:week-picker="mode == 'week'"
:range="mode == 'list' ? { autoRange: listLength - 1 } : (mode == 'tableList' ? { partialRange: false } : false)"
:text-input="mode == 'day'"
:week-start="weekStart"
:week-numbers="{ type: weekNumbers }"
+20 -72
View File
@@ -1,109 +1,57 @@
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
},
rangeEnd: {
type: luxon.DateTime,
default: null
}
},
emits: [
"update:currentDate",
"update:range",
"click",
"requestModalOpen"
],
data() {
return {
focusDate: this.currentDate,
rangeOffset: 0
focusEnd: this.rangeEnd || this.currentDate
};
},
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);
return luxon.Interval.fromDateTimes(this.focusDate.startOf('day'), this.focusEnd.endOf('day'));
}
},
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);
}
}
currentDate(newDate) {
this.focusDate = newDate;
if (!this.rangeEnd)
this.focusEnd = newDate;
this.$emit('update:range', this.range);
},
rangeEnd(newEnd) {
this.focusEnd = newEnd || this.focusDate;
this.$emit('update:range', this.range);
}
},
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>
<table-view ref="view" v-bind="$attrs" :day="focusDate" :end="focusEnd">
<template v-slot="slot"><slot v-bind="slot" mode="week" /></template>
</table-view>
</div>
`
}
}
@@ -1,8 +1,6 @@
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",
@@ -20,6 +18,10 @@ export default {
day: {
type: luxon.DateTime,
required: true
},
end: {
type: luxon.DateTime,
required: true
}
},
data()
@@ -30,10 +32,13 @@ export default {
},
computed: {
start() {
return this.day.startOf('week', { useLocaleWeeks: true });
return this.day.startOf('day');
},
tableEnd() {
return this.end.endOf('day');
},
preparedEvents() {
const end = this.start.plus({ days: 7 });
const end = this.tableEnd;
return this.events
.filter(e => e.start < end && e.end > this.start)
.sort((a, b) => a.start.ts - b.start.ts)
@@ -112,15 +117,6 @@ export default {
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
@@ -2,6 +2,8 @@ import CalendarGrid from '../../Base/Grid.js';
import LabelDay from '../../Base/Label/Day.js';
import LabelDow from '../../Base/Label/Dow.js';
import LabelTime from '../../Base/Label/Time.js';
import FormInput from "../../../Form/Input.js";
export default {
name: "WeekView",
@@ -9,7 +11,8 @@ export default {
CalendarGrid,
LabelDay,
LabelDow,
LabelTime
LabelTime,
FormInput
},
inject: {
timeGrid: "timeGrid",
@@ -23,6 +26,11 @@ export default {
},
collapseEmptyDays: Boolean
},
data() {
return {
visibleDays: JSON.parse(localStorage.getItem('fhc-calender-visible-days')) ?? [true, true, true, true, true, true, true]
}
},
computed: {
start() {
return this.day.startOf('week', { useLocaleWeeks: true });
@@ -44,11 +52,19 @@ export default {
const end = this.hoursPlan?.end ?? 23;
return Array.from({ length: end - start + 1 }, (e, i) => luxon.Duration.fromObject({ hours: i + start }));
}
},
dayVisibility() {
return this.axisMain.map(date => this.visibleDays[date.weekday -1])
}
},
methods: {
isToday(date) {
return date.hasSame(luxon.DateTime.now().setZone(this.timezone), 'day');
},
toggleDay(day)
{
this.visibleDays.splice(day, 1, !this.visibleDays[day])
localStorage.setItem('fhc-calender-visible-days', JSON.stringify(this.visibleDays))
}
},
template: /* html */`
@@ -60,9 +76,10 @@ export default {
:axis-main-collapsible="collapseEmptyDays"
:snap-to-grid="!!timeGrid"
all-day-events
:day-visibility="dayVisibility"
>
<template #main-header="{ date }">
<div :class="{ today: isToday(date) }">
<template #main-header="{ date, index }">
<div :class="{ today: isToday(date) }" class="d-flex flex-column align-items-center">
<label-dow
v-bind="{ date }"
@cal-click="evt => evt.detail.source = 'day'"
@@ -70,6 +87,13 @@ export default {
<label-day
v-bind="{ date }"
/>
<form-input
type="checkbox"
:checked="visibleDays[date.weekday - 1]"
@change="toggleDay(date.weekday - 1)"
@click.stop
>
</form-input>
</div>
</template>
<template #part-header="{ part }">
+24 -3
View File
@@ -16,6 +16,10 @@ export default {
},
inject: {
renderers: {from: 'renderers'},
canToggleGrid: {
from: 'canToggleGrid',
default: false
},
appConfig: {
from: 'appConfig',
default: {
@@ -59,15 +63,21 @@ export default {
type: Array,
default: () => ['all']
},
showEvents: {
type: Boolean,
default: true
}
},
emits: [
"update:date",
"update:mode",
"update:range",
"update:date-range",
"drop",
"resize",
"event-hover",
"event-unhover"
"event-unhover",
"open-reservierung"
],
data() {
@@ -146,9 +156,18 @@ export default {
return '--event-bg:#' + event.farbe;
},
updateRange(rangeInterval) {
if (this.currentMode === 'tableList')
return;
this.rangeInterval = rangeInterval;
this.$emit('update:range', rangeInterval);
},
handleDateRange({ start, end }) {
this.rangeInterval = luxon.Interval.fromDateTimes(start.startOf('day'), end.endOf('day'));
this.reset();
this.$emit('update:range', this.rangeInterval);
this.$emit('update:date-range', { start, end });
},
ondrop(payload){
this.$emit('drop', payload);
},
@@ -224,10 +243,11 @@ export default {
@update:date="(newDate, newMode) => $emit('update:date', newDate, newMode)"
@update:mode="(newMode, newDate) => { currentMode = newMode; $emit('update:mode', newMode, newDate) }"
@update:range="updateRange"
@update:date-range="handleDateRange"
>
<template v-slot="{ event, mode }">
<div
:class="['event-type-' + event.type + ' ' + mode + 'PageContainer', { 'event--parked': parkedEvents.has(String(event.kalender_id)) }]"
:class="['event-type-' + event.type + ' ' + mode + 'PageContainer', { 'event--parked': parkedEvents.has(String(event.kalender_id)) }, {'event--opacity': !showEvents}]"
:type="mode == 'day' ? 'button' : undefined"
:style="eventStyle(event)"
@mouseenter="$emit('event-hover', event)"
@@ -256,6 +276,7 @@ export default {
class="d-flex align-items-center gap-2"
style="cursor:pointer"
@click="showRaster = !showRaster"
v-if="canToggleGrid"
>
<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>
@@ -281,4 +302,4 @@ export default {
</div>
</template>
</fhc-calendar>`
}
}
+71 -21
View File
@@ -14,19 +14,32 @@ export default {
type: Array,
default: [],
},
lecturers: {
type: Array,
default: [],
},
studiensemester: {
type: String,
default: null
},
previewLehreinheitId: {
type: [String, Number],
default: null
},
previewLoading: {
type: Boolean,
default: false
},
},
emits: ['select-lecturer', 'select-kw'],
emits: ['select-lecturer', 'select-kw', 'preview-raumvorschlag'],
data() {
return {
searchparam: '',
allCourses: [],
sortBy: null,
multiWeekIds: new Set(),
favorites: new Set()
favorites: new Set(),
filter: {}
}
},
computed: {
@@ -86,32 +99,45 @@ export default {
studiengaenge: {
deep: true,
handler(val) {
this.searchparam = '';
this.loadCoursesByStg(val);
this.loadCourses();
}
},
lecturers: {
deep: true,
handler() {
this.loadCourses();
}
},
studiensemester() {
if (this.studiengaenge)
this.loadCoursesByStg(this.studiengaenge);
this.loadCourses();
},
},
methods: {
async loadCoursesByStg(stg) {
if (stg.length <= 0)
{
this.allCourses = [];
return;
async loadCourses()
{
const hasLektoren = this.lecturers.length > 0;
const hasStg = this.studiengaenge.length > 0;
this.filter = {};
if (hasLektoren)
this.filter.uid = this.lecturers;
if (hasStg) {
this.filter.stg = this.studiengaenge.map(
({ stg_kz, semester, orgform_kurzbz }) => ({
stg_kz,
semester,
orgform_kurzbz,
}),
);
}
let payload = stg.map(({ stg_kz, semester, orgform_kurzbz }) => ({
stg_kz,
semester,
orgform_kurzbz
}));
this.$api.call(ApiCoursePicker.getByStg(payload, this.studiensemester))
if (Object.keys(this.filter).length === 0)
return this.allCourses = [];
this.$api.call(ApiCoursePicker.getCourses(this.filter, this.studiensemester))
.then(result => {
this.allCourses = result.data.map(e => ({
this.allCourses = result?.data?.map(e => ({
lehreinheit_id: e.lehreinheit_id,
lektoren: e.lektoren,
raumtyp: e.raumtyp,
@@ -177,7 +203,7 @@ export default {
},
reload()
{
this.loadCoursesByStg(this.studiengaenge);
this.loadCourses();
},
toggleMultiWeek(course)
{
@@ -210,6 +236,17 @@ export default {
{
return this.favorites.has(course.lehreinheit_id);
},
selectRaumvorschlag(course)
{
if (this.isRaumvorschlagActive(course))
this.$emit('preview-raumvorschlag', null);
else
this.$emit('preview-raumvorschlag', course.orig);
},
isRaumvorschlagActive(course)
{
return this.previewLehreinheitId != null && this.previewLehreinheitId === course.lehreinheit_id;
},
},
mounted() {
this.favorites = new Set(JSON.parse(localStorage.getItem('tempus_coursepicker_favs') || '[]'));
@@ -253,7 +290,7 @@ export default {
</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">
<div v-if="Object.keys(filter).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>
@@ -264,7 +301,7 @@ export default {
style="cursor:grab"
:style="courseStyle(course)"
class="course-picker-row rounded-3 mb-2 p-2"
:class="{ 'course-picker-row-pinned': isPinned(course) }"
:class="{ 'course-picker-row-pinned': isPinned(course), 'course-picker-row-preview': isRaumvorschlagActive(course) }"
v-draggable:move.noimage="dragLehreinheitCollection(course)"
tabindex="0"
>
@@ -284,6 +321,19 @@ export default {
v-tooltip="isMultiWeek(course) ? 'MultiWeek aktiv' : 'MultiWeek Verplanung aktivieren'"
@click.stop="toggleMultiWeek(course)"
></i>
<i
v-if="!(previewLoading && isRaumvorschlagActive(course))"
class="fa fa-door-open"
:class="isRaumvorschlagActive(course) ? 'text-primary' : 'text-muted'"
style="cursor:pointer"
v-tooltip="isRaumvorschlagActive(course) ? 'Raumvorschlag ausblenden' : 'Raumvorschlag einblenden'"
@click.stop="selectRaumvorschlag(course)"
></i>
<i
v-else
class="fa fa-spinner fa-spin text-primary"
v-tooltip="'Räume werden geprüft...'"
></i>
</div>
<!--TODO(david) entfernen, dient nur für das mappen mit der lvverwaltung-->
@@ -47,6 +47,10 @@ export default {
<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 class="d-flex align-items-center gap-2" @click="l.showCoursePicker = !l.showCoursePicker" style="cursor:pointer">
<i :class="l.showCoursePicker ? 'fa-solid fa-toggle-on text-primary' : 'fa-solid fa-toggle-off text-muted'"></i>
<span class="form-check-label">Course Picker</span>
</div>
</div>
</div>
</div>
@@ -43,6 +43,25 @@ export function getTempusSearchbarOptions(self)
self.addToFilter(data, 'mitarbeiter');
}
},
{
label: "zum Course Picker hinzufügen",
icon: "fas fa-plus",
type: "function",
action: (data) => {
self.addToCoursePicker(data);
}
},
{
label: "LVVerwaltung",
icon: "fas fa-up-right-from-square",
type: "function",
action: (data) => {
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root +
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
"/LVVerwaltung/stdsem/" + self.selectedStudiensemester + "/emp/" + data.uid;
window.open(link, "_blank");
}
},
]
},
}
+1 -2
View File
@@ -20,11 +20,10 @@ export default {
required: true,
},
},
emits: ["focus-searchbar", "language-changed"],
emits: ["language-changed"],
methods: {
focusSearchbar() {
this.$refs.searchbar?.$refs?.input?.focus();
this.$emit("focus-searchbar");
},
},
template: /* html */ `
+353
View File
@@ -0,0 +1,353 @@
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: [],
loading: false
};
},
computed: {
semester() {
if (!this.max_semester)
return [];
return Array.from({length: this.max_semester}, (_, index) => index);
},
studienplanTrigger() {
return [
this.formData.oe_kurzbz,
this.formData.studiensemester_kurzbz,
this.formData.ausbildungssemester
].join('|');
}
},
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;
},
studienplanTrigger() {
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 ?? null;
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.formData.studiensemester_kurzbz);
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.formData.studiensemester_kurzbz);
this.$refs.modal.hide();
})
.catch(this.$fhcAlert.handleSystemError);
},
start(data) {
this.loading = true;
return this.$refs.form
.call(ApiTempusSync.start(data ?? this.formData))
.then(() => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successStart'));
this.$refs.modal.hide();
this.loading = false;
})
.catch(this.$fhcAlert.handleSystemError)
.finally(() => {
this.loading = false;
});
},
hideModal()
{
this.formData = {}
}
},
created() {
this.$api
.call(ApiTempusSync.getSyncStatus())
.then(result => this.syncStati = result.data ?? [])
.catch(error => this.$fhcAlert.handleSystemError(error));
},
template: `
<bs-modal ref="modal" @hideBsModal="hideModal">
<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.organisationseinheittyp_kurzbz }}] {{ organisation.bezeichnung }} {{ ['b', 'm'].includes(organisation.typ) ? organisation.stgbezeichnung : '' }}
</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"
:disabled="loading"
>
<template v-if="mode === 'start'">{{ $p.t('global', 'jetztStarten') }}</template>
<template v-else>{{ $p.t('ui', 'ok') }}</template>
</button>
</template>
</bs-modal>`
};
+3 -2
View File
@@ -9,7 +9,7 @@ export default {
directives: {
drop
},
emits: ['update:parkedKeys'],
emits: ['update:parkedKeys', 'select-kw'],
data() {
return {
parked: [],
@@ -66,11 +66,12 @@ export default {
>
<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
@select-kw="$emit('select-kw', $event)"
@unpark="unpark"
/>
</div>
</div>
+81 -13
View File
@@ -1,11 +1,14 @@
import BsModal from "../Bootstrap/Modal.js";
import ApiKalender from "../../api/factory/tempus/kalender.js";
import FormInput from "../../../js/components/Form/Input.js";
export default {
name: "RaumauswahlModal",
components: {
BsModal,
Listbox: primevue.listbox,
FormInput
},
emits: ["saved"],
data() {
@@ -14,6 +17,9 @@ export default {
selectedRooms: [],
event: null,
loading: false,
mode: 'edit',
pendingEvent: null,
location: null,
};
},
methods: {
@@ -22,10 +28,12 @@ export default {
if (!orig?.kalender_id)
return;
this.mode = 'edit';
this.event = orig;
this.loading = true;
this.entries = [];
this.selectedRooms = [];
this.location = this.event.location
this.$refs.modal.show();
@@ -53,13 +61,51 @@ export default {
},
hide() {
this.selectedRooms = [];
this.pendingEvent = null;
this.$refs.modal.hide();
},
async confirm() {
let ort_kurzbz = this.selectedRooms.map((room) => room.ort_kurzbz);
showForNew(lehreinheit_id, start_time, end_time, vorschlaege)
{
this.mode = 'new'
this.pendingEvent = {lehreinheit_id, start_time, end_time}
this.event = null;
this.loading = false;
this.entries = vorschlaege ?? [];
this.selectedRooms = [];
if (ort_kurzbz.length < 1)
this.$refs.modal.show();
},
async confirm() {
if (this.mode === 'new')
{
const result = await this.$api.call(
ApiKalender.addKalenderEvent(
this.pendingEvent.lehreinheit_id,
this.selectedRooms.map((room) => room.ort_kurzbz),
this.pendingEvent.start_time,
this.pendingEvent.end_time
)
).then((result) => result.data)
if (result.needs_room_selection)
{
this.showForNew(this.pendingEvent.lehreinheit_id, this.pendingEvent.start_time, this.pendingEvent.end_time, result.raum_vorschlaege);
return;
}
this.hide();
this.$emit('saved');
return;
}
let orte = {};
orte.ort_kurzbz = this.selectedRooms.map((room) => room.ort_kurzbz);
orte.location = this.location;
if (orte.ort_kurzbz.length < 1 && orte.location?.trim().length < 1)
{
await this.$api.call(
ApiKalender.deleteOrtEntry(this.event.kalender_id),
@@ -68,7 +114,7 @@ export default {
else
{
await this.$api.call(
ApiKalender.updateKalenderEvent(this.event.kalender_id, {ort_kurzbz}),
ApiKalender.updateKalenderEvent(this.event.kalender_id, {orte}),
);
}
@@ -119,16 +165,38 @@ export default {
listStyle="max-height: 300px"
:emptyMessage="$p.t('ui', 'keineEintraegeGefunden')"
>
<template #option="{ option }">
<div class="d-flex justify-content-between w-100">
<span><i class="fa-solid fa-door-open me-2"></i>{{ option.ort_kurzbz }}</span>
<span
class="text-muted"
v-tooltip="{ value: (option.details || []).join('\\n'), class: 'custom-tooltip' }"
>{{ option.score }}</span>
</div>
</template>
<template #option="{ option }">
<div class="room-option">
<i class="fa-solid fa-door-open text-muted"></i>
<span v-if="option.raumtyp_kurzbz">
[{{ option.raumtyp_kurzbz }}]
</span>
<span v-else></span>
<span class="fw-semibold">
{{ option.ort_kurzbz }}
</span>
<span class="text-muted small text-nowrap">
{{ option.max_person }} Pers.
</span>
<span
class="text-muted text-end"
v-tooltip="{ value: (option.details || []).join('\\n'), class: 'custom-tooltip' }"
>
{{ option.score }}
</span>
</div>
</template>
</Listbox>
<form-input
v-if="mode === 'edit'"
label="Location"
type="textarea"
container-class="col-12"
name="location"
v-model="location"
></form-input>
<div class="d-flex justify-content-end gap-2 mt-3">
<button type="button" class="btn btn-secondary" @click="hide">
@@ -1,13 +1,19 @@
export default {
props:{
props: {
event: {
type: Object,
required: true
}
required: true,
},
},
computed:{
computed: {
classes() {
const classes = ['cis-renderer-lehreinheit-calendar-event', 'calendar-event-default', 'h-100', 'w-100', 'p-1'];
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');
@@ -16,67 +22,79 @@ export default {
return classes;
},
topicString() {
return Array.isArray(this.event.topic) ? this.event.topic.join(', ') : this.event.topic;
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;
let orte = [
...(this.event.ort_kurzbz || []),
this.event.location?.trim(),
].filter(Boolean);
return orte.join(', ');
},
gruppeString() {
return Array.isArray(this.event.gruppe)
? this.event.gruppe.map(gruppe => gruppe.bezeichnung).join(', ')
? 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('global/uhrzeit'), [this.start, this.end].join(' - ')].join(
': ',
),
);
tooltipArray.push([
this.$p.t('profilUpdate/topic'),
this.topicString
].join(": "));
tooltipArray.push(
[this.$p.t('profilUpdate/topic'), this.topicString].join(': '),
);
tooltipArray.push([
this.$p.t('person/ort'),
this.ortString
].join(": "));
tooltipArray.push([this.$p.t('person/ort'), this.ortString].join(': '));
if (this.gruppeString) {
tooltipArray.push([
this.$p.t('lehre/gruppe'),
this.gruppeString
].join(": "));
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(": "));
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(": "));
tooltipArray.push(
[
this.$p.t('lehre/lektor'),
this.event.lektor.map((lektor) => lektor.kurzbz).join('\n'),
].join(': '),
);
}
}
return tooltipArray.join("\n");
return tooltipArray.join('\n');
},
start() {
return luxon.Duration
.fromISOTime(this.event.beginn)
.toISOTime({ suppressSeconds: true });
return luxon.Duration.fromISOTime(this.event.beginn).toISOTime({
suppressSeconds: true,
});
},
end() {
return luxon.Duration
.fromISOTime(this.event.ende)
.toISOTime({ suppressSeconds: true });
return luxon.Duration.fromISOTime(this.event.ende).toISOTime({
suppressSeconds: true,
});
},
tags() {
if (typeof this.event.tags === 'string') {
@@ -87,7 +105,7 @@ export default {
return [];
}
}
return this.event.tags || [];
},
tagsTooltip() {
@@ -95,7 +113,7 @@ export default {
return '';
}
return this.tags.map(tag => tag.beschreibung).join("\n");
return this.tags.map((tag) => tag.beschreibung).join('\n');
},
resourcesTooltip() {
if (!this.event.resources || this.event.resources.length === 0) {
@@ -112,10 +130,10 @@ export default {
}
}
return resources.map(resource => resource.beschreibung).join("\n");
return resources.map((resource) => resource.beschreibung).join('\n');
},
},
template: /*html*/`
template: /*html*/ `
<div
:class="classes"
class="position-relative"
@@ -133,6 +151,9 @@ export default {
class="fa-solid fa-tags"
></i>
</div>
<div class="position-absolute bottom-0 start-0 m-1">
{{event.verplante_stunden}}
</div>
<div
v-if="!event.allDayEvent && event?.beginn && event?.ende"
class="event-time d-none d-xl-grid h-100"
@@ -162,4 +183,4 @@ export default {
</div>
</div>
`,
}
};
@@ -80,9 +80,6 @@ export default {
},
},
methods: {
mehtodNumberPadding: function (number) {
return numberPadding(number);
},
methodFormatDate: function (d) {
return formatDate(d);
},
@@ -200,4 +197,4 @@ export default {
<lv-menu :containerStyles="['p-0']" :rowStyles="['m-0']" v-if="lvMenu.length" :menu="lvMenu" />
</div>`,
}
}
@@ -83,7 +83,8 @@ export default {
.toISOTime({ suppressSeconds: true });
},
ortString() {
return Array.isArray(this.event.ort_kurzbz) ? this.event.ort_kurzbz.join(', ') : this.event.ort_kurzbz;
let orte = [...(this.event.ort_kurzbz || []), this.event.location?.trim()].filter(Boolean)
return orte.join(', ')
},
topicString() {
return Array.isArray(this.event.titel) ? this.event.titel.join(', ') : this.event.titel;
@@ -52,9 +52,6 @@ export default {
},
},
methods: {
mehtodNumberPadding: function (number) {
return numberPadding(number);
},
methodFormatDate: function (d) {
return formatDate(d);
},
@@ -133,4 +130,4 @@ export default {
</table>
</div>`,
}
}
+16 -2
View File
@@ -33,7 +33,8 @@ export default {
show_all_fields: false,
filteredUsers: [],
filteredGroups: [],
abortController: null
abortController: null,
location: null
};
},
created()
@@ -141,11 +142,16 @@ export default {
},
save()
{
let orte = {
ort_kurzbz: this.selected_rooms.map(r => r.ort_kurzbz),
location: this.location
}
this.$api.call(
ApiReservierung.addReservierung(
this.titel,
this.beschreibung,
this.selected_rooms.map(r => r.ort_kurzbz),
orte,
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 })),
@@ -220,6 +226,14 @@ export default {
</template>
</Multiselect>
</div>
<div class="col-6">
<form-input
:label="$p.t('global', 'raum')"
type="textarea"
name="location"
v-model="location"
></form-input>
</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
+90 -68
View File
@@ -1,75 +1,92 @@
import FhcCoursepicker from "./Coursepicker.js";
import LectureSelection from "./Filters/LectureSelection.js";
import VerbandSelection from "./Filters/VerbandSelection.js";
import RoomSelection from "./Filters/RoomSelection.js";
import ParkingSlot from "./ParkingSlot.js";
import StvStudiensemester from "../Stv/Studentenverwaltung/Studiensemester.js";
import FhcCoursepicker from './Coursepicker.js';
import LectureSelection from './Filters/LectureSelection.js';
import VerbandSelection from './Filters/VerbandSelection.js';
import RoomSelection from './Filters/RoomSelection.js';
import ParkingSlot from './ParkingSlot.js';
import StvStudiensemester from '../Stv/Studentenverwaltung/Studiensemester.js';
export default {
name: "TempusSidebarMenu",
components: {
FhcCoursepicker,
LectureSelection,
VerbandSelection,
RoomSelection,
ParkingSlot,
StvStudiensemester,
},
props: {
previewRole: {
type: String,
required: true,
},
rooms: {
type: Array,
required: true,
},
studiengaenge: {
type: Array,
required: true,
},
studiengaengeAll: {
type: Array,
default: () => [],
},
lecturers: {
type: Array,
required: true,
},
selectedStudiensemester: {
type: String,
required: true,
},
},
emits: [
"update:previewRole",
"update:rooms",
"update:studiengaenge",
"update:parkedKeys",
"update:selectedStudiensemester",
"remove-lecturer",
"select-lecturer",
"select-kw",
"sync",
],
methods: {
reloadCoursepicker() {
return this.$refs.coursepicker?.reload();
},
isParked(id) {
return this.$refs.parking?.isParked(id) ?? false;
},
park(items) {
return this.$refs.parking?.park(null, items);
},
unpark(item) {
return this.$refs.parking?.unpark(item);
},
},
template: /* html */ `
name: 'TempusSidebarMenu',
components: {
FhcCoursepicker,
LectureSelection,
VerbandSelection,
RoomSelection,
ParkingSlot,
StvStudiensemester,
},
props: {
previewRole: {
type: String,
required: true,
},
rooms: {
type: Array,
required: true,
},
studiengaenge: {
type: Array,
required: true,
},
studiengaengeAll: {
type: Array,
default: () => [],
},
lecturers: {
type: Array,
required: true,
},
selectedStudiensemester: {
type: String,
required: true,
},
raumvorschlagPreview: {
type: Object,
default: null,
},
raumvorschlagLoading: {
type: Boolean,
default: false,
},
},
emits: [
'update:previewRole',
'update:rooms',
'update:studiengaenge',
'update:parkedKeys',
'update:selectedStudiensemester',
'remove-lecturer',
'select-lecturer',
'select-kw',
'sync',
'preview-raumvorschlag',
],
methods: {
reloadCoursepicker() {
return this.$refs.coursepicker?.reload();
},
isParked(id) {
return this.$refs.parking?.isParked(id) ?? false;
},
park(items) {
return this.$refs.parking?.park(null, items);
},
unpark(item) {
return this.$refs.parking?.unpark(item);
},
},
computed: {
courseLecturers() {
if (!this.lecturers.length) return [];
return this.lecturers
.filter((lecture) => lecture.showCoursePicker)
.map((lecture) => lecture.uid);
},
},
template: /* html */ `
<nav
id="sidebarMenu"
class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100 d-flex flex-column"
class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100 w-100"
>
<div class="sidebar-icons d-flex flex-row align-items-start py-2 gap-1 ps-2">
<button
@@ -149,12 +166,17 @@ export default {
<parking-slot
ref="parking"
@update:parked-keys="$emit('update:parkedKeys', $event)"
@select-kw="$emit('select-kw', $event)"
></parking-slot>
<fhc-coursepicker
ref="coursepicker"
:studiengaenge="studiengaenge"
:studiensemester="selectedStudiensemester"
:lecturers="courseLecturers"
:preview-lehreinheit-id="raumvorschlagPreview?.lehreinheit_id ?? null"
:preview-loading="raumvorschlagLoading"
@preview-raumvorschlag="$emit('preview-raumvorschlag', $event)"
@select-lecturer="$emit('select-lecturer', $event)"
@select-kw="$emit('select-kw', $event)"
></fhc-coursepicker>
+286 -190
View File
@@ -21,7 +21,6 @@ import ApiRenderers from '../../api/factory/renderers.js';
import ApiTempusConfig from '../../api/factory/tempus/config.js';
import drop from '../../directives/drop.js';
import AppConfig from '../AppConfig.js';
import ApiStudiengangTree from '../../api/factory/tempus/studiengangtree.js';
import ApiInfo from '../../api/factory/tempus/info.js';
import Reservierung from './Reservierung.js';
import { getTempusShortcuts } from './shortcuts.js';
@@ -38,6 +37,7 @@ import TempusVerbandMenu from './VerbandMenu.js';
import TempusSidebarMenu from './SidebarMenu.js';
import RaumauswahlModal from './RaumauswahlModal.js';
import LehreinheitModal from './LehreinheitModal.js';
import HorizontalSplit from '../horizontalsplit/horizontalsplit.js';
export default {
name: 'Tempus',
@@ -56,6 +56,7 @@ export default {
TagsAssignmentModal,
RaumauswahlModal,
LehreinheitModal,
HorizontalSplit,
},
props: {
defaultSemester: String,
@@ -93,6 +94,7 @@ export default {
deleteEntries: (origList) => this.deleteEntries(origList),
openRaumauswahl: (orig) => this.$refs.raumModal.show(orig),
},
canToggleGrid: this.permissions.stundenraster,
};
},
data() {
@@ -100,41 +102,25 @@ export default {
appconfig: {},
currentMode: 'week',
configEndpoints: ApiTempusConfig,
endpoint: ApiStudiengangTree,
raumVorschlaege: [],
selected: [],
lv_id: null,
events: null,
minimized: false,
currentlySelectedEvent: null,
hoveredEvent: null,
studiensemesterKurzbz: this.defaultSemester,
lists: {
nations: [],
sprachen: [],
geschlechter: [],
},
renderers: null,
renderers: {},
ort_kurzbz: null,
view: 'room',
parkedKeys: new Set(),
lecturers: [],
studiengaenge: [],
rooms: [],
overlayCache: [],
overlayCache: {},
extraBackgrounds: [],
lastRange: null,
stg: null,
semester: null,
studiensemester_kurzbz: null,
visibleStatusArray: {},
visibleStatus: ['all'],
selectedStudiensemester:
this.studiensemester_kurzbz ?? this.defaultSemester,
calendarDate: luxon.DateTime.now()
.setZone(this.config.timezone)
.toISODate(),
selectedStudiensemester: this.defaultSemester,
calendarDatesByMode: {
week: luxon.DateTime.now().setZone(this.config.timezone).toISODate(),
month: luxon.DateTime.now().setZone(this.config.timezone).toISODate(),
tableList: luxon.DateTime.now()
.setZone(this.config.timezone)
.toISODate(),
},
historyEntries: [],
previewRole: 'planer',
multiWeekModal: {
@@ -145,11 +131,19 @@ export default {
endTime: null,
},
studiengaenge_all: [],
raumvorschlagPreview: null,
raumvorschlagLoading: false,
showEvents: true,
reservierungPending: false,
bcc: null,
};
},
computed: {
currentDay() {
return luxon.DateTime.now().setZone(this.config.timezone).toISODate();
calendarDate() {
return (
this.calendarDatesByMode[this.currentMode] ??
this.calendarDatesByMode.week
);
},
visibleLecturerUids() {
if (!this.lecturers.length) return null;
@@ -157,22 +151,6 @@ export default {
.filter((lecture) => lecture.showEvents)
.map((lecture) => lecture.uid);
},
visibleStatusOptions() {
return Object.entries(this.visibleStatusArray).map(([key, label]) => ({
key,
label,
}));
},
visibleStatusValue() {
if (this.visibleStatus.includes('all'))
return this.visibleStatusOptions.filter(
(visibleStatus) => visibleStatus.key === 'all',
);
return this.visibleStatus.map((status) => ({
key: status,
label: this.visibleStatusArray[status],
}));
},
keyboardShortcuts() {
return getTempusShortcuts(this);
},
@@ -187,6 +165,7 @@ export default {
await this.deleteEntryCall(orig);
this.$refs.calendar.resetEventLoader();
this.$refs.sidebar.reloadCoursepicker();
this.rebuildRaumvorschlag();
},
async deleteEntries(origList) {
let validList = (origList ?? []).filter((orig) => orig?.kalender_id);
@@ -226,7 +205,80 @@ export default {
.call(ApiKalender.syncToStudent(orig.kalender_id))
.then(() => this.$refs.calendar.resetEventLoader());
},
setOrt: function (data) {
rebuildRaumvorschlag() {
if (!this.raumvorschlagPreview?.lehreinheit_id) return;
this.previewRaumvorschlag({
lehreinheit_id: this.raumvorschlagPreview.lehreinheit_id,
});
},
clearRaumvorschlagPreview() {
this.raumvorschlagPreview = null;
this.raumvorschlagLoading = false;
this.rebuildExtraBackgrounds();
this.showEvents = true;
},
async previewRaumvorschlag(orig) {
if (!orig?.lehreinheit_id) {
this.clearRaumvorschlagPreview();
return;
}
if (!this.lastRange) return;
const preview = {
lehreinheit_id: orig.lehreinheit_id,
slots: [],
};
this.raumvorschlagPreview = preview;
this.raumvorschlagLoading = true;
try {
const result = await this.$api.call(
ApiKalender.getRaumvorschlagSlots(
orig.lehreinheit_id,
this.lastRange.start.toISODate(),
this.lastRange.end.toISODate(),
),
);
if (this.raumvorschlagPreview !== preview) return;
preview.slots = (result.data ?? []).map((slot) => ({
class: `rounded-3 bg-raumvorschlag-slot rating-${slot.rating}`,
start: slot.isostart,
matchEnd: slot.isoend,
end: slot.marker_isoend,
label: slot.label,
raeume: slot.raeume,
}));
this.showEvents = preview.slots.length === 0;
} catch (error) {
if (this.raumvorschlagPreview === preview) this.showEvents = true;
this.$fhcAlert.handleSystemError(error);
} finally {
if (this.raumvorschlagPreview === preview) {
this.raumvorschlagLoading = false;
this.rebuildExtraBackgrounds();
}
}
},
findRaumvorschlagSlot(lehreinheit_id, startDT, endDT) {
if (this.raumvorschlagPreview?.lehreinheit_id !== lehreinheit_id)
return null;
return (
this.raumvorschlagPreview.slots.find((slot) => {
const slotStart = luxon.DateTime.fromISO(slot.start);
const slotEnd = luxon.DateTime.fromISO(slot.matchEnd);
return (
slotStart.toMillis() === startDT.toMillis() &&
slotEnd.toMillis() === endDT.toMillis()
);
}) ?? null
);
},
setOrt(data) {
this.ort_kurzbz = data.ort_kurzbz;
this.rooms = [{ ort_kurzbz: data.ort_kurzbz }];
},
@@ -237,7 +289,7 @@ export default {
if (verbandMenu)
bootstrap.Offcanvas.getOrCreateInstance(verbandMenu).hide();
},
onSelectVerband({ path, stg_kz, semester, orgform_kurzbz, name }) {
onSelectVerband({ stg_kz, semester, orgform_kurzbz, name }) {
let exists = this.studiengaenge.some(
(stg) =>
stg.stg_kz == stg_kz &&
@@ -252,25 +304,46 @@ export default {
];
}
},
setEmp: function (data) {
const uid = data.uid;
const label = data.name;
setEmp(data) {
if (!data?.uid) return;
for (const lect of this.lecturers) delete this.overlayCache[lect.uid];
this.lecturers = [
{
uid,
label,
uid: data.uid,
label: data.name,
showEvents: true,
overlays: { blocks: true, wishes: true },
showCoursePicker: false,
},
];
this.$refs.calendar.resetEventLoader();
if (this.lastRange) this.handleRange(this.lastRange);
},
addToFilter: function (filter, type) {
addToCoursePicker(data) {
const uid = data.uid;
let lecturer = this.lecturers.find((lecture) => lecture.uid === uid);
if (!lecturer) {
this.addToFilter(data, 'mitarbeiter');
lecturer = this.lecturers.find((lecture) => lecture.uid === uid);
if (lecturer) {
lecturer.overlays = {
blocks: false,
wishes: false,
};
}
}
if (lecturer) {
lecturer.showCoursePicker = true;
}
},
addToFilter(filter, type) {
if (type === 'ort') {
const ort_kurzbz = filter.ort_kurzbz;
if (!this.rooms.some((room) => room.ort_kurzbz === ort_kurzbz)) {
@@ -279,13 +352,20 @@ export default {
} else if (type === 'mitarbeiter') {
const uid = filter.uid;
const label = filter.name;
if (!this.lecturers.some((l) => l.uid === uid)) {
let lecturer = this.lecturers.find((lecture) => lecture.uid === uid);
if (!lecturer) {
this.lecturers.push({
uid,
label,
showEvents: true,
overlays: { blocks: true, wishes: true },
showCoursePicker: false,
});
} else {
lecturer.showEvents = true;
lecturer.overlays = { blocks: true, wishes: true };
}
}
@@ -304,17 +384,32 @@ export default {
},
{ zone: this.config.timezone },
);
this.calendarDate = date.toISODate();
this.calendarDatesByMode = {
...this.calendarDatesByMode,
week: date.toISODate(),
};
this.currentMode = 'week';
},
handleChangeDate(newDate) {
if (newDate && luxon.DateTime.isDateTime(newDate) && newDate.isValid)
this.calendarDate = newDate.toISODate();
if (!(newDate && luxon.DateTime.isDateTime(newDate) && newDate.isValid))
return;
this.calendarDatesByMode = {
...this.calendarDatesByMode,
[this.currentMode]: newDate.toISODate(),
};
},
handleChangeMode(newMode, newDate) {
if (!newMode) return;
if (newDate && luxon.DateTime.isDateTime(newDate) && newDate.isValid) {
this.calendarDatesByMode = {
...this.calendarDatesByMode,
[this.currentMode]: newDate.toISODate(),
};
}
this.currentMode = newMode;
if (newDate && luxon.DateTime.isDateTime(newDate) && newDate.isValid)
this.calendarDate = newDate.toISODate();
},
updateCollision() {
this.$api
@@ -325,21 +420,6 @@ export default {
})
.catch(this.$fhcAlert.handleSystemErrors);
},
toggleStatus(selected) {
if (!selected || selected.length === 0) {
this.visibleStatus = ['all'];
return;
}
const hasAll = selected.includes('all');
const hadAll = this.visibleStatus.includes('all');
if (hasAll && !hadAll) {
this.visibleStatus = ['all'];
return;
}
this.visibleStatus = selected.filter((k) => k !== 'all');
if (this.visibleStatus.length === 0) this.visibleStatus = ['all'];
},
searchfunction(params) {
return this.$api.call(ApiSearchbar.search(params));
},
@@ -383,51 +463,6 @@ export default {
),
];
},
toDateTime(value, timezone) {
if (luxon.DateTime.isDateTime(value)) return value;
if (value?.date?.isValid) return value.date;
if (typeof value === 'number')
return luxon.DateTime.fromMillis(value, { zone: timezone });
if (value instanceof Date)
return luxon.DateTime.fromJSDate(value, { zone: timezone });
if (typeof value === 'string')
return luxon.DateTime.fromISO(value, { zone: timezone });
return luxon.DateTime.invalid('invalid datetime');
},
getLastEndOfSameDay(startDT, ends) {
if (!ends?.length) return null;
const dayKey = startDT.toISODate();
let lastSameDay = null;
for (const end of ends) {
const dt = luxon.DateTime.isDateTime(end)
? end
: luxon.DateTime.fromISO(String(end), { zone: startDT.zoneName });
if (!dt.isValid) continue;
if (dt.toISODate() === dayKey) lastSameDay = dt;
}
return lastSameDay;
},
clampEndToGrid(startDT, durationMin, ends) {
const calculatedEnd = startDT.plus({ minutes: durationMin });
const lastGridEndSameDay = this.getLastEndOfSameDay(startDT, ends);
if (!lastGridEndSameDay) return calculatedEnd;
return calculatedEnd > lastGridEndSameDay
? lastGridEndSameDay
: calculatedEnd;
},
_parseDates(start, end) {
const startDT = luxon.DateTime.fromISO(start);
const endDT = luxon.DateTime.fromISO(end);
@@ -494,6 +529,7 @@ export default {
() => {
this.$refs.calendar.resetEventLoader();
this.$refs.sidebar.reloadCoursepicker();
this.rebuildRaumvorschlag();
},
);
},
@@ -523,6 +559,8 @@ export default {
)
.then(() => {
this.$refs.calendar.resetEventLoader();
this.$refs.sidebar.reloadCoursepicker();
this.rebuildRaumvorschlag();
this.bcc.postMessage('dropped');
});
}
@@ -546,24 +584,68 @@ export default {
return;
}
return this.$api
.call(
ApiKalender.addKalenderEvent(
obj.orig.lehreinheit_id,
this.rooms.length
? this.rooms.map((r) => r.ort_kurzbz)
: (obj.orig.ort_kurzbz ?? []),
const matchedSlot = this.findRaumvorschlagSlot(
obj.orig.lehreinheit_id,
startDT,
endDT,
);
const raeume = matchedSlot?.raeume ?? null;
start_time,
end_time,
),
)
.then((result) => result.data)
.then((result) => {
this.$refs.calendar.resetEventLoader();
this.$refs.sidebar.reloadCoursepicker();
this.bcc.postMessage('dropped');
});
if (!raeume) {
return this.$api
.call(
ApiKalender.addKalenderEvent(
obj.orig.lehreinheit_id,
this.rooms.length
? this.rooms.map((r) => r.ort_kurzbz)
: (obj.orig.ort_kurzbz ?? []),
start_time,
end_time,
),
)
.then((result) => result.data)
.then((result) => {
if (result.needs_room_selection) {
this.$refs.raumModal.showForNew(
obj.orig.lehreinheit_id,
start_time,
end_time,
result.raum_vorschlaege,
);
return;
}
this.$refs.calendar.resetEventLoader();
this.$refs.sidebar.reloadCoursepicker();
this.rebuildRaumvorschlag();
this.bcc.postMessage('dropped');
});
}
if (raeume.length === 1) {
return this.$api
.call(
ApiKalender.addKalenderEvent(
obj.orig.lehreinheit_id,
raeume[0].ort_kurzbz,
start_time,
end_time,
),
)
.then(() => {
this.$refs.calendar.resetEventLoader();
this.$refs.sidebar.reloadCoursepicker();
this.bcc.postMessage('dropped');
this.rebuildRaumvorschlag();
});
}
this.$refs.raumModal.showForNew(
obj.orig.lehreinheit_id,
start_time,
end_time,
raeume,
);
} else if (obj.type === 'kalender') {
return this._updateKalenderEvent(
obj,
@@ -577,6 +659,7 @@ export default {
id: obj.orig.kalender_id,
});
this.$refs.calendar.resetEventLoader();
this.rebuildRaumvorschlag();
this.bcc.postMessage('dropped');
},
);
@@ -597,6 +680,7 @@ export default {
onMultiWeekConfirmed() {
this.$refs.calendar.resetEventLoader();
this.$refs.sidebar.reloadCoursepicker();
this.rebuildRaumvorschlag();
this.bcc.postMessage('dropped');
},
handleRange(range) {
@@ -620,6 +704,11 @@ export default {
}
this.rebuildExtraBackgrounds();
if (this.raumvorschlagPreview)
this.previewRaumvorschlag({
lehreinheit_id: this.raumvorschlagPreview.lehreinheit_id,
});
},
getOverlays(uid, range, rangeKey) {
@@ -706,6 +795,9 @@ export default {
if (lect.overlays.wishes) res.push(...(entry.wishes || []));
}
if (this.raumvorschlagPreview)
res.push(...this.raumvorschlagPreview.slots);
this.extraBackgrounds = res;
},
@@ -727,10 +819,14 @@ export default {
this.previewRole = role;
this.$refs.calendar.resetEventLoader();
},
openReservierung() {
this.reservierungPending = false;
this.$refs.reservierung?.show();
},
triggerSync() {
this.$api
.call(ApiKalender.sync())
.then(this.$refs.calendar.resetEventLoader());
.then(() => this.$refs.calendar.resetEventLoader());
},
onEventHover(event) {
this.hoveredEvent = event;
@@ -747,7 +843,7 @@ export default {
if (this.$refs.sidebar.isParked(event.kalender_id)) {
this.$refs.sidebar.unpark({ type: event.type, id: event.kalender_id });
} else {
this.$refs.sidebar.park(null, {
this.$refs.sidebar.park({
type: 'kalender',
id: event.kalender_id,
orig: event,
@@ -760,9 +856,6 @@ export default {
this.deleteEntry(event);
},
clearHoveredEvent() {
this.hoveredEvent = null;
},
focusSearchbar() {
this.$refs.header?.focusSearchbar();
},
@@ -780,6 +873,7 @@ export default {
studiengaenge: {
deep: true,
handler() {
this.clearRaumvorschlagPreview();
this.$refs.calendar.resetEventLoader();
},
},
@@ -793,7 +887,7 @@ export default {
});
},
beforeUnmount() {
this.bcc.close();
this.bcc?.close();
},
async created() {
await this.$api
@@ -838,9 +932,6 @@ export default {
}
}
if (this.renderers === null) {
this.renderers = {};
}
if (!this.renderers[rendertype]) {
this.renderers[rendertype] = {};
}
@@ -850,10 +941,6 @@ export default {
}
});
this.$api.call(ApiTempusConfig.getHeader()).then((res) => {
this.visibleStatusArray = res.data.visible_status;
this.visibleStatus = ['all'];
});
this.$api.call(ApiInfo.getStudiengaenge()).then((res) => {
this.studiengaenge_all = res.data;
});
@@ -871,56 +958,65 @@ export default {
:logout-url="logoutUrl"
:searchbaroptions="searchbaroptions"
:searchfunction="searchfunction"
@language-changed="this.$refs.calendar.resetEventLoader();"
@language-changed="this.$refs.calendar.resetEventLoader();"
/>
<div class="container-fluid overflow-hidden heightfull">
<div class="row h-100">
<tempus-app-menu />
<tempus-sidebar-menu
ref="sidebar"
:preview-role="previewRole"
:rooms="rooms"
:studiengaenge="studiengaenge"
:studiengaenge-all="studiengaenge_all"
:lecturers="lecturers"
:selected-studiensemester="selectedStudiensemester"
@update:preview-role="handlePreviewRoleChange"
@update:rooms="rooms = $event"
@update:studiengaenge="studiengaenge = $event"
@update:parked-keys="parkedKeys = $event"
@update:selected-studiensemester="selectedStudiensemester = $event"
@remove-lecturer="removeLecturer"
@select-lecturer="setEmp"
@select-kw="jumpToKw"
@sync="triggerSync"
/>
<main class="col-md-8 ms-sm-auto col-lg-9 col-xl-10">
<fhc-calendar
ref="calendar"
:timezone="config.timezone"
:get-promise-func="getPromiseFunc"
:visible-status="visibleStatus"
:date="calendarDate"
:mode="currentMode"
:parkedEvents="parkedKeys"
:visible-lecturers="visibleLecturerUids"
@drop="dropHandler"
@resize="resizeHandler"
@update:date="handleChangeDate"
@update:mode="handleChangeMode"
@event-hover="onEventHover"
@event-unhover="onEventUnhover"
:extra-backgrounds="extraBackgrounds"
@update:range="handleRange"
class="responsive-calendar"
/>
</main>
<horizontal-split :defaultRatio="[15, 85]">
<template #left>
<tempus-sidebar-menu
ref="sidebar"
:preview-role="previewRole"
:rooms="rooms"
:studiengaenge="studiengaenge"
:studiengaenge-all="studiengaenge_all"
:lecturers="lecturers"
:selected-studiensemester="selectedStudiensemester"
:raumvorschlag-preview="raumvorschlagPreview"
:raumvorschlag-loading="raumvorschlagLoading"
@update:preview-role="handlePreviewRoleChange"
@update:rooms="rooms = $event"
@update:studiengaenge="studiengaenge = $event"
@update:parked-keys="parkedKeys = $event"
@update:selected-studiensemester="selectedStudiensemester = $event"
@remove-lecturer="removeLecturer"
@select-lecturer="addToFilter($event, 'mitarbeiter')"
@select-kw="jumpToKw"
@sync="triggerSync"
@preview-raumvorschlag="previewRaumvorschlag"
/>
</template>
<template #right>
<fhc-calendar
ref="calendar"
:timezone="config.timezone"
:get-promise-func="getPromiseFunc"
:visible-status="visibleStatus"
:date="calendarDate"
:mode="currentMode"
:parkedEvents="parkedKeys"
:visible-lecturers="visibleLecturerUids"
:show-events="showEvents"
@drop="dropHandler"
@resize="resizeHandler"
@update:date="handleChangeDate"
@update:mode="handleChangeMode"
@event-hover="onEventHover"
@event-unhover="onEventUnhover"
@open-reservierung="openReservierung"
:extra-backgrounds="extraBackgrounds"
@update:range="handleRange"
class="responsive-calendar"
/>
</template>
</horizontal-split>
</div>
</div>
<app-config ref="config" v-model="appconfig" :endpoints="configEndpoints"></app-config>
<tempus-verband-menu
ref="verbandMenu"
:endpoint="endpoint"
@select-verband-and-close="onSelectVerbandAndClose"
/>
<raumauswahl-modal ref="raumModal" @saved="$refs.calendar.resetEventLoader()"/>
+243
View File
@@ -0,0 +1,243 @@
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';
import FhcLoader from '../../components/Loader.js';
export default {
props: {
config: Object,
defaultSemester: String
},
components: {
CoreFilterCmpt,
CoreBaseLayout,
CoreNavigationCmpt,
FormInput,
TempusSyncModal,
FhcLoader
},
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'},
{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-3';
const editButton = document.createElement('button');
editButton.className = 'btn btn-outline-secondary';
editButton.innerHTML = '<i class="fa fa-edit"></i>';
editButton.addEventListener('click', () => {
this.$refs.syncModal.openEdit(cell.getRow().getData());
});
container.append(editButton);
const startButton = document.createElement('button');
startButton.className = 'btn btn-outline-secondary';
startButton.innerHTML = '<i class="fa fa-play"></i>';
startButton.addEventListener('click', () => {
startButton.disabled = true;
this.$refs.loader.show();
this.$refs.syncModal
.start(cell.getRow().getData())
.then(() => {
startButton.disabled = false;
this.$refs.loader.hide();
})
.catch(() => {
this.$refs.loader.hide();
startButton.disabled = false;
});
});
container.append(startButton);
const deleteButton = document.createElement('button');
deleteButton.className = 'btn btn-outline-secondary';
deleteButton.innerHTML = '<i class="fa fa-trash"></i>';
deleteButton.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(deleteButton);
return container;
}
}
],
}
}
},
methods: {
openNewModal()
{
this.$refs.syncModal.openNew();
},
reloadTable()
{
this.$refs.syncTable.reloadTable();
},
openStartModal()
{
this.$refs.syncModal.openStart();
},
onSaved(studiensemester_kurzbz)
{
if (studiensemester_kurzbz && studiensemester_kurzbz !== this.studiensemester_kurzbz)
{
this.studiensemester_kurzbz = studiensemester_kurzbz;
}
else
{
this.reloadTable();
}
}
},
template: `
<core-navigation-cmpt></core-navigation-cmpt>
<core-base-layout>
<template #main>
<fhc-loader ref="loader" :timeout="0"></fhc-loader>
<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 value="all">{{ $p.t('ui', 'alleAnzeigen') }}</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="onSaved"
>
</tempus-sync-modal>
</template>
</core-base-layout>
`
};
@@ -5,12 +5,6 @@ export default {
components: {
BaseTreemenu,
},
props: {
endpoint: {
type: Object,
required: true,
},
},
emits: ['select-verband-and-close'],
methods: {
onSelectVerbandAndClose(payload) {
+4
View File
@@ -106,6 +106,10 @@ require_once('dbupdate_3.4/75959_StudVw_Automatische_Tags.php');
require_once('dbupdate_3.4/76160_lvv_favorites.php');
require_once('dbupdate_3.4/77080_tabulator_presets_table.php');
require_once('dbupdate_3.4/78292_gehaltsanpassungtyp.php');
require_once('dbupdate_3.4/77949_tempus_synchronisation.php');
require_once('dbupdate_3.4/78287_tempus_raumzuordnung.php');
require_once('dbupdate_3.4/78285_tempus_stundenraster.php');
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
+29
View File
@@ -249,3 +249,32 @@ if ($result = @$db->db_query("SELECT 1 FROM public.tbl_variablenname WHERE name
echo 'public.tbl_variablenname: Added name "tempus_favorites"<br>';
}
}
if(!$result = @$db->db_query("SELECT status_kurzbz_max FROM lehre.tbl_kalender LIMIT 1"))
{
$qry = "ALTER TABLE lehre.tbl_kalender ADD COLUMN status_kurzbz_max character varying(32);
UPDATE lehre.tbl_kalender_status
SET status_kurzbz = 'to_delete',
bezeichnung = 'to_delete'
WHERE status_kurzbz = 'todelete';
UPDATE lehre.tbl_kalender_status SET sort = 9 WHERE status_kurzbz = 'deleted';
UPDATE lehre.tbl_kalender_status SET sort = 10 WHERE status_kurzbz = 'archived';
INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung, bezeichnung_mehrsprachig, sort) VALUES (E'to_delete_preview', E'to_delete_preview', E'{\"Zur Löschung in Voransicht\", \"Marked for Deletion in Preview\"}', 7);
INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung, bezeichnung_mehrsprachig, sort) VALUES (E'to_delete_live', E'to_delete_live', E'{\"Zur Löschung in Live\", \"Marked for Deletion in Live\"}', 8);
ALTER TABLE lehre.tbl_kalender ADD CONSTRAINT tbl_kalender_status_max_fk FOREIGN KEY (status_kurzbz_max)
REFERENCES lehre.tbl_kalender_status (status_kurzbz) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE;
";
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_kalender: '.$db->db_last_error().'</strong><br>';
else
echo '<br />lehre.tbl_kalender: Spalte status_kurzbz_max hinzugefuegt.
<br />lehre.tbl_kalender_status: status_kurzbz von todelete auf to_delete umbenannt.
<br />lehre.tbl_kalender_status: status_kurzbz to_delete_preview hinzugefuegt.
<br />lehre.tbl_kalender_status: status_kurzbz to_delete_live hinzugefuegt.
';
}
@@ -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>';
}
@@ -0,0 +1,16 @@
<?php
if (! defined('DB_NAME')) exit('No direct script access allowed');
if($result = $db->db_query("SELECT * FROM system.tbl_berechtigung WHERE berechtigung_kurzbz='basis/tempus_stundenraster'"))
{
if($db->db_num_rows($result)==0)
{
$qry = "INSERT INTO system.tbl_berechtigung(berechtigung_kurzbz, beschreibung)
VALUES('basis/tempus_stundenraster','Berechtigung, um den Stundenraster zu aktivieren/deaktivieren');";
if(!$db->db_query($qry))
echo '<strong>Berechtigung: '.$db->db_last_error().'</strong><br>';
else
echo 'Neue Berechtigung basis/tempus_stundenraster hinzugefuegt!<br>';
}
}
@@ -0,0 +1,17 @@
<?php
if (! defined('DB_NAME')) exit('No direct script access allowed');
if ($result = @$db->db_query("SELECT 1 FROM public.tbl_variablenname WHERE name = 'roomless_planning';"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "INSERT INTO public.tbl_variablenname(name, defaultwert) VALUES('roomless_planning', false);
INSERT INTO public.tbl_variablenname(name, defaultwert) VALUES('priority_room_planning', false);
INSERT INTO public.tbl_variablenname(name, defaultwert) VALUES('dialog_room_planning', false);";
if (!$db->db_query($qry))
echo '<strong>public.tbl_variablenname '.$db->db_last_error().'</strong><br>';
else
echo 'public.tbl_variablenname: Added "roomless_planning", "priority_room_planning", "dialog_room_planning"<br>';
}
}
+220
View File
@@ -3363,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(
'app' => 'core',
'category' => 'lehre',
@@ -37385,6 +37505,86 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'roomless_planning_error',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Raumloseplanung deaktiviert',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Roomless planning is disabled.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'roomless_planning',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Raumlos planen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Plan without room',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'priority_room_planning',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => ' Prioritätsraum verwenden',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Use priority room',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'dialog_room_planning',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Raumauswahl anzeigen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Show room selection',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'softwarebereitstellung',
'category' => 'ui',
@@ -39128,6 +39328,26 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'successStart',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => '"Erfolgreich gestartet',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Started successfully',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',