Merge branch 'master' into feature-70376/Lohnguide

This commit is contained in:
Harald Bamberger
2026-04-22 18:46:57 +02:00
69 changed files with 8887 additions and 1283 deletions
+8 -1
View File
@@ -208,7 +208,14 @@ $config['navigation_header'] = array(
'expand' => true, 'expand' => true,
'sort' => 30, 'sort' => 30,
'requiredPermissions' => 'lehre/anrechnungszeitfenster:rw' 'requiredPermissions' => 'lehre/anrechnungszeitfenster:rw'
) ),
'dashboardadmin' => array(
'link' => site_url('dashboard/Admin'),
'description' => 'Dashboard Admin',
'expand' => true,
'sort' => 40,
'requiredPermissions' => 'dashboard/admin:r'
)
) )
) )
) )
@@ -0,0 +1,121 @@
<?php
/**
* 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/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
* This controller operates between (interface) the JS (GUI) and the back-end
* Provides data to the ajax get calls about addresses
* This controller works with JSON calls on the HTTP GET or POST and the output is always JSON
*/
class Board extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'list' => 'dashboard/admin:r',
'create' => 'dashboard/admin:rw',
'update' => 'dashboard/admin:rw',
'delete' => 'dashboard/admin:rw'
]);
// Models
$this->load->model('dashboard/Dashboard_model', 'DashboardModel');
}
public function list()
{
$result = $this->DashboardModel->load();
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($result);
}
public function create()
{
$dashboard_kurzbz = $this->input->post('dashboard_kurzbz');
$result = $this->DashboardModel->insert([
'dashboard_kurzbz' => $dashboard_kurzbz
]);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function update()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('dashboard_id', 'Dashboard ID', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$dashboard_id = $this->input->post('dashboard_id');
$dashboard_kurzbz = $this->input->post('dashboard_kurzbz');
$beschreibung = $this->input->post('beschreibung');
$result = $this->DashboardModel->update([
'dashboard_id' => $dashboard_id
], [
'dashboard_kurzbz' => $dashboard_kurzbz,
'beschreibung' => $beschreibung
]);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($result);
}
public function delete()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('dashboard_id', 'Dashboard ID', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$dashboard_id = $this->input->post('dashboard_id');
//delete all presets
$this->load->model('dashboard/Dashboard_Preset_model', 'DashboardPresetModel');
$result = $this->DashboardPresetModel->delete([
'dashboard_id' => $dashboard_id
]);
$this->getDataOrTerminateWithError($result);
//delete all widgets
$this->load->model('dashboard/Dashboard_Widget_model', 'DashboardWidgetModel');
$result = $this->DashboardWidgetModel->delete([
'dashboard_id' => $dashboard_id
]);
$this->getDataOrTerminateWithError($result);
$result = $this->DashboardModel->delete($dashboard_id);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($result);
}
}
@@ -0,0 +1,200 @@
<?php
/**
* 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/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
* This controller operates between (interface) the JS (GUI) and the back-end
* Provides data to the ajax get calls about addresses
* This controller works with JSON calls on the HTTP GET or POST and the output is always JSON
*/
class Preset extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'list' => 'dashboard/admin:r',
'getBatch' => 'dashboard/admin:r',
'addWidget' => 'dashboard/admin:rw',
'removeWidget' => 'dashboard/admin:rw'
]);
// Load language phrases
$this->loadPhrases([
'ui'
]);
// Libraries
$this->load->library('dashboard/DashboardLib');
// Models
$this->load->model('ressource/Funktion_model', 'FunktionModel');
}
public function list($dashboard_kurzbz)
{
$sql = "
WITH
dashboard_presets AS (
SELECT
*
FROM
dashboard.tbl_dashboard_preset dp
JOIN
dashboard.tbl_dashboard d ON d.dashboard_id = dp.dashboard_id
WHERE
d.dashboard_kurzbz = {$this->db->escape($dashboard_kurzbz)}
),
general AS (
SELECT
'general' AS funktion_kurzbz,
'Allgemein' AS beschreibung
)
(
SELECT
f.funktion_kurzbz,
f.beschreibung,
COUNT(p.preset_id) AS has_preset
FROM
general f
LEFT JOIN
dashboard_presets p ON p.funktion_kurzbz IS NULL
GROUP BY
f.funktion_kurzbz, f.beschreibung
)
UNION ALL
(
SELECT
f.funktion_kurzbz,
f.beschreibung,
COUNT(p.preset_id) AS has_preset
FROM
public.tbl_funktion f
LEFT JOIN
dashboard_presets p ON p.funktion_kurzbz = f.funktion_kurzbz
GROUP BY
f.funktion_kurzbz, f.beschreibung
ORDER BY
f.beschreibung ASC
)
";
$result = $this->FunktionModel->execReadOnlyQuery($sql);
$funktionen = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($funktionen);
}
public function getBatch()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('db', 'Dashboard', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$db = $this->input->post('db');
$funktionen = $this->input->post('funktionen') ?: [];
$result = [];
foreach ($funktionen as $funktion) {
$conf = $this->dashboardlib->getPreset($db, $funktion);
if ($conf) {
$preset = json_decode($conf->preset, true);
if (!isset($preset[$funktion]) || !isset($preset[$funktion]['widgets']))
$result[$funktion] = [];
else
$result[$funktion] = $preset[$funktion]['widgets'];
} else {
$result[$funktion] = [];
}
}
return $this->terminateWithSuccess($result);
}
public function addWidget()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('dashboard', 'Dashboard', 'required');
$this->form_validation->set_rules('funktion_kurzbz', 'Funktion', 'required');
$this->form_validation->set_rules('widget[widget]', 'Widget', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$dashboard_kurzbz = $this->input->post('dashboard');
$funktion_kurzbz = $this->input->post('funktion_kurzbz');
$widget = $this->input->post('widget');
if (!isset($widget['widgetid']))
$widget['widgetid'] = $this->dashboardlib->generateWidgetId($dashboard_kurzbz);
$preset = $this->dashboardlib->getPresetOrCreateEmptyPreset($dashboard_kurzbz, $funktion_kurzbz);
$preset_decoded = json_decode($preset->preset, true);
$this->dashboardlib->addWidgetsToWidgets($preset_decoded, $dashboard_kurzbz, $funktion_kurzbz, [$widget]);
$preset->preset = json_encode($preset_decoded);
$result = $this->dashboardlib->insertOrUpdatePreset($preset);
$this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($widget['widgetid']);
}
public function removeWidget()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('db', 'Dashboard', 'required');
$this->form_validation->set_rules('funktion_kurzbz', 'Funktion', 'required');
$this->form_validation->set_rules('widgetid', 'Widget', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$dashboard_kurzbz = $this->input->post('db');
$funktion_kurzbz = $this->input->post('funktion_kurzbz');
$widgetid = $this->input->post('widgetid');
$preset = $this->dashboardlib->getPreset($dashboard_kurzbz, $funktion_kurzbz);
if (!$preset)
show_404();
$preset_decoded = json_decode($preset->preset, true);
if (!$this->dashboardlib->removeWidgetFromWidgets($preset_decoded, $funktion_kurzbz, $widgetid))
show_404();
$preset->preset = json_encode($preset_decoded);
$result = $this->dashboardlib->insertOrUpdatePreset($preset);
$this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(array('msg' => $this->p->t('dashboard', 'success_savePreset')));
}
}
@@ -0,0 +1,159 @@
<?php
/**
* 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/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
* This controller operates between (interface) the JS (GUI) and the back-end
* Provides data to the ajax get calls about the users dashboard
* This controller works with JSON calls on the HTTP GET or POST and the output is always JSON
*/
class User extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'get' => 'dashboard/benutzer:r',
'addWidget' => 'dashboard/benutzer:rw',
'removeWidget' => 'dashboard/benutzer:rw'
]);
// Libraries
$this->load->library('dashboard/DashboardLib');
// Models
$this->load->model('ressource/Funktion_model', 'FunktionModel');
}
public function get($dashboard_kurzbz)
{
$dashboard = $this->dashboardlib->getDashboardByKurzbz($dashboard_kurzbz);
if (!$dashboard)
show_404();
$uid = $this->authlib->getAuthObj()->username;
/*$mergedconfig = $this->dashboardlib->getMergedConfig($dashboard->dashboard_id, $uid);
$this->terminateWithSuccess([
'general' => call_user_func_array(
'array_merge_recursive',
$mergedconfig
)
]);*/
$defaultconfig = $this->dashboardlib->getDefaultConfig($dashboard->dashboard_id);
$userconfig = $this->dashboardlib->getUserConfig($dashboard->dashboard_id, $uid);
$defaultconfig_squashed = $defaultconfig ? call_user_func_array('array_replace_recursive', $defaultconfig) : [];
$userconfig_squashed = $userconfig ? call_user_func_array('array_replace_recursive', $userconfig) : [];
$mergedconfig = array_replace_recursive($defaultconfig_squashed, $userconfig_squashed);
$this->terminateWithSuccess([
DashboardLib::SECTION_IF_FUNKTION_KURZBZ_IS_NULL => $mergedconfig
]);
}
public function addWidget()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('dashboard', 'Dashboard', 'required');
$this->form_validation->set_rules('widget[widget]', 'Widget', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$widget = $this->input->post('widget');
$dashboard_kurzbz = $this->input->post('dashboard');
$uid = $this->authlib->getAuthObj()->username;
if (!isset($widget['widgetid']))
$widget['widgetid'] = $this->dashboardlib->generateWidgetId($dashboard_kurzbz);
$override = $this->dashboardlib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid);
$override_decoded = json_decode($override->override, true);
if (!isset($override_decoded['general']) || !is_array($override_decoded['general']))
$override_decoded['general'] = [];
if (!isset($override_decoded['general']['widgets']))
$override_decoded['general']['widgets'] = [];
$override_decoded['general']['widgets'][$widget['widgetid']] = $widget;
// NOTE(chris): remove doubles in other funktionen
foreach ($override_decoded as $funktion => $array) {
if ($funktion == 'general')
continue;
if (isset($array['widgets']) && isset($array['widgets'][$widget['widgetid']]))
unset($override_decoded[$funktion]['widgets'][$widget['widgetid']]);
}
$override->override = json_encode($override_decoded);
$result = $this->dashboardlib->insertOrUpdateOverride($override);
$this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($widget['widgetid']);
}
public function removeWidget()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('dashboard', 'Dashboard', 'required');
$this->form_validation->set_rules('widget', 'Widget', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$widget_id = $this->input->post('widget');
$dashboard_kurzbz = $this->input->post('dashboard');
$uid = $this->authlib->getAuthObj()->username;
$override = $this->dashboardlib->getOverride($dashboard_kurzbz, $uid);
if (!$override)
show_404();
$override_decoded = json_decode($override->override, true);
foreach (array_keys($override_decoded) as $k) {
if (!isset($override_decoded[$k]["widgets"])) {
unset($override_decoded[$k]);
continue;
}
if (isset($override_decoded[$k]["widgets"][$widget_id])) {
unset($override_decoded[$k]["widgets"][$widget_id]);
}
if (!$override_decoded[$k]["widgets"]) {
unset($override_decoded[$k]);
}
}
$override->override = json_encode($override_decoded);
$result = $this->dashboardlib->insertOrUpdateOverride($override);
$this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess();
}
}
@@ -0,0 +1,137 @@
<?php
/**
* 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/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
* This controller operates between (interface) the JS (GUI) and the back-end
* Provides data to the ajax get calls about the users dashboard
* This controller works with JSON calls on the HTTP GET or POST and the output is always JSON
*/
class Widget extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'get' => ['dashboard/benutzer:r', 'dashboard/admin:r'],
'list' => 'dashboard/admin:r',
'listAllowed' => ['dashboard/benutzer:rw', 'dashboard/admin:r'],
'setAllowed' => 'dashboard/admin:rw'
]);
// Libraries
$this->load->library('dashboard/DashboardLib');
// Models
$this->load->model('dashboard/Widget_model', 'WidgetModel');
}
public function get($id)
{
$result = $this->WidgetModel->load($id);
$widget = $this->getDataOrTerminateWithError($result);
if (!$widget)
return $this->terminateWithSuccess([
"widget_id" => 0,
"widget_kurzbz" => "notfound",
"arguments" => [
"className" => 'alert-danger',
"title" => 'Widget Not Found',
"msg" => 'The widget with the id ' . $id . ' could not be found'
],
"setup" => [
"name" => 'Widget Not Found',
"file" => absoluteJsImportUrl('public/js/components/DashboardWidget/Default.js'),
"width" => 1,
"height" => 1
]
]);
$widget = current($widget);
$widget->arguments = json_decode($widget->arguments);
$tmpsetup = json_decode($widget->setup);
$tmpsetup->file = absoluteJsImportUrl($tmpsetup->file);
$widget->setup = $tmpsetup;
$this->terminateWithSuccess($widget);
}
public function list($dashboard)
{
$result = $this->WidgetModel->getWithAllowedForDashboard($dashboard);
$widgets = $this->getDataOrTerminateWithError($result);
$widgets = array_map(function ($widget) {
$widget->arguments = json_decode($widget->arguments);
$tmpsetup = json_decode($widget->setup);
$tmpsetup->file = absoluteJsImportUrl($tmpsetup->file);
$widget->setup = $tmpsetup;
return $widget;
}, $widgets);
$this->terminateWithSuccess($widgets);
}
public function listAllowed($dashboard)
{
$result = $this->WidgetModel->getForDashboard($dashboard);
$widgets = $this->getDataOrTerminateWithError($result);
$widgets = array_map(function ($widget) {
$widget->arguments = json_decode($widget->arguments);
$tmpsetup = json_decode($widget->setup);
$tmpsetup->file = absoluteJsImportUrl($tmpsetup->file);
$widget->setup = $tmpsetup;
return $widget;
}, $widgets);
$this->terminateWithSuccess($widgets);
}
public function setAllowed()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('dashboard_id', 'Dashboard', 'required');
$this->form_validation->set_rules('widget_id', 'Widget', 'required');
$this->form_validation->set_rules('allowed', 'Allowed', 'is_bool');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$data = [
'dashboard_id' => $this->input->post('dashboard_id'),
'widget_id' => $this->input->post('widget_id')
];
$this->load->model('dashboard/Dashboard_Widget_model', 'DashboardWidgetModel');
if ($this->input->post('allowed'))
$result = $this->DashboardWidgetModel->insert($data);
else
$result = $this->DashboardWidgetModel->delete($data);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
}
@@ -42,14 +42,22 @@ class Messages extends FHCAPI_Controller
]); ]);
} }
public function getMessages($id, $type_id, $size, $page) public function getMessages($id, $type_id, $size=null, $page=null)
{ {
if($type_id != 'person_id'){ if($type_id != 'person_id'){
$id = $this->_getPersonId($id, $type_id); $id = $this->_getPersonId($id, $type_id);
} }
$offset = $size * ($page - 1); if(!(is_null($size) && is_null($page)))
$limit = $size; {
$offset = $size * ($page - 1);
$limit = $size;
}
else
{
$offset = null;
$limit = null;
}
$result = $this->MessageModel->getMessagesForTable($id, $offset, $limit); $result = $this->MessageModel->getMessagesForTable($id, $offset, $limit);
@@ -626,7 +626,7 @@ class Students extends FHCAPI_Controller
$this->addFilter($studiensemester_kurzbz); $this->addFilter($studiensemester_kurzbz);
$result = $this->PrestudentModel->loadWhere($where); $result = $this->PrestudentModel->loadWhere($where);
$data = $this->getDataOrTerminateWithError($result); $data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data); $this->terminateWithSuccess($data);
@@ -851,40 +851,44 @@ class Students extends FHCAPI_Controller
$stdsemEsc = $studiensemester_kurzbz ? $this->PrestudentModel->escape($studiensemester_kurzbz) : 'NULL'; $stdsemEsc = $studiensemester_kurzbz ? $this->PrestudentModel->escape($studiensemester_kurzbz) : 'NULL';
$this->load->config('stv'); $this->load->config('stv');
$tags = $this->config->item('stv_prestudent_tags');
$whereTags = ''; if(defined('STV_TAGS_ENABLED') && STV_TAGS_ENABLED)
if (is_array($tags) && !isEmptyArray($tags)) { {
$tags = array_keys($tags); $tags = $this->config->item('stv_prestudent_tags');
foreach ($tags as $key => $tag) { $whereTags = '';
$tags[$key] = $this->db->escape($tag); if (is_array($tags) && !isEmptyArray($tags)) {
$tags = array_keys($tags);
foreach ($tags as $key => $tag) {
$tags[$key] = $this->db->escape($tag);
}
$whereTags = " AND nt.typ_kurzbz IN (" . implode(",", $tags) . ")";
} }
$whereTags = " AND nt.typ_kurzbz IN (" . implode(",", $tags) . ")"; $subQueryTag = "
(
SELECT
tag.prestudent_id,
COALESCE(json_agg(tag ORDER BY tag.done), '[]'::json) AS tags
FROM (
SELECT DISTINCT ON (n.notiz_id)
n.notiz_id AS id,
nt.typ_kurzbz,
array_to_json(nt.bezeichnung_mehrsprachig)->>0 AS beschreibung,
n.text AS notiz,
nt.style,
n.erledigt AS done,
nz.prestudent_id
FROM public.tbl_notizzuordnung AS nz
JOIN public.tbl_notiz AS n ON nz.notiz_id = n.notiz_id
JOIN public.tbl_notiz_typ AS nt ON n.typ = nt.typ_kurzbz "
. $whereTags .
"
) AS tag
GROUP BY tag.prestudent_id
) AS tag_data_agg
";
} }
$subQueryTag = "
(
SELECT
tag.prestudent_id,
COALESCE(json_agg(tag ORDER BY tag.done), '[]'::json) AS tags
FROM (
SELECT DISTINCT ON (n.notiz_id)
n.notiz_id AS id,
nt.typ_kurzbz,
array_to_json(nt.bezeichnung_mehrsprachig)->>0 AS beschreibung,
n.text AS notiz,
nt.style,
n.erledigt AS done,
nz.prestudent_id
FROM public.tbl_notizzuordnung AS nz
JOIN public.tbl_notiz AS n ON nz.notiz_id = n.notiz_id
JOIN public.tbl_notiz_typ AS nt ON n.typ = nt.typ_kurzbz "
. $whereTags .
"
) AS tag
GROUP BY tag.prestudent_id
) AS tag_data_agg
";
$this->PrestudentModel->addJoin('public.tbl_studiengang stg', 'studiengang_kz', 'LEFT'); $this->PrestudentModel->addJoin('public.tbl_studiengang stg', 'studiengang_kz', 'LEFT');
$this->PrestudentModel->addJoin('public.tbl_person p', 'person_id'); $this->PrestudentModel->addJoin('public.tbl_person p', 'person_id');
@@ -907,11 +911,17 @@ class Students extends FHCAPI_Controller
AND ps.studiensemester_kurzbz=public.get_stdsem_prestudent(tbl_prestudent.prestudent_id, ' . $stdsemEsc . ') AND ps.studiensemester_kurzbz=public.get_stdsem_prestudent(tbl_prestudent.prestudent_id, ' . $stdsemEsc . ')
AND ps.ausbildungssemester=public.get_absem_prestudent(tbl_prestudent.prestudent_id, ' . $stdsemEsc . ')', 'LEFT'); AND ps.ausbildungssemester=public.get_absem_prestudent(tbl_prestudent.prestudent_id, ' . $stdsemEsc . ')', 'LEFT');
$this->PrestudentModel->addJoin($subQueryTag, 'tag_data_agg.prestudent_id = tbl_prestudent.prestudent_id', 'LEFT'); if(defined('STV_TAGS_ENABLED') && STV_TAGS_ENABLED)
{
$this->PrestudentModel->addJoin($subQueryTag, 'tag_data_agg.prestudent_id = tbl_prestudent.prestudent_id', 'LEFT');
}
$this->PrestudentModel->addSelect("b.uid"); $this->PrestudentModel->addSelect("b.uid");
$this->PrestudentModel->addSelect('tag_data_agg.tags'); if(defined('STV_TAGS_ENABLED') && STV_TAGS_ENABLED)
{
$this->PrestudentModel->addSelect('tag_data_agg.tags');
}
$this->PrestudentModel->addSelect('titelpre'); $this->PrestudentModel->addSelect('titelpre');
$this->PrestudentModel->addSelect('nachname'); $this->PrestudentModel->addSelect('nachname');
$this->PrestudentModel->addSelect('vorname'); $this->PrestudentModel->addSelect('vorname');
@@ -0,0 +1,52 @@
<?php
/**
* 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/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*/
class Admin extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
// Set required permissions
parent::__construct(
array(
'index' => 'dashboard/admin:rw',
'preview' => 'dashboard/admin:r',
)
);
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
public function index()
{
$this->load->view('dashboard/admin.php', []);
}
public function preview($dashboard_kurzbz = 'CIS')
{
$this->load->view('dashboard/preview.php', [
'dashboard_kurzbz' => $dashboard_kurzbz
]);
}
}
-76
View File
@@ -1,76 +0,0 @@
<?php
defined('BASEPATH') || exit('No direct script access allowed');
class Api extends Auth_Controller
{
public function __construct()
{
parent::__construct(
array(
'index' => 'dashboard/admin:rw',
'getNews' => 'dashboard/benutzer:r',
'getAmpeln' => 'dashboard/benutzer:r',
)
);
$this->load->library('AuthLib', null, 'AuthLib');
$this->_setAuthUID();
}
public function index()
{
echo 'Dashboard API Controller';
}
/**
* Get News.
*/
public function getNews()
{
$limit = $this->input->get('limit');
$this->load->model('content/News_model', 'NewsModel');
$result = $this->NewsModel->getAll($limit);
if (hasData($result))
{
$this->outputJson(getData($result), REST_Controller::HTTP_OK);
}
else
{
$this->terminateWithJsonError('fehler entdeckt');
}
}
/**
* Get Ampeln.
*/
public function getAmpeln()
{
$this->load->model('content/Ampel_model', 'AmpelModel');
$result = $this->AmpelModel->getByUser($this->_uid);
if (hasData($result))
{
$this->outputJson(getData($result), REST_Controller::HTTP_OK);
}
else
{
$this->terminateWithJsonError('fehler entdeckt');
}
}
/**
* Retrieve the UID of the logged user and checks if it is valid
*/
private function _setAuthUID()
{
$this->_uid = getAuthUID();
if (!$this->_uid) show_error('User authentification failed');
}
}
@@ -1,216 +0,0 @@
<?php
defined('BASEPATH') || exit('No direct script access allowed');
/**
* Description of Config
*
* @author bambi
*/
class Config extends Auth_Controller
{
public function __construct()
{
parent::__construct(
array(
'index' => 'dashboard/benutzer:r',
'dummy' => 'dashboard/benutzer:r',
'genWidgetId' => 'dashboard/benutzer:rw',
'addWidgetsToPreset' => 'dashboard/admin:rw',
'removeWidgetFromPreset' => 'dashboard/admin:rw',
'addWidgetsToUserOverride' => 'dashboard/benutzer:rw',
'removeWidgetFromUserOverride' => 'dashboard/benutzer:rw',
'funktionen' => 'dashboard/admin:r',
'preset' => 'dashboard/admin:r',
'presetBatch' => 'dashboard/admin:r'
)
);
$this->load->library('dashboard/DashboardLib', null, 'DashboardLib');
$this->load->library('AuthLib', null, 'AuthLib');
$this->load->model('ressource/Funktion_model', 'FunktionModel');
}
public function index()
{
$dashboard_kurzbz = $this->input->get('db');
$uid = $this->AuthLib->getAuthObj()->username;
$dashboard = $this->DashboardLib->getDashboardByKurzbz($dashboard_kurzbz);
if(!$dashboard) {
http_response_code(404);
$this->terminateWithJsonError(array(
'error' => 'Dashboard ' . $dashboard_kurzbz . ' not found.'
));
}
$mergedconfig = $this->DashboardLib->getMergedConfig($dashboard->dashboard_id, $uid);
$this->outputJsonSuccess($mergedconfig);
}
public function genWidgetId()
{
$dashboard_kurzbz = $this->input->get('db');
$widgetid = $this->DashboardLib->generateWidgetId($dashboard_kurzbz);
$this->outputJsonSuccess(array(
'widgetid' => $widgetid
));
}
public function addWidgetsToPreset()
{
$input = json_decode($this->input->raw_input_stream);
$dashboard_kurzbz = $input->db;
$funktion_kurzbz = $input->funktion_kurzbz;
$preset = $this->DashboardLib->getPresetOrCreateEmptyPreset($dashboard_kurzbz, $funktion_kurzbz);
$preset_decoded = json_decode($preset->preset, true);
$this->DashboardLib->addWidgetsToWidgets($preset_decoded, $dashboard_kurzbz, $funktion_kurzbz, $input->widgets);
$preset->preset = json_encode($preset_decoded);
$result = $this->DashboardLib->insertOrUpdatePreset($preset);
if (isError($result)) {
http_response_code(500);
$this->terminateWithJsonError('preset could not be saved');
}
$this->outputJsonSuccess(array('msg' => 'preset successfully stored.', 'data' => $preset_decoded));
}
public function removeWidgetFromPreset()
{
$input = json_decode($this->input->raw_input_stream);
$dashboard_kurzbz = $input->db;
$funktion_kurzbz = $input->funktion_kurzbz;
$widgetid = $input->widgetid;
$preset = $this->DashboardLib->getPreset($dashboard_kurzbz, $funktion_kurzbz);
if ($preset === null) {
http_response_code(404);
$this->terminateWithJsonError('preset for dashboard ' . $dashboard_kurzbz . ' and funktion ' . $funktion_kurzbz . ' not found.');
}
$preset_decoded = json_decode($preset->preset, true);
if (!$this->DashboardLib->removeWidgetFromWidgets($preset_decoded, $funktion_kurzbz, $widgetid))
{
http_response_code(404);
$this->terminateWithJsonError('widgetid ' . $widgetid . ' not found');
}
$preset->preset = json_encode($preset_decoded);
$result = $this->DashboardLib->insertOrUpdatePreset($preset);
if (isError($result))
{
http_response_code(500);
$this->terminateWithJsonError('failed to remove widget');
}
$this->outputJsonSuccess(array('msg' => 'preset successfully updated.'));
}
public function addWidgetsToUserOverride()
{
$input = json_decode($this->input->raw_input_stream);
$dashboard_kurzbz = $input->db;
$funktion_kurzbz = $input->funktion_kurzbz;
$uid = $this->AuthLib->getAuthObj()->username;
$override = $this->DashboardLib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid);
$override_decoded = json_decode($override->override, true);
$this->DashboardLib->addWidgetsToWidgets($override_decoded, $dashboard_kurzbz, $funktion_kurzbz, $input->widgets);
$override->override = json_encode($override_decoded);
$result = $this->DashboardLib->insertOrUpdateOverride($override);
if (isError($result)) {
http_response_code(500);
$this->terminateWithJsonError('override could not be saved');
}
$this->outputJsonSuccess(array('msg' => 'override successfully stored.', 'data' => $override_decoded));
}
public function removeWidgetFromUserOverride()
{
$input = json_decode($this->input->raw_input_stream);
$dashboard_kurzbz = $input->db;
$funktion_kurzbz = $input->funktion_kurzbz;
$uid = $this->AuthLib->getAuthObj()->username;
$widgetid = $input->widgetid;
$override = $this->DashboardLib->getOverride($dashboard_kurzbz, $uid);
if (empty($override)) {
http_response_code(404);
$this->terminateWithJsonError('userconfig for dashboard ' . $dashboard_kurzbz . ' not found.');
}
$override_decoded = json_decode($override->override, true);
if (!$this->DashboardLib->removeWidgetFromWidgets($override_decoded, $funktion_kurzbz, $widgetid))
{
http_response_code(404);
$this->terminateWithJsonError('widgetid ' . $widgetid . ' not found');
}
$override->override = json_encode($override_decoded);
$result = $this->DashboardLib->insertOrUpdateOverride($override, $uid);
if (isError($result))
{
http_response_code(500);
$this->terminateWithJsonError('failed to remove widget');
}
$this->outputJsonSuccess(array('msg' => 'override successfully updated.'));
}
public function funktionen()
{
$funktionen = $this->FunktionModel->load();
if (isError($funktionen)) {
http_response_code(404);
$this->terminateWithJsonError([
'error' => getError($funktionen)
]);
}
return $this->outputJsonSuccess(getData($funktionen) ?: []);
}
public function preset()
{
$db = $this->input->get('db');
$funktion = $this->input->get('funktion');
$conf = $this->DashboardLib->getPreset($db, $funktion);
if (!$conf)
return $this->outputJsonSuccess(['widgets' => [$funktion => []]]);
return $this->outputJsonSuccess(json_decode($conf->preset, true));
}
public function presetBatch()
{
$db = $this->input->get('db');
$funktionen = $this->input->get('funktionen');
$result = [];
foreach ($funktionen as $funktion) {
$conf = $this->DashboardLib->getPreset($db, $funktion);
if ($conf)
{
$preset = json_decode($conf->preset, true);
if (!isset($preset[$funktion]) || !isset($preset[$funktion]['widgets']))
$result[$funktion] = [];
else
$result[$funktion] = $preset[$funktion]['widgets'];
}
else
$result[$funktion] = [];
}
return $this->outputJsonSuccess($result);
}
}
@@ -1,86 +0,0 @@
<?php
defined('BASEPATH') || exit('No direct script access allowed');
/**
* Description of Widget
*
* @author chris
*/
class Dashboard extends Auth_Controller
{
public function __construct()
{
parent::__construct(
array(
'index' => 'dashboard/admin:r',
'create' => 'dashboard/admin:rw',
'update' => 'dashboard/admin:rw',
'delete' => 'dashboard/admin:rw'
)
);
$this->load->library('dashboard/DashboardLib', null, 'DashboardLib');
$this->load->model('dashboard/Dashboard_model', 'DashboardModel');
}
public function index()
{
$result = $this->DashboardModel->load();
if (isError($result)) {
http_response_code(404);
$this->terminateWithJsonError([
'error' => getError($result)
]);
}
return $this->outputJsonSuccess(getData($result) ?: []);
}
public function create()
{
$input = $this->getPostJSON();
$result = $this->DashboardModel->insert($input);
if (isError($result)) {
http_response_code(404);
$this->terminateWithJsonError([
'error' => getError($result)
]);
}
return $this->outputJsonSuccess(getData($result) ?: []);
}
public function update()
{
$input = $this->getPostJSON();
$result = $this->DashboardModel->update($input->dashboard_id, $input);
if (isError($result)) {
http_response_code(404);
$this->terminateWithJsonError([
'error' => getError($result)
]);
}
return $this->outputJsonSuccess(getData($result) ?: []);
}
public function delete()
{
$input = $this->getPostJSON();
$result = $this->DashboardModel->delete($input->dashboard_id);
if (isError($result)) {
http_response_code(404);
$this->terminateWithJsonError([
'error' => getError($result)
]);
}
return $this->outputJsonSuccess(getData($result) ?: []);
}
}
@@ -1,58 +0,0 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*/
class DashboardDemo extends Auth_Controller
{
private $_uid; // uid of the logged user
/**
* Constructor
*/
public function __construct()
{
// Set required permissions
parent::__construct(
array(
'index' => 'dashboard/benutzer:r',
'admin' => 'dashboard/admin:rw'
)
);
$this->load->library('AuthLib');
$this->load->library('WidgetLib');
$this->_setAuthUID(); // sets property uid
$this->setControllerId(); // sets the controller id
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
public function index()
{
$this->load->view('dashboard/dashboard_demo.php', []);
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
public function admin()
{
$this->load->view('dashboard/dashboard_demo_admin.php', []);
}
// -----------------------------------------------------------------------------------------------------------------
// Private methods
/**
* Retrieve the UID of the logged user and checks if it is valid
*/
private function _setAuthUID()
{
$this->_uid = getAuthUID();
if (!$this->_uid) show_error('User authentification failed');
}
}
@@ -1,134 +0,0 @@
<?php
defined('BASEPATH') || exit('No direct script access allowed');
/**
* Description of Widget
*
* @author chris
*/
class Widget extends Auth_Controller
{
public function __construct()
{
parent::__construct(
array(
'index' => ['dashboard/benutzer:r', 'dashboard/admin:r'],
'getAll' => 'dashboard/admin:r',
'getWidgetsForDashboard' => ['dashboard/benutzer:rw', 'dashboard/admin:r'],
'setAllowed' => 'dashboard/admin:rw'
)
);
$this->load->library('dashboard/DashboardLib', null, 'DashboardLib');
$this->load->model('dashboard/Widget_model', 'WidgetModel');
$this->load->model('dashboard/Dashboard_Widget_model', 'DashboardWidgetModel');
}
public function index()
{
$widget_id = $this->input->get('id');
$widget = $this->WidgetModel->load($widget_id);
if (isError($widget) || !getData($widget))
return $this->outputJsonSuccess([
"widget_id" => 0,
"widget_kurzbz" => "notfound",
"arguments" => [
"className" => 'alert-danger',
"title" => 'Widget Not Found',
"msg" => 'The widget with the id ' . $widget_id . ' could not be found'
],
"setup" => [
"name" => 'Widget Not Found',
"file" => absoluteJsImportUrl('public/js/components/DashboardWidget/Default.js'),
"width" => 1,
"height" => 1
]
]);
$widget = current(getData($widget));
$widget->arguments = json_decode($widget->arguments);
$tmpsetup = json_decode($widget->setup);
$tmpsetup->file = absoluteJsImportUrl($tmpsetup->file);
$widget->setup = $tmpsetup;
return $this->outputJsonSuccess($widget);
}
public function getAll()
{
$dashboard_id = $this->input->get('dashboard_id');
$result = $this->WidgetModel->getWithAllowedForDashboard($dashboard_id);
if (isError($result))
return $this->outputJsonError(getError($result));
$tmpwidgets = getData($result) ?: [];
$widgets = array_map(function($widget) {
$widget->arguments = json_decode($widget->arguments);
$tmpsetup = json_decode($widget->setup);
$tmpsetup->file = absoluteJsImportUrl($tmpsetup->file);
$widget->setup = $tmpsetup;
return $widget;
}, $tmpwidgets);
$this->outputJsonSuccess($widgets);
}
public function getWidgetsForDashboard()
{
$db = $this->input->get('db');
$result = $this->WidgetModel->getForDashboard($db);
if (isError($result)) {
http_response_code(404);
$this->terminateWithJsonError([
'error' => getError($result)
]);
}
$tmpwidgets = getData($result) ?: [];
$widgets = array_map(function($widget) {
$widget->arguments = json_decode($widget->arguments);
$tmpsetup = json_decode($widget->setup);
$tmpsetup->file = absoluteJsImportUrl($tmpsetup->file);
$widget->setup = $tmpsetup;
return $widget;
}, $tmpwidgets);
$this->outputJsonSuccess($widgets);
}
public function setAllowed()
{
$input = $this->getPostJSON();
$dashboard_id = $input->dashboard_id;
$widget_id = $input->widget_id;
$action = $input->action;
if ($action == 'add') {
$result = $this->DashboardWidgetModel->insert([
'dashboard_id' => $dashboard_id,
'widget_id' => $widget_id
]);
} elseif ($action == 'delete') {
$result = $this->DashboardWidgetModel->delete([
'dashboard_id' => $dashboard_id,
'widget_id' => $widget_id
]);
} else {
http_response_code(404); // TODO(chris): 400?
$this->terminateWithJsonError([
'error' => 'action value invalid'
]);
}
if (isError($result)) {
http_response_code(404);
$this->terminateWithJsonError([
'error' => getError($result)
]);
}
return $this->outputJsonSuccess(getData($result));
}
}
+1 -1
View File
@@ -128,7 +128,7 @@ class AntragLib
return $this->_ci->StudierendenantragstatusModel->resumeAntraegeForAbmeldungStgl($antrag_id); return $this->_ci->StudierendenantragstatusModel->resumeAntraegeForAbmeldungStgl($antrag_id);
} }
// NOTE(chris): get last status that is not pause // NOTE(chris): get last status that is not pause
$this->_ci->StudierendenantragstatusModel->addOrder('insertamum'); $this->_ci->StudierendenantragstatusModel->addOrder('insertamum', 'DESC');
$this->_ci->StudierendenantragstatusModel->addLimit(1); $this->_ci->StudierendenantragstatusModel->addLimit(1);
$result = $this->_ci->StudierendenantragstatusModel->loadWhere([ $result = $this->_ci->StudierendenantragstatusModel->loadWhere([
'studierendenantrag_id' => $antrag_id, 'studierendenantrag_id' => $antrag_id,
+16 -1
View File
@@ -50,6 +50,7 @@ class PermissionLib
const LOGINAS_PERSONIDS_BLACKLIST = 'permission_loginas_personids_blacklist'; const LOGINAS_PERSONIDS_BLACKLIST = 'permission_loginas_personids_blacklist';
private $_ci; // CI instance private $_ci; // CI instance
private $access_rights; // current users access rights
private static $bb; // benutzerberechtigung private static $bb; // benutzerberechtigung
/** /**
@@ -61,6 +62,8 @@ class PermissionLib
// Loads CI instance // Loads CI instance
$this->_ci =& get_instance(); $this->_ci =& get_instance();
$this->access_rights = null;
$this->_ci->config->load('permission'); // Loads permission configuration $this->_ci->config->load('permission'); // Loads permission configuration
// If it's NOT called from command line // If it's NOT called from command line
@@ -69,8 +72,10 @@ class PermissionLib
// API Caller rights initialization // API Caller rights initialization
$authObj = $this->_ci->authlib->getAuthObj(); $authObj = $this->_ci->authlib->getAuthObj();
self::$bb = new benutzerberechtigung(); self::$bb = new benutzerberechtigung();
if ($authObj) if ($authObj) {
self::$bb->getBerechtigungen($authObj->{AuthLib::AO_USERNAME}); self::$bb->getBerechtigungen($authObj->{AuthLib::AO_USERNAME});
$this->access_rights = self::$bb->berechtigungen;
}
} }
} }
@@ -340,6 +345,16 @@ class PermissionLib
} }
} }
/**
* Returns the access rights for the current user
*
* @return array|null
*/
public function getAccessRights()
{
return $this->access_rights;
}
//------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------
// Private methods // Private methods
@@ -49,7 +49,7 @@ class DashboardLib
public function getMergedConfig($dashboard_id, $uid) public function getMergedConfig($dashboard_id, $uid)
{ {
$defaultconfig = $this->getDefaultConfig($dashboard_id, $uid); $defaultconfig = $this->getDefaultConfig($dashboard_id);
$userconfig = $this->getUserConfig($dashboard_id, $uid); $userconfig = $this->getUserConfig($dashboard_id, $uid);
$mergedconfig = array_replace_recursive($defaultconfig, $userconfig); $mergedconfig = array_replace_recursive($defaultconfig, $userconfig);
@@ -57,14 +57,31 @@ class DashboardLib
return $mergedconfig; return $mergedconfig;
} }
public function getDefaultConfig($dashboard_id, $uid) public function getDefaultConfig($dashboard_id)
{ {
$res_presets = $this->_ci->DashboardPresetModel->getPresets($dashboard_id, $uid); $funktion_kurzbzs = [];
$rights = $this->_ci->permissionlib->getAccessRights();
if ($rights)
$funktion_kurzbzs = array_unique(array_map(function ($right) {
return $right->funktion_kurzbz;
}, $rights));
$this->_ci->DashboardPresetModel->db
->group_start()
->where_in('funktion_kurzbz', $funktion_kurzbzs)
->or_where('funktion_kurzbz IS NULL')
->group_end();
$this->_ci->DashboardPresetModel->addOrder('funktion_kurzbz', 'DESC');
$result = $this->_ci->DashboardPresetModel->loadWhere([
'dashboard_id' => $dashboard_id
]);
$defaultconfig = array(); $defaultconfig = array();
if (hasData($res_presets)) if (hasData($result))
{ {
$presets = getData($res_presets); $presets = getData($result);
foreach ($presets as $presetobj) foreach ($presets as $presetobj)
{ {
$preset = json_decode($presetobj->preset, true); $preset = json_decode($presetobj->preset, true);
@@ -137,8 +154,10 @@ class DashboardLib
$dashboard = $this->getDashboardByKurzbz($dashboard_kurzbz); $dashboard = $this->getDashboardByKurzbz($dashboard_kurzbz);
$funktion_kurzbz = ($section === self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL) ? null : $section; $funktion_kurzbz = ($section === self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL) ? null : $section;
$result = $this->_ci->DashboardPresetModel $result = $this->_ci->DashboardPresetModel->loadWhere([
->getPresetByDashboardAndFunktion($dashboard->dashboard_id, $funktion_kurzbz); 'dashboard_id' => $dashboard->dashboard_id,
'funktion_kurzbz' => $funktion_kurzbz
]);
if (hasData($result)) if (hasData($result))
{ {
@@ -195,11 +214,11 @@ class DashboardLib
{ {
foreach ($addwigets as $widget) foreach ($addwigets as $widget)
{ {
if(!isset($widget->widgetid)) if(!isset($widget['widgetid']))
{ {
$widget->widgetid = $this->generateWidgetId($dashboard_kurzbz); $widget['widgetid'] = $this->generateWidgetId($dashboard_kurzbz);
} }
$this->addWidgetToWidgets($widgets, $section, $widget, $widget->widgetid); $this->addWidgetToWidgets($widgets, $section, $widget, $widget['widgetid']);
} }
} }
@@ -11,57 +11,4 @@ class Dashboard_Preset_model extends DB_Model
$this->dbTable = 'dashboard.tbl_dashboard_preset'; $this->dbTable = 'dashboard.tbl_dashboard_preset';
$this->pk = 'preset_id'; $this->pk = 'preset_id';
} }
/**
* Get Presets of given uid.
* @param integer dashboard_id
* @param string $uid
* @return array
*/
public function getPresets($dashboard_id, $uid)
{
// TODO: get Funktionen for uid and load all preset for all funktionen for uid
//return $this->loadWhere(array('dashboard_id' => $dashboard_id, 'funktion_kurzbz'=> null));
$sql = <<<EOSQL
SELECT
*
FROM
dashboard.tbl_dashboard_preset
WHERE
dashboard_id = ?
AND (
funktion_kurzbz IN (
SELECT
DISTINCT funktion_kurzbz
FROM
public.tbl_benutzerfunktion
WHERE
uid = ?
AND
NOW()::date
BETWEEN
COALESCE(datum_von, '1970-01-01')
AND
COALESCE(datum_bis, '2170-12-31')
)
OR
funktion_kurzbz IS NULL
)
ORDER BY
funktion_kurzbz DESC
EOSQL;
return $this->execQuery($sql, array($dashboard_id, $uid));
}
/**
* Get Preset by Dashboard and Funktion
* @param integer dashboard_id
* @param string funktion_kurzbz
* @return array
*/
public function getPresetByDashboardAndFunktion($dashboard_id, $funktion_kurzbz)
{
return $this->loadWhere(array('dashboard_id' => $dashboard_id, 'funktion_kurzbz' => $funktion_kurzbz));
}
} }
@@ -402,14 +402,17 @@ class Lehrveranstaltung_model extends DB_Model
SELECT SELECT
vorname, nachname, mitarbeiter_uid, lehrfunktion_kurzbz vorname, nachname, mitarbeiter_uid, lehrfunktion_kurzbz
FROM FROM
lehre.tbl_lehreinheit lehre.tbl_lehreinheit le
JOIN lehre.tbl_lehreinheitmitarbeiter lema USING (lehreinheit_id) JOIN lehre.tbl_lehreinheitmitarbeiter lema USING (lehreinheit_id)
JOIN public.tbl_benutzer b ON b.uid = lema.mitarbeiter_uid JOIN public.tbl_benutzer b ON b.uid = lema.mitarbeiter_uid
JOIN public.tbl_person p using (person_id) JOIN public.tbl_person p using (person_id)
WHERE WHERE
tbl_lehreinheit.lehrveranstaltung_id= ? le.lehrveranstaltung_id= ?
AND tbl_lehreinheit.studiensemester_kurzbz = ? AND le.studiensemester_kurzbz = ?
AND lehrfunktion_kurzbz = 'LV-Leitung' AND lehrfunktion_kurzbz = 'LV-Leitung'
AND lema.mitarbeiter_uid NOT like '_Dummy%'
AND b.aktiv = TRUE
AND p.aktiv = TRUE
ORDER BY ORDER BY
lema.insertamum DESC lema.insertamum DESC
LIMIT 1 LIMIT 1
@@ -79,10 +79,10 @@ class Paabgabe_model extends DB_Model
JOIN public.tbl_benutzer ON (public.tbl_benutzer.uid = student_uid) JOIN public.tbl_benutzer ON (public.tbl_benutzer.uid = student_uid)
JOIN public.tbl_person USING (person_id) JOIN public.tbl_person USING (person_id)
WHERE (campus.tbl_paabgabe.insertamum >= NOW() - INTERVAL ? WHERE (campus.tbl_paabgabe.insertamum::date = CURRENT_DATE - INTERVAL ?
OR campus.tbl_paabgabe.updateamum >= NOW() - INTERVAL ?) OR campus.tbl_paabgabe.updateamum::date = CURRENT_DATE - INTERVAL ?)
AND campus.tbl_paabgabe.paabgabetyp_kurzbz IN ?"; AND campus.tbl_paabgabe.paabgabetyp_kurzbz IN ?";
return $this->execQuery($query, [$interval, $interval, $relevantTypes]); return $this->execQuery($query, [$interval, $interval, $relevantTypes]);
} }
@@ -108,7 +108,7 @@ class Paabgabe_model extends DB_Model
JOIN public.tbl_person ON (public.tbl_benutzer.person_id = public.tbl_person.person_id) JOIN public.tbl_person ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
WHERE campus.tbl_paabgabe.abgabedatum IS NOT NULL WHERE campus.tbl_paabgabe.abgabedatum IS NOT NULL
AND campus.tbl_paabgabe.abgabedatum >= NOW() - INTERVAL ?"; AND campus.tbl_paabgabe.abgabedatum = CURRENT_DATE - INTERVAL ?";
if($relevantTypes !== null) { if($relevantTypes !== null) {
$query .= " AND campus.tbl_paabgabe.paabgabetyp_kurzbz IN ?"; $query .= " AND campus.tbl_paabgabe.paabgabetyp_kurzbz IN ?";
@@ -594,7 +594,10 @@ class Studiengang_model extends DB_Model
$this->addSelect('p.prestudent_id'); $this->addSelect('p.prestudent_id');
$this->addSelect('pers.vorname'); $this->addSelect('pers.vorname');
$this->addSelect('pers.nachname'); $this->addSelect('pers.nachname');
$this->addSelect("CONCAT(UPPER(pers.nachname), ' ', pers.vorname, ' (', " . $this->dbTable . ".bezeichnung, ')') AS name"); $this->addSelect("CONCAT(UPPER(pers.nachname), ' ', pers.vorname, ' (', "
. $this->dbTable . ".bezeichnung, ', ', "
. "UPPER(" . $this->dbTable . ".typ), "
. "UPPER(" . $this->dbTable . ".kurzbz),')') AS name");
$this->addJoin('public.tbl_prestudent p', 'studiengang_kz'); $this->addJoin('public.tbl_prestudent p', 'studiengang_kz');
$this->addJoin( $this->addJoin(
@@ -261,6 +261,42 @@ class Benutzerfunktion_model extends DB_Model
} }
/**
* Get active Kompetenzfeldleitung bei UID.
*
* @param $uid
* @return array|stdClass|null
*/
public function getKFLByUID($uid)
{
$query = '
SELECT
bf.uid,
bf.oe_kurzbz,
oe.organisationseinheittyp_kurzbz
FROM
public.tbl_benutzerfunktion bf
JOIN public.tbl_organisationseinheit oe USING (oe_kurzbz)
JOIN public.tbl_benutzer b USING (uid)
WHERE
b.uid = ?
AND b.aktiv = TRUE
AND funktion_kurzbz = \'Leitung\'
AND organisationseinheittyp_kurzbz = \'Kompetenzfeld\'
AND (datum_von IS NULL OR datum_von <= now())
AND (datum_bis IS NULL OR datum_bis >= now())
';
$parameters_array = array();
if (is_string($uid))
{
$parameters_array[] = $uid;
}
return $this->execQuery($query, $parameters_array);
}
public function insertBenutzerfunktion($Json) public function insertBenutzerfunktion($Json)
{ {
unset($Json['benutzerfunktion_id']); unset($Json['benutzerfunktion_id']);
+6 -4
View File
@@ -242,6 +242,7 @@ class Message_model extends DB_Model
*/ */
public function getMessagesForTable($person_id, $offset, $limit) public function getMessagesForTable($person_id, $offset, $limit)
{ {
$limitoffset = (!is_null($offset) && !is_null($limit)) ? 'limit ? offset ?' : '';
$sql = <<<EOSQL $sql = <<<EOSQL
with filtered_messages as ( with filtered_messages as (
select select
@@ -310,11 +311,12 @@ class Message_model extends DB_Model
public.tbl_person pr on pr.person_id = fm.recipient_id public.tbl_person pr on pr.person_id = fm.recipient_id
order by order by
m.insertamum DESC m.insertamum DESC
limit ? {$limitoffset}
offset ?;
EOSQL; EOSQL;
$parametersArray = array($person_id, $person_id, $limit, $offset); $parametersArray = $limitoffset
? array($person_id, $person_id, $limit, $offset)
: array($person_id, $person_id);
$count = 0; $count = 0;
$data = $this->execQuery($sql, $parametersArray); $data = $this->execQuery($sql, $parametersArray);
@@ -325,7 +327,7 @@ EOSQL;
$data = getData($data); $data = getData($data);
if($data) if($data)
{ {
$count = ceil($data[0]->total_msgs / $limit); $count = is_null($limit) ? 1 : ceil($data[0]->total_msgs / $limit);
} }
return success(['data' => $data, 'count' => $count]); return success(['data' => $data, 'count' => $count]);
@@ -8,9 +8,15 @@ $this->load->view(
'axios027' => true, 'axios027' => true,
'restclient' => true, 'restclient' => true,
'vue3' => true, 'vue3' => true,
'customJSModules' => ['public/js/apps/Dashboard.js'], 'primevue3' => true,
'vuedatepicker11' => true,
'customJSs' => [
'vendor/moment/luxonjs/luxon.min.js'
],
'customJSModules' => ['public/js/apps/Dashboard/Admin.js'],
'customCSSs' => [ 'customCSSs' => [
'public/css/components/dashboard.css' 'public/css/components/dashboard.css',
'public/css/components/primevue.css',
], ],
'navigationcomponent' => true 'navigationcomponent' => true
) )
@@ -25,7 +31,7 @@ $this->load->view(
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Dashboard</h1> <h1 class="h2">Dashboard</h1>
</div> </div>
<core-dashboard dashboard="CIS" apiurl="<?= site_url('dashboard'); ?>"></core-dashboard> <dashboard-admin></dashboard-admin>
</div> </div>
</div> </div>
@@ -8,7 +8,12 @@ $this->load->view(
'axios027' => true, 'axios027' => true,
'restclient' => true, 'restclient' => true,
'vue3' => true, 'vue3' => true,
'customJSModules' => ['public/js/apps/DashboardAdmin.js'], 'vuedatepicker11' => true,
'primevue3' => true,
'customJSs' => [
'vendor/moment/luxonjs/luxon.min.js'
],
'customJSModules' => ['public/js/apps/Dashboard/Preview.js'],
'customCSSs' => [ 'customCSSs' => [
'public/css/components/dashboard.css' 'public/css/components/dashboard.css'
], ],
@@ -23,9 +28,9 @@ $this->load->view(
<div id="content"> <div id="content">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Dashboard</h1> <h1 class="h2">Dashboard <?= $dashboard_kurzbz ?></h1>
</div> </div>
<dashboard-admin dashboard="CIS" apiurl="<?= site_url('dashboard'); ?>"></dashboard-admin> <core-dashboard dashboard="<?= $dashboard_kurzbz ?>"></core-dashboard>
</div> </div>
</div> </div>
+6 -4
View File
@@ -46,12 +46,13 @@ echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<link rel="stylesheet" href="../../../skin/tablesort.css" type="text/css"/> <link rel="stylesheet" href="../../../skin/tablesort.css" type="text/css"/>
<link rel="stylesheet" href="../../../skin/style.css.php" type="text/css"> <link rel="stylesheet" href="../../../skin/style.css.php" type="text/css">
<link rel="stylesheet" type="text/css" href="../../../skin/jquery-ui-1.9.2.custom.min.css"> <link rel="stylesheet" type="text/css" href="../../../skin/jquery-ui-1.9.2.custom.min.css">
<script type="text/javascript" src="../../../vendor/jquery/jquery1/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="../../../vendor/christianbach/tablesorter/jquery.tablesorter.min.js"></script>
<script type="text/javascript" src="../../../vendor/components/jqueryui/jquery-ui.min.js"></script> <script type="text/javascript" src="../../../vendor/components/jqueryui/jquery-ui.min.js"></script>
<script type="text/javascript" src="../../../include/js/jquery.ui.datepicker.translation.js"></script> <script type="text/javascript" src="../../../include/js/jquery.ui.datepicker.translation.js"></script>
<script type="text/javascript" src="../../../vendor/jquery/sizzle/sizzle.js"></script>'; <script type="text/javascript" src="../../../vendor/jquery/sizzle/sizzle.js"></script>';
include('../../../include/meta/jquery.php');
include('../../../include/meta/jquery-tablesorter.php');
const MOODLE_ADDON_KURZBZ = 'moodle'; const MOODLE_ADDON_KURZBZ = 'moodle';
// Load Addons to get Moodle_Path // Load Addons to get Moodle_Path
@@ -71,7 +72,7 @@ echo '
$("#myTable").tablesorter( $("#myTable").tablesorter(
{ {
sortList: [[0,0],[1,0]], sortList: [[0,0],[1,0]],
widgets: [\'zebra\'] widgets: [\'zebra\',\'filter\']
}); });
} }
); );
@@ -151,8 +152,9 @@ foreach($service->result as $row)
$person = new person(); $person = new person();
$person->getPersonFromBenutzer($row->operativ_uid); $person->getPersonFromBenutzer($row->operativ_uid);
$operativ = $person->nachname.' '.$person->vorname; $operativ = $person->nachname.' '.$person->vorname;
$oeBez = new organisationseinheit($row->oe_kurzbz);
echo '<tr>'; echo '<tr>';
echo '<td>',$row->oe_kurzbz,'</td>'; echo '<td>',$oeBez->bezeichnung,'</td>';
echo '<td><b>'.$row->bezeichnung.'</b></td>'; echo '<td><b>'.$row->bezeichnung.'</b></td>';
echo '<td>',$row->beschreibung,'</td>'; echo '<td>',$row->beschreibung,'</td>';
echo '<td><nobr><a href="../profile/index.php?uid='.$row->design_uid.'">',$design,'</a></nobr></td>'; echo '<td><nobr><a href="../profile/index.php?uid='.$row->design_uid.'">',$design,'</a></nobr></td>';
+1 -1
View File
@@ -293,7 +293,7 @@ else if (isset($_SESSION['pruefling_id']))
} }
$lastsemester = $row->semester; $lastsemester = $row->semester;
echo '<table border="0" cellspacing="0" cellpadding="0" id="Gebiet" style="display: visible; border-collapse: separate; border-spacing: 0 3px;">'; echo '<table border="0" cellspacing="0" cellpadding="0" id="Gebiet" style="display: visible; border-collapse: separate; border-spacing: 0 3px; margin-top: 5px;">';
echo '<tr><td class="HeaderTesttool">'. ($row->semester == '1' ? $p->t('testtool/basisgebiete') : $p->t('testtool/quereinstiegsgebiete')).'</td></tr>'; echo '<tr><td class="HeaderTesttool">'. ($row->semester == '1' ? $p->t('testtool/basisgebiete') : $p->t('testtool/quereinstiegsgebiete')).'</td></tr>';
} }
@@ -342,6 +342,8 @@ echo '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>';
<vbox> <vbox>
<checkbox id="mitarbeiter-entwicklungsteam-detail-checkbox-neu" checked="true" hidden="true" /> <checkbox id="mitarbeiter-entwicklungsteam-detail-checkbox-neu" checked="true" hidden="true" />
<textbox id="mitarbeiter-entwicklungsteam-detail-textbox-studiengang" hidden="true" /> <textbox id="mitarbeiter-entwicklungsteam-detail-textbox-studiengang" hidden="true" />
<textbox id="mitarbeiter-entwicklungsteam-detail-entwicklungsteam_id" hidden="true" />
<groupbox id="mitarbeiter-entwicklungsteam-detail-groupbox" flex="1"> <groupbox id="mitarbeiter-entwicklungsteam-detail-groupbox" flex="1">
<caption label="Details" /> <caption label="Details" />
<grid id="mitarbeiter-entwicklungsteam-detail-grid" style="margin:4px;" flex="1"> <grid id="mitarbeiter-entwicklungsteam-detail-grid" style="margin:4px;" flex="1">
@@ -1708,6 +1708,7 @@ function MitarbeiterEntwicklungsteamSelect()
document.getElementById('mitarbeiter-entwicklungsteam-detail-textbox-studiengang').value=studiengang_kz; document.getElementById('mitarbeiter-entwicklungsteam-detail-textbox-studiengang').value=studiengang_kz;
document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-beginn').value=beginn; document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-beginn').value=beginn;
document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-ende').value=ende; document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-ende').value=ende;
document.getElementById('mitarbeiter-entwicklungsteam-detail-entwicklungsteam_id').value=entwicklungsteam_id;
MitarbeiterEntwicklungsteamDetailDisableFields(false); MitarbeiterEntwicklungsteamDetailDisableFields(false);
} }
@@ -1725,6 +1726,7 @@ function MitarbeiterEntwicklungsteamSpeichern()
studiengang_kz_old = document.getElementById('mitarbeiter-entwicklungsteam-detail-textbox-studiengang').value; studiengang_kz_old = document.getElementById('mitarbeiter-entwicklungsteam-detail-textbox-studiengang').value;
beginn = document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-beginn').value; beginn = document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-beginn').value;
ende = document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-ende').value; ende = document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-ende').value;
entwicklungsteam_id = document.getElementById('mitarbeiter-entwicklungsteam-detail-entwicklungsteam_id').value;
if(studiengang_kz=='') if(studiengang_kz=='')
{ {
@@ -802,6 +802,10 @@ echo '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>';
class="sortDirectionIndicator" class="sortDirectionIndicator"
sort="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#fgm" /> sort="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#fgm" />
<splitter class="tree-splitter"/> <splitter class="tree-splitter"/>
<treecol id="student-prestudent-tree-rolle-faktiv" label="F-Aktiv" flex="1" hidden="true" persist="hidden, width, ordinal"
class="sortDirectionIndicator"
sort="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#faktiv" />
<splitter class="tree-splitter"/>
</treecols> </treecols>
<template> <template>
@@ -828,6 +832,7 @@ echo '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>';
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/prestudentrolle/rdf#aktiv rdf:http://www.technikum-wien.at/prestudentrolle/rdf#stichtagsaktiv" label="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#updateamum"/> <treecell properties="Aktiv_rdf:http://www.technikum-wien.at/prestudentrolle/rdf#aktiv rdf:http://www.technikum-wien.at/prestudentrolle/rdf#stichtagsaktiv" label="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#updateamum"/>
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/prestudentrolle/rdf#aktiv rdf:http://www.technikum-wien.at/prestudentrolle/rdf#stichtagsaktiv" label="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#updatevon"/> <treecell properties="Aktiv_rdf:http://www.technikum-wien.at/prestudentrolle/rdf#aktiv rdf:http://www.technikum-wien.at/prestudentrolle/rdf#stichtagsaktiv" label="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#updatevon"/>
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/prestudentrolle/rdf#aktiv rdf:http://www.technikum-wien.at/prestudentrolle/rdf#stichtagsaktiv" label="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#fgm"/> <treecell properties="Aktiv_rdf:http://www.technikum-wien.at/prestudentrolle/rdf#aktiv rdf:http://www.technikum-wien.at/prestudentrolle/rdf#stichtagsaktiv" label="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#fgm"/>
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/prestudentrolle/rdf#aktiv rdf:http://www.technikum-wien.at/prestudentrolle/rdf#stichtagsaktiv" label="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#faktiv"/>
</treerow> </treerow>
</treeitem> </treeitem>
</treechildren> </treechildren>
+2
View File
@@ -3550,9 +3550,11 @@ function StudentZeugnisDokumentArchivieren()
case 'microcredentialzertifikat_1': case 'microcredentialzertifikat_1':
case 'microcredentialzertifikat_2': case 'microcredentialzertifikat_2':
case 'microcredentialzertifikat_3': case 'microcredentialzertifikat_3':
case 'microcredentialzertifikat_4':
case 'microcredential_1': case 'microcredential_1':
case 'microcredential_2': case 'microcredential_2':
case 'microcredential_3': case 'microcredential_3':
case 'microcredential_4':
xml = 'microcredential.xml.php'; xml = 'microcredential.xml.php';
break; break;
+3 -1
View File
@@ -364,9 +364,10 @@ class entwicklungsteam extends basis_db
$bismeldung_jahr = $datetime->format('Y'); $bismeldung_jahr = $datetime->format('Y');
//laden des Datensatzes //laden des Datensatzes
$qry = "SELECT * $qry = "SELECT tbl_entwicklungsteam.*, tbl_besqual.*, tbl_studiengang.studiengang_kz, tbl_studiengang.melderelevant
FROM bis.tbl_entwicklungsteam FROM bis.tbl_entwicklungsteam
JOIN bis.tbl_besqual USING(besqualcode) JOIN bis.tbl_besqual USING(besqualcode)
JOIN public.tbl_studiengang USING(studiengang_kz)
WHERE mitarbeiter_uid=".$this->db_add_param($mitarbeiter_uid)." WHERE mitarbeiter_uid=".$this->db_add_param($mitarbeiter_uid)."
AND (beginn is NULL OR beginn <= make_date(". $this->db_add_param($bismeldung_jahr). "::INTEGER, 12, 31)) AND (beginn is NULL OR beginn <= make_date(". $this->db_add_param($bismeldung_jahr). "::INTEGER, 12, 31))
AND (ende is NULL OR ende >= make_date(". $this->db_add_param($bismeldung_jahr). "::INTEGER, 1, 1))"; AND (ende is NULL OR ende >= make_date(". $this->db_add_param($bismeldung_jahr). "::INTEGER, 1, 1))";
@@ -394,6 +395,7 @@ class entwicklungsteam extends basis_db
$obj->insertvon = $row->insertvon; $obj->insertvon = $row->insertvon;
$obj->ext_id = $row->ext_id; $obj->ext_id = $row->ext_id;
$obj->besqual = $row->besqualbez; $obj->besqual = $row->besqualbez;
$obj->melderelevant = $this->db_parse_bool($row->melderelevant);
$this->result[] = $obj; $this->result[] = $obj;
} }
+164 -7
View File
@@ -552,9 +552,41 @@ class lehreinheitmitarbeiter extends basis_db
$beginn = new DateTime($beginn); $beginn = new DateTime($beginn);
$ende = new DateTime($ende); $ende = new DateTime($ende);
// get relevant Studiensemester
$studiensemester_kurzbz_arr = [];
$qry = '
SELECT
studiensemester_kurzbz
FROM
public.tbl_studiensemester
WHERE
start BETWEEN
'. $this->db_add_param($beginn->format('Y-m-d')). ' AND
'. $this->db_add_param($ende->format('Y-m-d'));
if ($this->db_query($qry))
{
while($row = $this->db_fetch_object())
{
$studiensemester_kurzbz_arr[] = $row->studiensemester_kurzbz;
}
}
else
{
$this->errormsg = 'Fehler bei der Datenbankabfrage';
return false;
}
$lehrgaengeDistr = $this->_getLehrgaengeForDistribution($studiensemester_kurzbz_arr);
if (!is_array($studiensemester_kurzbz_arr) || empty($studiensemester_kurzbz_arr)) return true;
$qry = ' $qry = '
WITH semester_sws_tbl AS ( WITH semester_sws_tbl AS (
SELECT DISTINCT lehreinheit_id, studiensemester_kurzbz, lema.semesterstunden, stg.studiengang_kz SELECT
DISTINCT lehreinheit_id, studiensemester_kurzbz, lema.semesterstunden,
stg.studiengang_kz, stg.melde_studiengang_kz, stg.lgartcode, stg.melderelevant
FROM lehre.tbl_lehreinheitmitarbeiter lema FROM lehre.tbl_lehreinheitmitarbeiter lema
JOIN lehre.tbl_lehreinheit USING (lehreinheit_id) JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id) JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id)
@@ -564,38 +596,104 @@ class lehreinheitmitarbeiter extends basis_db
JOIN public.tbl_studiengang stg ON stg.studiengang_kz = sto.studiengang_kz JOIN public.tbl_studiengang stg ON stg.studiengang_kz = sto.studiengang_kz
JOIN public.tbl_studiensemester ss USING (studiensemester_kurzbz) JOIN public.tbl_studiensemester ss USING (studiensemester_kurzbz)
WHERE mitarbeiter_uid = '. $this->db_add_param($uid). ' WHERE mitarbeiter_uid = '. $this->db_add_param($uid). '
AND ( AND ss.studiensemester_kurzbz IN ('.$this->implode4SQL($studiensemester_kurzbz_arr).')
ss.start BETWEEN
'. $this->db_add_param($beginn->format('Y-m-d')). ' AND
'. $this->db_add_param($ende->format('Y-m-d')). ')
-- nur lehre, die bisgemeldet wird -- nur lehre, die bisgemeldet wird
AND lema.bismelden AND lema.bismelden
AND stg.melderelevant
-- keine lehreinheiten ohne semesterstunden -- keine lehreinheiten ohne semesterstunden
AND lema.semesterstunden != 0 AND lema.semesterstunden != 0
) )
SELECT SELECT
studiengang_kz, studiengang_kz,
studiensemester_kurzbz, studiensemester_kurzbz,
melde_studiengang_kz,
lgartcode,
sum(semesterstunden) AS summe, sum(semesterstunden) AS summe,
round(sum(semesterstunden) / 15, 2) AS sws round(sum(semesterstunden) / 15, 2) AS sws
FROM FROM
semester_sws_tbl semester_sws_tbl
GROUP BY GROUP BY
studiengang_kz, studiengang_kz,
studiensemester_kurzbz studiensemester_kurzbz,
melde_studiengang_kz,
lgartcode
ORDER BY ORDER BY
studiengang_kz; studiengang_kz;
'; ';
if ($this->db_query($qry)) if ($this->db_query($qry))
{ {
$additionalLehrgaenge = [];
while($row = $this->db_fetch_object()) while($row = $this->db_fetch_object())
{ {
$obj = new StdClass(); $obj = new StdClass();
$obj->studiengang_kz = $row->studiengang_kz; $obj->studiengang_kz = $row->studiengang_kz;
$obj->studiensemester_kurzbz = $row->studiensemester_kurzbz; $obj->studiensemester_kurzbz = $row->studiensemester_kurzbz;
$obj->melde_studiengang_kz = $row->melde_studiengang_kz;
$obj->lgartcode = $row->lgartcode;
$obj->sws = $row->sws; $obj->sws = $row->sws;
if (isset($lehrgaengeDistr[$uid][$row->studiensemester_kurzbz]))
{
$lehrgaenge = $lehrgaengeDistr[$uid][$row->studiensemester_kurzbz];
foreach ($lehrgaenge as $lehreinheit_id => $lehrgangKzArr)
{
// wenn lehrgang gefunden, zusammenhängende Lehrgaenge holen und sws aufteilen
if (array_key_exists($row->studiengang_kz, $lehrgangKzArr))
{
foreach ($lehrgangKzArr as $studiengang_kz => $lehrgang)
{
// check: nur eine Studiengangsverknüpfung pro Mitarbeiter, Semester, und Referenzstudiengang
if (
$studiengang_kz == $row->studiengang_kz
|| isset(
$additionalLehrgaenge[$uid][$row->studiensemester_kurzbz][$row->studiengang_kz][$studiengang_kz]
)
) continue;
// Lehrgang erstellen
$lg = new StdClass();
$lg->mitarbeiter_uid = $uid;
$lg->melde_studiengang_kz = $lehrgang->melde_studiengang_kz;
$lg->lgartcode = $lehrgang->lgartcode;
$lg->studiengang_kz = $lehrgang->studiengang_kz;
$lg->studiensemester_kurzbz = $lehrgang->studiensemester_kurzbz;
$lg->summe = $row->summe;
$lg->sws = $row->sws;
// Lehrgang, der mit Ursprungsstudiengang aufgrund lehreinheit "verknüpft" ist, hinzufügen
$additionalLehrgaenge[$uid][$row->studiensemester_kurzbz][$row->studiengang_kz][$studiengang_kz] = $lg;
}
}
}
// ignorieren, wenn für den Studiengang keine verknüpften Lehrgaenge hat
if (isset($additionalLehrgaenge[$uid][$row->studiensemester_kurzbz][$row->studiengang_kz]))
{
$addLehrgaenge = $additionalLehrgaenge[$uid][$row->studiensemester_kurzbz][$row->studiengang_kz];
// sws Durchschnitt über alle verknuepften Lehrgaenge berechnet
$summeSws = $row->summe/(count($addLehrgaenge) + 1);
$sws = $row->sws/(count($addLehrgaenge) + 1);
// neue sws zuweisen
$obj->summe = $summeSws;
$obj->sws = $sws;
foreach ($addLehrgaenge as $conn_ws_studiengang_kz => $lehrgang)
{
// sws fuer jeden verknuepften Lehrgang zuweisen
$lehrgang->summe = $summeSws;
$lehrgang->sws = $sws;
// neue lehrgang sws hinzufuegen
$this->result [] = $lehrgang;
}
}
}
$this->result []= $obj; $this->result []= $obj;
} }
return true; return true;
@@ -655,4 +753,63 @@ class lehreinheitmitarbeiter extends basis_db
return false; return false;
} }
/**
* Get "connected" Lehrgaenge for equal sws distribution.
* @param $studiensemester_kurzbz_arr all semester for which Lehrgaenge should be retrieved
* @return object success or error
*/
private function _getLehrgaengeForDistribution($studiensemester_kurzbz_arr)
{
if (!is_array($studiensemester_kurzbz_arr) || empty($studiensemester_kurzbz_arr)) return [];
$qry = "
WITH gruppen AS (
SELECT
mitarbeiter_uid, lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, sem.start, sem.ende,
lehreinheitgruppe_id, studiengang_kz, melde_studiengang_kz, lgartcode
FROM
lehre.tbl_lehreinheitmitarbeiter lema
JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id)
JOIN lehre.tbl_lehreinheitgruppe legr USING (lehreinheit_id)
JOIN public.tbl_studiengang stg USING (studiengang_kz)
JOIN public.tbl_studiensemester sem USING (studiensemester_kurzbz)
WHERE
bismelden
AND stg.melderelevant
AND stg.typ = 'l'
AND le.studiensemester_kurzbz IN (".$this->implode4SQL($studiensemester_kurzbz_arr).")
)
SELECT
DISTINCT mitarbeiter_uid, studiensemester_kurzbz, lehreinheit_id, studiengang_kz, melde_studiengang_kz, lgartcode
FROM
gruppen gr
GROUP BY
mitarbeiter_uid, studiensemester_kurzbz, lehreinheit_id, studiengang_kz, melde_studiengang_kz, lgartcode
ORDER BY
mitarbeiter_uid, studiensemester_kurzbz, lehreinheit_id, studiengang_kz, melde_studiengang_kz, lgartcode";
$lehrgaengeDistributions = [];
if($this->db_query($qry))
{
while($row = $this->db_fetch_object())
{
// group by properties
$lehrgaengeDistributions
[$row->mitarbeiter_uid]
[$row->studiensemester_kurzbz]
[$row->lehreinheit_id]
[$row->studiengang_kz]
= $row;
}
}
else
{
$this->errormsg = 'Fehler bei der Datenbankabfrage';
return false;
}
return $lehrgaengeDistributions;
}
} }
+1
View File
@@ -706,6 +706,7 @@ class prestudent extends person
$rolle->bestaetigtam = $row->bestaetigtam; $rolle->bestaetigtam = $row->bestaetigtam;
$rolle->bestaetigtvon = $row->bestaetigtvon; $rolle->bestaetigtvon = $row->bestaetigtvon;
$rolle->fgm = $row->fgm; $rolle->fgm = $row->fgm;
$rolle->faktiv = $this->db_parse_bool($row->faktiv);
$rolle->anmerkung_status = $row->anmerkung; $rolle->anmerkung_status = $row->anmerkung;
$rolle->bewerbung_abgeschicktamum = $row->bewerbung_abgeschicktamum; $rolle->bewerbung_abgeschicktamum = $row->bewerbung_abgeschicktamum;
$rolle->rt_stufe = $row->rt_stufe; $rolle->rt_stufe = $row->rt_stufe;
+3
View File
@@ -193,3 +193,6 @@
word-break: break-word; word-break: break-word;
} }
.news-list-item p {
word-break: break-word;
}
+46
View File
@@ -0,0 +1,46 @@
/**
* 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 {
list() {
return {
method: 'get',
url: 'api/frontend/v1/dashboard/board/list'
};
},
add(params) {
return {
method: 'post',
url: 'api/frontend/v1/dashboard/board/create',
params
};
},
update(params) {
return {
method: 'post',
url: 'api/frontend/v1/dashboard/board/update',
params
};
},
delete(dashboard_id) {
return {
method: 'post',
url: 'api/frontend/v1/dashboard/board/delete',
params: { dashboard_id }
};
}
}
+47
View File
@@ -0,0 +1,47 @@
/**
* 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 {
list(dashboard_kurzbz) {
return {
method: 'get',
url: 'api/frontend/v1/dashboard/preset/list/'
+ encodeURIComponent(dashboard_kurzbz)
};
},
getBatch(params) {
return {
method: 'post',
url: 'api/frontend/v1/dashboard/preset/getBatch',
params
};
},
addWidget(params) {
return {
method: 'post',
url: 'api/frontend/v1/dashboard/preset/addWidget',
params
};
},
removeWidget(params) {
return {
method: 'post',
url: 'api/frontend/v1/dashboard/preset/removeWidget',
params
};
}
};
+45
View File
@@ -0,0 +1,45 @@
/**
* 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 {
get(dashboard) {
return {
method: 'get',
url: '/api/frontend/v1/dashboard/user/get/' + dashboard
};
},
addWidget(dashboard, widget) {
return {
method: 'post',
url: '/api/frontend/v1/dashboard/user/addWidget',
params: {
dashboard,
widget
}
};
},
removeWidget(dashboard, widget) {
return {
method: 'post',
url: '/api/frontend/v1/dashboard/user/removeWidget',
params: {
dashboard,
widget
}
};
}
};
+46
View File
@@ -0,0 +1,46 @@
/**
* 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 {
get(widget) {
return {
method: 'get',
url: '/api/frontend/v1/dashboard/widget/get/' + widget
};
},
list(dashboard) {
return {
method: 'get',
url: '/api/frontend/v1/dashboard/widget/list/' + dashboard
};
},
listAllowed(dashboard) {
return {
method: 'get',
url: '/api/frontend/v1/dashboard/widget/listAllowed/' + dashboard
};
},
setAllowed(dashboard_id, widget_id, allowed) {
return {
method: 'post',
url: '/api/frontend/v1/dashboard/widget/setAllowed',
params: {
dashboard_id, widget_id, allowed
}
};
}
};
+8 -5
View File
@@ -17,13 +17,16 @@
export default { export default {
getMessages(params) { getMessages(params) {
let url = 'api/frontend/v1/messages/messages/getMessages'
+ '/' + params.id
+ '/' + params.type;
if(params.size && params.page) {
url += '/' + params.size
+ '/' + params.page;
}
return { return {
method: 'get', method: 'get',
url: 'api/frontend/v1/messages/messages/getMessages/' url: url
+ params.id + '/'
+ params.type + '/'
+ params.size + '/'
+ params.page
}; };
}, },
getVorlagen(){ getVorlagen(){
+60 -9
View File
@@ -1,16 +1,67 @@
import {CoreNavigationCmpt} from '../../components/navigation/Navigation.js'; import { CoreNavigationCmpt } from '../../components/navigation/Navigation.js';
import DashboardAdmin from '../../components/Dashboard/Admin.js'; import DashboardAdmin from '../../components/Dashboard/Admin.js';
import PluginsPhrasen from '../../plugins/Phrasen.js'; import PluginsPhrasen from '../../plugins/Phrasen.js';
import ApiRenderers from '../../api/factory/renderers.js';
const app = Vue.createApp({ const app = Vue.createApp({
name: 'AdminApp', name: 'DashboardAdminApp',
data: () => ({ data: () => ({
appSideMenuEntries: {} appSideMenuEntries: {},
}), renderers: null
components: { }),
CoreNavigationCmpt, components: {
DashboardAdmin CoreNavigationCmpt,
} DashboardAdmin
},
provide() {
return {
// TODO(chris): move those two into the components that need it
renderers: Vue.computed(() => this.renderers),
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone
};
},
created() {
this.$api
.call(ApiRenderers.loadRenderers())
.then(res => {
for (let rendertype of Object.keys(res.data)) {
let modalTitle = null;
let modalContent = null;
let calendarEvent = null;
if (res.data[rendertype].modalTitle)
modalTitle = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].modalTitle)));
if (res.data[rendertype].modalContent)
modalContent = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].modalContent)));
if (res.data[rendertype].calendarEvent)
calendarEvent = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].calendarEvent)));
if (res.data[rendertype].calendarEventStyles) {
var head = document.head;
if (!head.querySelector(`link[href="${res.data[rendertype].calendarEventStyles}"]`)) {
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = res.data[rendertype].calendarEventStyles;
head.appendChild(link);
}
}
if (this.renderers === null) {
this.renderers = {};
}
if (!this.renderers[rendertype]) {
this.renderers[rendertype] = {}
}
this.renderers[rendertype].modalTitle = modalTitle;
this.renderers[rendertype].modalContent = modalContent;
this.renderers[rendertype].calendarEvent = calendarEvent;
}
})
.catch(this.$fhcAlert.handleSystemErrors);
}
}); });
app.use(PluginsPhrasen); app.use(PluginsPhrasen);
app.directive('tooltip', primevue.tooltip);
app.mount('#main'); app.mount('#main');
+17
View File
@@ -0,0 +1,17 @@
import {CoreNavigationCmpt} from '../../components/navigation/Navigation.js';
import CoreDashboard from '../../components/Dashboard/Dashboard.js';
import PluginsPhrasen from '../../plugins/Phrasen.js';
const app = Vue.createApp({
name: 'DashboardPreviewApp',
data: () => ({
appSideMenuEntries: {}
}),
components: {
CoreNavigationCmpt,
CoreDashboard
}
});
app.use(PluginsPhrasen);
app.directive('tooltip', primevue.tooltip);
app.mount('#main');
+1 -1
View File
@@ -17,7 +17,7 @@ export default {
</template> </template>
<template v-slot:footer> <template v-slot:footer>
<button type="button" class="btn btn-primary" @click="result=true;this.hide()">OK</button> <button type="button" class="btn btn-primary" @click="result=true;this.hide()">OK</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{this.$p.t('ui', 'cancel')}}</button>
</template> </template>
</bs-modal>` </bs-modal>`
} }
+66 -34
View File
@@ -3,15 +3,20 @@ import DashboardAdminEdit from "./Admin/Edit.js";
import DashboardAdminWidgets from "./Admin/Widgets.js"; import DashboardAdminWidgets from "./Admin/Widgets.js";
import DashboardAdminPresets from "./Admin/Presets.js"; import DashboardAdminPresets from "./Admin/Presets.js";
import ApiDashboardBoard from "../../api/factory/dashboard/board.js";
import ApiDashboardWidget from "../../api/factory/dashboard/widget.js";
export default { export default {
name: 'DashboardAdmin',
components: { components: {
DashboardAdminEdit, DashboardAdminEdit,
DashboardAdminWidgets, DashboardAdminWidgets,
DashboardAdminPresets DashboardAdminPresets,
}, },
provide() { provide() {
return { return {
adminMode: true adminMode: true,
widgetsSetup: Vue.computed(() => this.dashboards[this.current] ? this.dashboards[this.current].widgetSetup : null)
}; };
}, },
data() { data() {
@@ -22,9 +27,6 @@ export default {
}; };
}, },
computed: { computed: {
apiurl() {
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard';
},
dashboard() { dashboard() {
return this.dashboards.find(el => el.dashboard_id == this.current); return this.dashboards.find(el => el.dashboard_id == this.current);
} }
@@ -35,33 +37,50 @@ export default {
BsPrompt.popup('New Dashboard name').then( BsPrompt.popup('New Dashboard name').then(
name => { name => {
_name = name; _name = name;
return axios.post(this.apiurl + '/Dashboard/create', { const params = {
dashboard_kurzbz: name dashboard_kurzbz: name
}) };
} return this.$api
).then(res => { .call(ApiDashboardBoard.add(params))
let newDashboard = { .then(response =>{
dashboard_id: res.data.retval, this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
dashboard_kurzbz: _name,
beschreibung: '' let newDashboard = {
}; dashboard_id: response.data,
this.dashboards.push(newDashboard); dashboard_kurzbz: _name,
this.current = newDashboard.dashboard_id; beschreibung: ''
}).catch(err => err !== undefined ? console.error('ERROR:', err) : 0); };
this.dashboards.push(newDashboard);
this.current = newDashboard.dashboard_id;
})
.catch(this.$fhcAlert.handleSystemError);
});
}, },
dashboardUpdate(dashboard) { dashboardUpdate(dashboard) {
// TODO(chris): Loading or message return this.$api
axios.post(this.apiurl + '/Dashboard/update', dashboard).then(() => { .call(ApiDashboardBoard.update(dashboard))
let old = this.dashboards.find(el => el.dashboard_id == dashboard.dashboard_id); .then(response =>{
old.dashboard_kurzbz = dashboard.dashboard_kurzbz;
old.beschreibung = dashboard.beschreibung; this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
}).catch(err => console.error('ERROR:', err));
let old = this.dashboards.find(el => el.dashboard_id == dashboard.dashboard_id);
old.dashboard_kurzbz = dashboard.dashboard_kurzbz;
old.beschreibung = dashboard.beschreibung;
})
.catch(this.$fhcAlert.handleSystemError);
}, },
dashboardDelete(dashboard_id) { dashboardDelete(dashboard_id) {
axios.post(this.apiurl + '/Dashboard/delete', {dashboard_id}).then(() => { return this.$api
this.current = -1; .call(ApiDashboardBoard.delete(dashboard_id))
this.dashboards = this.dashboards.filter(el => el.dashboard_id != dashboard_id); .then(response => {
}).catch(err => console.error('ERROR:', err)); this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
})
.catch(this.$fhcAlert.handleSystemError)
.finally(() => {
this.current = -1;
this.dashboards = this.dashboards.filter(el => el.dashboard_id != dashboard_id);
});
}, },
assignWidgets(widgets) { assignWidgets(widgets) {
this.widgets = widgets; this.widgets = widgets;
@@ -72,22 +91,35 @@ export default {
} }
}, },
created() { created() {
axios.get(this.apiurl + '/Dashboard').then(res => { this.$api
this.dashboards = res.data.retval; .call(ApiDashboardBoard.list())
}).catch(err => console.error('ERROR:', err)); .then(result => {
this.dashboards = result.data.retval;
for (const dashboard of this.dashboards) {
this.$api
.call(ApiDashboardWidget.list(dashboard.dashboard_id))
.then(res => {
dashboard.widgetSetup = res.data;
})
.catch(this.$fhcAlert.handleSystemError);
}
})
.catch(this.$fhcAlert.handleSystemError);
}, },
template: `<div class="dashboard-admin"> template: `<div class="dashboard-admin">
<div class="input-group"> <div class="input-group">
<label for="dashbaord-select" class="input-group-text">Dashboard:</label> <label for="dashboard-select" class="input-group-text">Dashboard:</label>
<select id="dashbaord-select" class="form-select" v-model="current"> <select id="dashboard-select" class="form-select" v-model="current">
<option v-for="dashboard in dashboards" :key="dashboard.dashboard_id" :value="dashboard.dashboard_id">{{dashboard.dashboard_kurzbz}}</option> <option v-for="dashboard in dashboards" :key="dashboard.dashboard_id" :value="dashboard.dashboard_id">{{dashboard.dashboard_kurzbz}}</option>
</select> </select>
<button class="btn btn-outline-secondary" type="button" @click="dashboardAdd"><i class="fa-solid fa-plus"></i></button> <button class="btn btn-outline-secondary" type="button" @click="dashboardAdd"><i class="fa-solid fa-plus"></i></button>
</div> </div>
<div v-if="dashboard"> <div v-if="dashboard">
<ul class="nav nav-tabs mt-3" role="tablist"> <ul class="nav nav-tabs mt-3" role="tablist">
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link" id="edit-tab" data-bs-toggle="tab" data-bs-target="#edit" type="button" role="tab" aria-controls="edit" aria-selected="false">Edit</button> <button class="nav-link" id="edit-tab" data-bs-toggle="tab" data-bs-target="#edit" type="button" role="tab" aria-controls="edit" aria-selected="false">{{this.$p.t('ui', 'bearbeiten')}}</button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link active" id="widgets-tab" data-bs-toggle="tab" data-bs-target="#widgets" type="button" role="tab" aria-controls="widgets" aria-selected="true">Widgets</button> <button class="nav-link active" id="widgets-tab" data-bs-toggle="tab" data-bs-target="#widgets" type="button" role="tab" aria-controls="widgets" aria-selected="true">Widgets</button>
@@ -101,7 +133,7 @@ export default {
<dashboard-admin-edit v-bind="dashboard" :key="dashboard.dashboard_id" @change="dashboardUpdate($event)" @delete="dashboardDelete($event)"></dashboard-admin-edit> <dashboard-admin-edit v-bind="dashboard" :key="dashboard.dashboard_id" @change="dashboardUpdate($event)" @delete="dashboardDelete($event)"></dashboard-admin-edit>
</div> </div>
<div class="tab-pane fade show active" id="widgets" role="tabpanel" aria-labelledby="widgets-tab"> <div class="tab-pane fade show active" id="widgets" role="tabpanel" aria-labelledby="widgets-tab">
<dashboard-admin-widgets :key="dashboard.dashboard_id" :dashboard_id="dashboard.dashboard_id" :widgets="widgets" @change="test" @assign-widgets="assignWidgets"></dashboard-admin-widgets> <dashboard-admin-widgets :key="dashboard.dashboard_id" :dashboard_id="dashboard.dashboard_id" :widgets="widgets" @assign-widgets="assignWidgets"></dashboard-admin-widgets>
</div> </div>
<div class="tab-pane fade" id="presets" role="tabpanel" aria-labelledby="presets-tab"> <div class="tab-pane fade" id="presets" role="tabpanel" aria-labelledby="presets-tab">
<dashboard-admin-presets :dashboard="dashboard.dashboard_kurzbz" :widgets="widgets"></dashboard-admin-presets> <dashboard-admin-presets :dashboard="dashboard.dashboard_kurzbz" :widgets="widgets"></dashboard-admin-presets>
+4 -3
View File
@@ -18,7 +18,8 @@ export default {
}, },
methods: { methods: {
sendDelete() { sendDelete() {
BsConfirm.popup('Sure?').then(() => this.$emit('delete', this.dashboard_id)).catch(); BsConfirm.popup(this.$p.t('ui', 'confirm_delete') + " " + this.$p.t('ui', 'deleteInfo'))
.then(() => this.$emit('delete', this.dashboard_id)).catch();
} }
}, },
template: `<div class="dashboard-admin-edit px-3"> template: `<div class="dashboard-admin-edit px-3">
@@ -31,8 +32,8 @@ export default {
<textarea id="dashboard-admin-edit-beschreibung" class="form-control" v-model="desc"></textarea> <textarea id="dashboard-admin-edit-beschreibung" class="form-control" v-model="desc"></textarea>
</div> </div>
<div> <div>
<button class="btn btn-danger" @click="sendDelete">Delete</button> <button class="btn btn-danger" @click="sendDelete">{{this.$p.t('ui', 'loeschen')}}</button>
<button class="btn btn-primary" @click="$emit('change', {dashboard_id,dashboard_kurzbz:kurzbz,beschreibung:desc})">Update</button> <button class="btn btn-primary" @click="$emit('change', {dashboard_id,dashboard_kurzbz:kurzbz,beschreibung:desc})">{{this.$p.t('ui', 'btnAktualisieren')}}</button>
</div> </div>
</div>` </div>`
} }
+111 -89
View File
@@ -1,6 +1,7 @@
import DashboardSection from "../Section.js"; import DashboardSection from "../Section.js";
import DashboardWidgetPicker from "../Widget/Picker.js"; import DashboardWidgetPicker from "../Widget/Picker.js";
import ObjectUtils from "../../../helpers/ObjectUtils.js"; import ObjectUtils from "../../../helpers/ObjectUtils.js";
import ApiDashboardPreset from "../../../api/factory/dashboard/preset.js";
export default { export default {
components: { components: {
@@ -17,9 +18,6 @@ export default {
tmpLoading: '' tmpLoading: ''
}), }),
computed: { computed: {
apiurl() {
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard';
},
pickerWidgets() { pickerWidgets() {
return this.widgets.filter(widget => widget.allowed); return this.widgets.filter(widget => widget.allowed);
} }
@@ -28,6 +26,7 @@ export default {
widgetAdd(section_name, widget) { widgetAdd(section_name, widget) {
this.$refs.widgetpicker.getWidget().then(widget_id => { this.$refs.widgetpicker.getWidget().then(widget_id => {
widget.widget = widget_id; widget.widget = widget_id;
widget.id = 'loading_' + String((new Date()).valueOf());
delete widget.custom; delete widget.custom;
widget.preset = 1; widget.preset = 1;
let loading = {...widget}; let loading = {...widget};
@@ -36,130 +35,153 @@ export default {
if (section.name == section_name) if (section.name == section_name)
section.widgets.push(loading); section.widgets.push(loading);
}); });
axios.post(this.apiurl + '/Config/addWidgetsToPreset', { const params = {
db: this.dashboard, dashboard: this.dashboard,
funktion_kurzbz: section_name, funktion_kurzbz: section_name,
widgets: [widget] widget
}).then(result => { };
let newId = Object.keys(result.data.retval.data[section_name].widgets).pop();
widget.id = newId; return this.$api
widget.custom = 1; .call(ApiDashboardPreset.addWidget(params))
this.sections.forEach(section => { .then(result => {
if (section.name == section_name) { let newId = result.data;
section.widgets.splice(section.widgets.indexOf(loading),1); widget.id = newId;
section.widgets.push(widget); widget.custom = 1;
} this.sections.forEach(section => {
}); if (section.name == section_name) {
}).catch(error => { section.widgets.splice(section.widgets.indexOf(loading),1);
console.error('ERROR: ', error); section.widgets.push(widget);
alert('ERROR: ' + error.response.data.retval); }
}); });
}).catch(() => {}); this.funktionen.forEach(funktion => {
if(funktion.funktion_kurzbz === section_name && funktion.has_preset < 1) {
funktion.has_preset = 1;
}
});
})
.catch(this.$fhcAlert.handleSystemError);
})
.catch(() => {});
}, },
widgetUpdate(section_name, payload) { widgetUpdate(section_name, payload) {
payload = payload[section_name]; payload = payload[section_name];
for (var k in payload) { for (var k in payload) {
for (var i in this.sections) { const section = this.sections.find(section => section.name == section_name);
if (this.sections[i].name == section_name) { for (var wid in section.widgets) {
for (var wid in this.sections[i].widgets) { if (section.widgets[wid].id == k) {
if (this.sections[i].widgets[wid].id == k) { payload[k] = ObjectUtils.mergeDeep(section.widgets[wid], payload[k]);
payload[k] = ObjectUtils.mergeDeep(this.sections[i].widgets[wid], payload[k]); // NOTE(chris): remove internal props
// NOTE(chris): remove internal props for (var prop of ['_x', '_y', '_w', '_h', 'index', 'id'])
for (var prop in {_x:1,_y:1,_w:1,_h:1,index:1,id:1}) if (payload[k][prop])
if (payload[k][prop]) delete payload[k][prop];
delete payload[k][prop];
break;
}
}
break; break;
} }
} }
payload[k].widgetid = k; payload[k].widgetid = k;
delete payload[k].custom; delete payload[k].custom;
} }
axios.post(this.apiurl + '/Config/addWidgetsToPreset', { this.$api
db: this.dashboard, .call(Object.entries(payload).map(([key, widget]) => [
funktion_kurzbz: section_name, key,
widgets: payload ApiDashboardPreset.addWidget({
}).then(() => { dashboard: this.dashboard,
this.sections.forEach(section => { funktion_kurzbz: section_name,
if (section.name == section_name) { widget
section.widgets.forEach((widget, i) => { })
if (payload[widget.id]) { ]))
payload[widget.id].id = widget.id; .then(result => {
payload[widget.id].index = widget.index; this.sections.forEach(section => {
section.widgets[i] = payload[widget.id]; if (section.name == section_name) {
section.widgets[i].custom = 1; section.widgets.forEach((widget, i) => {
} if (payload[widget.id]) {
}); payload[widget.id].id = widget.id;
} payload[widget.id].index = widget.index;
}); section.widgets[i] = payload[widget.id];
}).catch(error => { section.widgets[i].custom = 1;
// TODO(chris): revert placement on failure }
console.error('ERROR: ', error); });
alert('ERROR: ' + error.response.data.retval); }
}); });
})
.catch(this.$fhcAlert.handleSystemError);
}, },
widgetRemove(section_name, id) { widgetRemove(section_name, id) {
axios.post(this.apiurl + '/Config/removeWidgetFromPreset', { const params = {
db: this.dashboard, db: this.dashboard,
funktion_kurzbz: section_name, funktion_kurzbz: section_name,
widgetid: id widgetid: id
}).then(() => { };
this.sections.forEach(section => { return this.$api
if (section.name == section_name) .call(ApiDashboardPreset.removeWidget(params))
section.widgets = section.widgets.filter(widget => widget.id != id); .then(result => {
}); this.sections.forEach(section => {
}).catch(error => { if (section.name == section_name)
console.error('ERROR: ', error); section.widgets = section.widgets.filter(widget => widget.id != id);
alert('ERROR: ' + error.response.data.retval); });
}); })
.catch(this.$fhcAlert.handleSystemError);
}, },
loadSections(evt) { loadSections(evt) {
let funktionen = Array.from(evt.target.querySelectorAll("option:checked"),e=>e.value); let funktionen = Array.from(evt.target.querySelectorAll("option:checked"),e=>e.value);
this.sections = []; this.sections = [];
this.tmpLoading = funktionen.join('###'); this.tmpLoading = funktionen.join('###');
axios.get(this.apiurl + '/Config/presetBatch', {params: {
const params = {
db: this.dashboard, db: this.dashboard,
funktionen funktionen
}}).then(res => { };
if (this.tmpLoading !== funktionen.join('###'))
return; // NOTE(chris): prevent race condition return this.$api
for (var section in res.data.retval) { .call(ApiDashboardPreset.getBatch(params))
let widgets = []; .then(result => {
for (var wid in res.data.retval[section]) { if (this.tmpLoading !== funktionen.join('###'))
res.data.retval[section][wid].id = wid; return; // NOTE(chris): prevent race condition
res.data.retval[section][wid].custom = 1; for (var section in result.data) {
widgets.push(res.data.retval[section][wid]); let widgets = [];
for (var wid in result.data[section]) {
result.data[section][wid].id = wid;
result.data[section][wid].custom = 1;
widgets.push(result.data[section][wid]);
}
this.sections.push({
name: section,
widgets
});
} }
this.sections.push({ })
name: section, .catch(this.$fhcAlert.handleSystemError);
widgets
}); },
} loadFunktionen() {
}).catch(err => console.error('ERROR:', err)); this.$api
.call(ApiDashboardPreset.list(this.dashboard))
.then(result => {
this.funktionen = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
} }
}, },
created() { created() {
axios.get(this.apiurl + '/Config/funktionen').then(res => { this.loadFunktionen();
this.funktionen = {general: 'GENERAL'};
res.data.retval.forEach(funktion => {
this.funktionen[funktion.funktion_kurzbz] = funktion.beschreibung;
});
}).catch(err => console.error('ERROR:', err));
}, },
watch: { watch: {
dashboard() { dashboard() {
// TODO(chris): this should be done without a watcher // TODO(chris): this should be done without a watcher
this.loadSections({target:this.$refs.funktionenList}); this.loadSections({target:this.$refs.funktionenList});
this.loadFunktionen();
} }
}, },
template: `<div class="dashboard-admin-presets"> template: `<div class="dashboard-admin-presets">
<div class="row"> <div class="row">
<div class="col-3"> <div class="col-3">
<select ref="funktionenList" style="height:30em" class="form-control" multiple @input="loadSections"> <select ref="funktionenList" style="height:30em" class="form-control" multiple @input="loadSections">
<option v-for="name,id in funktionen" :key="id" :value="id">{{ name }}</option> <option
v-for="funktion in funktionen"
:key="funktion.funktion_kurzbz"
:value="funktion.funktion_kurzbz"
:class="(funktion.has_preset > 0) ? 'fw-bold' : ''"
>{{ funktion.beschreibung }}</option>
</select> </select>
</div> </div>
<div class="col-9"> <div class="col-9">
+13 -20
View File
@@ -1,3 +1,5 @@
import ApiDashboardWidget from "../../../api/factory/dashboard/widget.js";
export default { export default {
emits: [ emits: [
"change", "change",
@@ -7,34 +9,25 @@ export default {
dashboard_id: Number, dashboard_id: Number,
widgets: Array widgets: Array
}, },
computed: {
apiurl() {
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard';
}
},
methods: { methods: {
sendChange(widget_id) { sendChange(widget_id) {
let allow = !this.widgets.find(el => el.widget_id == widget_id).allowed; let allow = !this.widgets.find(el => el.widget_id == widget_id).allowed;
axios.post(this.apiurl + '/Widget/setAllowed', {
dashboard_id: this.dashboard_id, this.$api
widget_id, .call(ApiDashboardWidget.setAllowed(this.dashboard_id, widget_id, allow))
action: allow ? 'add' : 'delete' .catch(this.$fhcAlert.handleSystemError);
}).catch(err => console.error('ERROR: ' + err));
} }
}, },
created() { created() {
axios.get(this.apiurl + '/Widget/getAll', { this.$api
params:{ .call(ApiDashboardWidget.list(this.dashboard_id))
dashboard_id: this.dashboard_id .then(result => {
} this.$emit('assignWidgets', result.data.map(el => ({
}).then(
result => {
this.$emit('assignWidgets', result.data.retval.map(el => ({
...el, ...el,
...{setup:JSON.parse(el.setup),arguments:JSON.parse(el.arguments),allowed:!!el.allowed} allowed: !!el.allowed
}))); })));
} })
).catch(err => console.error('ERROR:', err)); .catch(this.$fhcAlert.handleSystemError);
}, },
template: ` template: `
<div class="dashboard-admin-widgets"> <div class="dashboard-admin-widgets">
+105 -138
View File
@@ -2,7 +2,8 @@ import DashboardSection from "./Section.js";
import DashboardWidgetPicker from "./Widget/Picker.js"; import DashboardWidgetPicker from "./Widget/Picker.js";
import ObjectUtils from "../../helpers/ObjectUtils.js"; import ObjectUtils from "../../helpers/ObjectUtils.js";
import ApiDashboard from '../../api/factory/cis/dashboard.js'; import ApiDashboardWidget from '../../api/factory/dashboard/widget.js';
import ApiDashboardUser from '../../api/factory/dashboard/user.js';
export default { export default {
name: 'Dashboard', name: 'Dashboard',
@@ -20,181 +21,147 @@ export default {
type: Object, type: Object,
required: true, required: true,
validator(value) { validator(value) {
return value && value.name && value.uid && value.timezone return value && value.name && value.timezone
} }
} }
}, },
data() { data() {
return { return {
sections: [], widgets: [],
widgets: null, originalWidgets: {},
editMode: false, widgetsSetup: null,
viewDataInternal: this.viewData editMode: false
} }
}, },
provide() { provide() {
return { return {
editMode: Vue.computed(()=>this.editMode), editMode: Vue.computed(()=>this.editMode),
widgetsSetup: Vue.computed(() => this.widgets), widgetsSetup: Vue.computed(() => this.widgetsSetup),
timezone: Vue.computed(() => this.viewData.timezone) timezone: Vue.computed(() => this.viewData.timezone)
} }
}, },
computed: {
apiurl() {
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard';
}
},
methods: { methods: {
widgetAdd(section_name, widget) { widgetAdd(section_name, widget) {
if (this.widgets === null) { // TODO(chris): remove section_name? (change order of params => get rid of it)
axios.get(this.apiurl + '/Widget/getWidgetsForDashboard', {params:{ this.$refs.widgetpicker
db: this.dashboard .getWidget()
}}).then(res => { .then(widget_id => {
res.data.retval.forEach(widget => { widget.widget = widget_id;
widget.arguments = JSON.parse(widget.arguments); widget.id = 'loading_' + String((new Date()).valueOf());
widget.setup = JSON.parse(widget.setup); let loading = { ...widget };
}); loading.loading = true;
this.widgets = res.data.retval; this.widgets.push(loading);
}).catch(err => console.error('ERROR:', err));
} this.$api
this.$refs.widgetpicker.getWidget().then(widget_id => { .call(ApiDashboardUser.addWidget(this.dashboard, widget))
widget.widget = widget_id; .then(result => {
widget.id = 'loading_' + String((new Date()).valueOf()); widget.id = result.data;
let loading = {...widget}; this.widgets.splice(this.widgets.indexOf(loading), 1);
loading.loading = true; this.widgets.push(widget);
this.sections.forEach(section => { this.originalWidgets[widget.id] = structuredClone(ObjectUtils.deepToRaw(widget));
if (section.name == section_name) })
section.widgets.push(loading); .catch(this.$fhcAlert.handleSystemError);
}); })
.catch(() => {});
axios.post(this.apiurl + '/Config/addWidgetsToUserOverride', {
db: this.dashboard,
funktion_kurzbz: section_name,
widgets: [widget]
}).then(result => {
let newId = Object.keys(result.data.retval.data[section_name].widgets).pop();
widget.id = newId;
this.sections.forEach(section => {
if (section.name == section_name) {
section.widgets.splice(section.widgets.indexOf(loading),1);
section.widgets.push(widget);
}
});
}).catch(error => {
console.error('ERROR: ', error);
alert('ERROR: ' + error.response.data.retval);
});
}).catch(() => {});
}, },
widgetUpdate(section_name, payload) { widgetUpdate(section_name, payload) {
payload = payload[section_name]; payload = payload[section_name];
for (var k in payload) { for (var k in payload) {
for (var i in this.sections) { for (var wid in this.widgets) {
if (this.sections[i].name == section_name) { if (this.widgets[wid].id == k) {
for (var wid in this.sections[i].widgets) { payload[k] = ObjectUtils.mergeDeep(this.widgets[wid], payload[k]);
if (this.sections[i].widgets[wid].id == k) { // NOTE(chris): remove internal props
payload[k] = ObjectUtils.mergeDeep(this.sections[i].widgets[wid], payload[k]); for (var prop of ['_x','_y','_w','_h','index','id','preset'])
// NOTE(chris): remove internal props if (payload[k][prop])
for (var prop in {_x:1,_y:1,_w:1,_h:1,index:1,id:1,preset:1}) delete payload[k][prop];
if (payload[k][prop])
delete payload[k][prop];
break;
}
}
break; break;
} }
} }
payload[k].widgetid = k; payload[k].widgetid = k;
} }
axios.post(this.apiurl + '/Config/addWidgetsToUserOverride', { this.$api
db: this.dashboard, .call(Object.entries(payload).map(([key, widget]) => [
funktion_kurzbz: section_name, key,
widgets: payload ApiDashboardUser.addWidget(this.dashboard, widget)
}).then(() => { ]))
this.sections.forEach(section => { .then(result => {
if (section.name == section_name) { const failed = result
section.widgets.forEach((widget, i) => { .filter(o => o.status == 'rejected')
if (payload[widget.id]) { .map(o => o.reason.config.errorHeader);
payload[widget.id].id = widget.id;
payload[widget.id].index = widget.index; this.widgets.forEach((widget, i) => {
section.widgets[i] = payload[widget.id]; if (failed.includes(widget.id)) {
this.widgets[i] = structuredClone(ObjectUtils.deepToRaw(this.originalWidgets[widget.id]));
/** NOTE(chris): if you wanna hide or unhide a
* preset and it fails: switch around the hidden
* value to revert it properly (checkboxes can't
* really handle it otherwise)
*/
if (payload[widget.id].hidden !== undefined) {
this.widgets[i].hidden = payload[widget.id].hidden;
this.$nextTick(() => {
this.widgets[i] = structuredClone(ObjectUtils.deepToRaw(this.originalWidgets[widget.id]));
});
} }
}); } else if (payload[widget.id]) {
} payload[widget.id].id = widget.id;
}); payload[widget.id].index = widget.index;
}).catch(error => { this.widgets[i] = payload[widget.id];
// TODO(chris): revert placement on failure this.originalWidgets[widget.id] = structuredClone(ObjectUtils.deepToRaw(this.widgets[i]));
console.error('ERROR: ', error); }
alert('ERROR: ' + error.response.data.retval); });
}); })
.catch(this.$fhcAlert.handleSystemError);
}, },
widgetRemove(section_name, id) { widgetRemove(section_name, id) {
axios.post(this.apiurl + '/Config/removeWidgetFromUserOverride', { this.$api
db: this.dashboard, .call(ApiDashboardUser.removeWidget(this.dashboard, id))
funktion_kurzbz: section_name, .then(() => {
widgetid: id this.widgets = this.widgets.filter(widget => widget.id != id);
}).then(() => { })
this.sections.forEach(section => { .catch(this.$fhcAlert.handleSystemError);
if (section.name == section_name)
section.widgets = section.widgets.filter(widget => widget.id != id);
});
}).catch(error => {
console.error('ERROR: ', error);
alert('ERROR: ' + error.response.data.retval);
});
} }
}, },
created() { created() {
this.$p.loadCategory('dashboard'); this.$p.loadCategory('dashboard');
axios.get(this.apiurl + '/Widget/getWidgetsForDashboard', {
params: {
db: this.dashboard
}
}).then(res => {
this.widgets = res.data.retval;
}).catch(err => console.error('ERROR:', err));
axios.get(this.apiurl + '/Config', {params:{ this.$api
db: this.dashboard .call(ApiDashboardWidget.listAllowed(this.dashboard))
}}).then(res => { .then(res => {
for (var name in res.data.retval) { this.widgetsSetup = res.data;
let widgets = []; })
let remove = []; .catch(this.$fhcAlert.handleSystemError);
for (var wid in res.data.retval[name].widgets) {
res.data.retval[name].widgets[wid].id = wid; this.$api
if (res.data.retval[name].widgets[wid].custom || res.data.retval[name].widgets[wid].preset) .call(ApiDashboardUser.get(this.dashboard))
widgets.push(res.data.retval[name].widgets[wid]); .then(res => {
else const widgets = [];
const remove = [];
for (var wid in res.data.general.widgets) {
let widget = res.data.general.widgets[wid];
widget.id = wid;
if (widget.custom || widget.preset) {
widgets.push(widget);
this.originalWidgets[wid] = structuredClone(widget);
} else {
remove.push(wid); remove.push(wid);
}
} }
this.sections.push({
name: name, remove.forEach(wid => this.widgetRemove('general', wid));
widgets: widgets
}); this.widgets = widgets;
remove.forEach(wid => this.widgetRemove(name, wid)); })
} .catch(this.$fhcAlert.handleSystemError);
this.sections = this.sections.sort((section1, section2) => {
if(section1.name == 'custom')
return 1;
if (section2.name == 'custom')
return -1;
return section2.widgets.length - section1.widgets.length;
});
}).catch(err => console.error('ERROR:', err));
},
async beforeMount() {
if (!this.viewData.name || !this.viewData.uid) {
const res = await this.$api.call(ApiDashboard.getViewData());
this.viewDataInternal = res.data
}
}, },
template: ` template: `
<div class="core-dashboard"> <div class="core-dashboard">
<h3 v-show="viewDataInternal?.name"> <h3>
{{ $p.t('global/personalGreeting', [ viewDataInternal?.name ]) }} {{ $p.t('global/personalGreeting', [ viewData?.name ]) }}
<button style="margin-left: 8px;" class="btn" @click="editMode = !editMode" aria-label="edit dashboard" v-tooltip="{showDelay:1000,value:'edit dashboard'}"><i class="fa-solid fa-gear" aria-hidden="true"></i></button> <button style="margin-left: 8px;" class="btn" @click="editMode = !editMode" aria-label="edit dashboard" v-tooltip="{showDelay:1000,value:'edit dashboard'}"><i class="fa-solid fa-gear" aria-hidden="true"></i></button>
</h3> </h3>
<dashboard-section v-for="(section, index) in sections" :key="section.name" :seperator="index" :name="section.name" :widgets="section.widgets" @widgetAdd="widgetAdd" @widgetUpdate="widgetUpdate" @widgetRemove="widgetRemove"></dashboard-section> <dashboard-section :seperator="0" name="general" :widgets="widgets" @widgetAdd="widgetAdd" @widgetUpdate="widgetUpdate" @widgetRemove="widgetRemove"></dashboard-section>
<dashboard-widget-picker ref="widgetpicker" :widgets="widgets"></dashboard-widget-picker> <dashboard-widget-picker ref="widgetpicker" :widgets="widgetsSetup"></dashboard-widget-picker>
</div>` </div>`
} }
+17 -3
View File
@@ -1,5 +1,5 @@
import BsModal from "../Bootstrap/Modal.js"; import BsModal from "../Bootstrap/Modal.js";
import CachedWidgetLoader from "../../composables/Dashboard/CachedWidgetLoader.js"; import { useCachedWidgetLoader } from "../../composables/Dashboard/CachedWidgetLoader.js";
import HeightTransition from "../Tranistion/HeightTransition.js"; import HeightTransition from "../Tranistion/HeightTransition.js";
export default { export default {
@@ -70,6 +70,14 @@ export default {
ready() { ready() {
return this.component && this.arguments !== null; return this.component && this.arguments !== null;
}, },
visible: {
get() {
return !this.hidden;
},
set(value) {
this.$emit('remove', this.hidden);
}
}
}, },
methods: { methods: {
unpin(){ unpin(){
@@ -142,8 +150,14 @@ export default {
this.isLoading = false; this.isLoading = false;
}, },
}, },
setup() {
const { actions } = useCachedWidgetLoader();
return {
loadWidget: actions.load
};
},
async created() { async created() {
this.widget = await CachedWidgetLoader.loadWidget(this.id); this.widget = await this.loadWidget(this.id);
let component = (await import(this.widget.setup.file)).default; let component = (await import(this.widget.setup.file)).default;
this.$options.components["widget" + this.widget.widget_id] = component; this.$options.components["widget" + this.widget.widget_id] = component;
this.component = "widget" + this.widget.widget_id; this.component = "widget" + this.widget.widget_id;
@@ -185,7 +199,7 @@ export default {
</a> </a>
<Transition> <Transition>
<div v-if="!custom && editMode" class="col-auto px-1 form-switch"> <div v-if="!custom && editMode" class="col-auto px-1 form-switch">
<input class="form-check-input ms-0" type="checkbox" role="switch" aria-label="toggle widget" id="flexSwitchCheckChecked" :checked="!hidden" @input="$emit('remove', hidden)"> <input class="form-check-input ms-0" type="checkbox" role="switch" aria-label="toggle widget" id="flexSwitchCheckChecked" v-model="visible" :value="true">
</div> </div>
</Transition> </Transition>
</div> </div>
+14 -7
View File
@@ -1,7 +1,7 @@
import BsConfirm from "../Bootstrap/Confirm.js"; import BsConfirm from "../Bootstrap/Confirm.js";
import DropGrid from '../Drop/Grid.js' import DropGrid from '../Drop/Grid.js'
import DashboardItem from "./Item.js"; import DashboardItem from "./Item.js";
import CachedWidgetLoader from "../../composables/Dashboard/CachedWidgetLoader.js"; import { useCachedWidgetLoader } from "../../composables/Dashboard/CachedWidgetLoader.js";
import WidgetIcon from "./Widget/WidgetIcon.js" import WidgetIcon from "./Widget/WidgetIcon.js"
export default { export default {
@@ -125,23 +125,23 @@ export default {
}, },
checkResizeLimit(item, w, h) { checkResizeLimit(item, w, h) {
// NOTE(chris): widgets needs to be loaded for this to work // NOTE(chris): widgets needs to be loaded for this to work
let widget = CachedWidgetLoader.getWidget(item.widget); let widget = this.widgetState[item.widget];
if (widget) { if (widget) {
let minmaxW = widget.setup.width; let minmaxW = { ...widget.setup.width };
if (minmaxW.max) if (minmaxW.max)
minmaxW.min = minmaxW.min || 1; minmaxW.min = minmaxW.min || 1;
else else
minmaxW = {min:minmaxW,max:minmaxW}; minmaxW = { min: minmaxW, max: minmaxW };
if (w < minmaxW.min) if (w < minmaxW.min)
w = minmaxW.min; w = minmaxW.min;
if (w > minmaxW.max) if (w > minmaxW.max)
w = minmaxW.max; w = minmaxW.max;
let minmaxH = widget.setup.height; let minmaxH = { ...widget.setup.height };
if (minmaxH.max) if (minmaxH.max)
minmaxH.min = minmaxH.min || 1; minmaxH.min = minmaxH.min || 1;
else else
minmaxH = {min:minmaxH,max:minmaxH}; minmaxH = { min: minmaxH, max: minmaxH };
if (h < minmaxH.min) if (h < minmaxH.min)
h = minmaxH.min; h = minmaxH.min;
if (h > minmaxH.max) if (h > minmaxH.max)
@@ -151,7 +151,7 @@ export default {
}, },
removeWidget(item, revert) { removeWidget(item, revert) {
if (item.custom) { if (item.custom) {
BsConfirm.popup('Are you sure you want to delete this widget?').then(() => this.$emit('widgetRemove', this.name, item.id)); BsConfirm.popup(this.$p.t('dashboard', 'alert_deleteWidget')).then(() => this.$emit('widgetRemove', this.name, item.id));
} else { } else {
let update = {}; let update = {};
update[item.id] = { hidden: !revert }; update[item.id] = { hidden: !revert };
@@ -199,6 +199,13 @@ export default {
this.$emit('widgetUpdate', this.name, payload); this.$emit('widgetUpdate', this.name, payload);
} }
}, },
setup() {
const { state: widgetState } = useCachedWidgetLoader();
return {
widgetState
};
},
mounted() { mounted() {
let self = this; let self = this;
let cont = self.$refs.container; let cont = self.$refs.container;
@@ -133,6 +133,7 @@ export default {
return this.$api return this.$api
.call(ApiMessages.getDataVorlage(vorlage_kurzbz)) .call(ApiMessages.getDataVorlage(vorlage_kurzbz))
.then(response => { .then(response => {
this.editor.setContent(response.data.text);
this.formData.body = response.data.text; this.formData.body = response.data.text;
this.formData.subject = response.data.subject; this.formData.subject = response.data.subject;
}).catch(this.$fhcAlert.handleSystemError); }).catch(this.$fhcAlert.handleSystemError);
@@ -203,24 +204,6 @@ export default {
}, },
}, },
watch: { watch: {
'formData.body': {
handler(newVal) {
const tinymcsVal = this.editor.getContent();
if (newVal && tinymcsVal != newVal) {
//Inhalt des Editors aktualisieren
this.editor.setContent(newVal);
}
}
},
'formData.vorlage_kurzbz': {
handler(newVal){
if (newVal && newVal != null) {
this.formData.subject = newVal;
return this.getDataVorlage(newVal);
}
}
},
messageId: { messageId: {
immediate: true, immediate: true,
handler: async function (newMessageId) { handler: async function (newMessageId) {
@@ -231,6 +214,7 @@ export default {
this.replyData = result.data; this.replyData = result.data;
if (this.replyData.length > 0) { if (this.replyData.length > 0) {
this.editor.setContent(this.replyData[0].replyBody);
this.formData.subject = this.replyData[0].replySubject; this.formData.subject = this.replyData[0].replySubject;
this.formData.body = this.replyData[0].replyBody; this.formData.body = this.replyData[0].replyBody;
this.formData.relationmessage_id = newMessageId; this.formData.relationmessage_id = newMessageId;
@@ -290,19 +274,6 @@ export default {
}) })
.catch(this.$fhcAlert.handleSystemError); .catch(this.$fhcAlert.handleSystemError);
//case of reply
if(this.messageId) {
this.$api
.call(ApiMessages.getReplyData(this.messageId))
.then(result => {
this.replyData = result.data;
this.formData.subject = this.replyData[0].replySubject;
this.formData.body = this.replyData[0].replyBody;
this.formData.relationmessage_id = this.messageId;
})
.catch(this.$fhcAlert.handleSystemError);
}
}, },
async mounted() { async mounted() {
this.initTinyMCE(); this.initTinyMCE();
@@ -64,7 +64,16 @@ export default {
target: this.$refs.editor.$refs.input, //Important: not selector: to enable multiple import of component target: this.$refs.editor.$refs.input, //Important: not selector: to enable multiple import of component
//height: 800, //height: 800,
//plugins: ['lists'], //plugins: ['lists'],
toolbar: 'styleselect | bold italic underline | alignleft aligncenter alignright alignjustify', toolbar: 'styleselect | bold italic underline | alignleft aligncenter alignright alignjustify | link',
plugins: 'link',
link_context_toolbar: true,
automatic_uploads: true,
default_link_target: "_blank",
link_title: true,
target_list: [
{ title: 'New tab', value: '_blank' },
{ title: 'Same tab', value: '_self' }
],
style_formats: [ style_formats: [
{title: 'Blocks', block: 'div'}, {title: 'Blocks', block: 'div'},
{title: 'Paragraph', block: 'p'}, {title: 'Paragraph', block: 'p'},
@@ -98,7 +107,8 @@ export default {
return this.$api return this.$api
.call(ApiMessages.sendMessage(this.typeId, data)) .call(ApiMessages.sendMessage(this.typeId, data))
.then(response => { .then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSent')); if(this.openMode == "inSamePage")
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSent'));
this.hideTemplate(); this.hideTemplate();
this.resetForm(); this.resetForm();
this.messageSent = true; this.messageSent = true;
@@ -114,19 +124,17 @@ export default {
return this.$api return this.$api
.call(ApiMessages.getDataVorlage(vorlage_kurzbz)) .call(ApiMessages.getDataVorlage(vorlage_kurzbz))
.then(response => { .then(response => {
this.editor.setContent(response.data.text);
this.formData.body = response.data.text; this.formData.body = response.data.text;
this.formData.subject = response.data.subject; this.formData.subject = response.data.subject;
}).catch(this.$fhcAlert.handleSystemError); }).catch(this.$fhcAlert.handleSystemError);
}, },
getPreviewText(){ getPreviewText(){
console.log("subj" + this.formData.subject);
const data = new FormData(); const data = new FormData();
data.append('data', JSON.stringify(this.formData.body)); data.append('data', JSON.stringify(this.formData.body));
data.append('ids', JSON.stringify(this.id)); data.append('ids', JSON.stringify(this.id));
console.log("subj" + this.formData.subject);
return this.$api return this.$api
.call(ApiMessages.getPreviewText( .call(ApiMessages.getPreviewText(
this.typeId, data)) this.typeId, data))
@@ -195,6 +203,7 @@ export default {
.call(ApiMessages.getReplyData(messageId)) .call(ApiMessages.getReplyData(messageId))
.then(result => { .then(result => {
this.replyData = result.data; this.replyData = result.data;
this.editor.setContent(this.replyData[0].replyBody);
this.formData.subject = this.replyData[0].replySubject; this.formData.subject = this.replyData[0].replySubject;
this.formData.body = this.replyData[0].replyBody; this.formData.body = this.replyData[0].replyBody;
this.formData.relationmessage_id = messageId; this.formData.relationmessage_id = messageId;
@@ -202,27 +211,6 @@ export default {
.catch(this.$fhcAlert.handleSystemError); .catch(this.$fhcAlert.handleSystemError);
} }
}, },
watch: {
'formData.body': {
handler(newVal) {
const tinymcsVal = this.editor.getContent();
if (newVal && tinymcsVal != newVal) {
//Inhalt des Editors aktualisieren
this.editor.setContent(newVal);
}
}
},
'formData.vorlage_kurzbz': {
handler(newVal){
if (newVal && newVal != null) {
this.formData.subject = newVal;
return this.getDataVorlage(newVal);
}
}
},
},
created(){ created(){
const missingparamsmsgs = []; const missingparamsmsgs = [];
if(!this.typeId) if(!this.typeId)
@@ -291,17 +279,8 @@ export default {
.catch(this.$fhcAlert.handleSystemError); .catch(this.$fhcAlert.handleSystemError);
//case of reply //case of reply
if(this.messageId != null) { if(this.messageId) {
this.loadReplyData(this.messageId); this.loadReplyData(this.messageId);
/* this.$api
.call(ApiMessages.getReplyData(this.messageId))
.then(result => {
this.replyData = result.data;
this.formData.subject = this.replyData[0].replySubject;
this.formData.body = this.replyData[0].replyBody;
this.formData.relationmessage_id = this.messageId;
})
.catch(this.$fhcAlert.handleSystemError);*/
} }
}, },
@@ -499,10 +478,10 @@ export default {
<div class="row"> <div class="row">
<div class="col-6" style="border-right: 1px"> <div class="col-6" style="border-right: 1px">
You can safely close this window. You can safely close this window/tab.
</div> </div>
<div class="col-6"> <div class="col-6">
Sie können dieses Fenster schließen. Fenster/Reiter kann geschlossen werden!
</div> </div>
</div> </div>
</div> </div>
@@ -65,7 +65,14 @@ export default {
buildTreemap(messages) { buildTreemap(messages) {
if (!messages || !messages.data || messages.data.length === 0) if (!messages || !messages.data || messages.data.length === 0)
{ {
return {data: [], last_page: 0}; if(this.tabulatorOptions.pagination)
{
return {data: [], last_page: 0};
}
else
{
return [];
}
} }
const last_page = messages.meta.count; const last_page = messages.meta.count;
@@ -106,7 +113,15 @@ export default {
// to avoid endless loop // to avoid endless loop
if (iteration > messages.length) break; if (iteration > messages.length) break;
} }
return {data: messageNested, last_page: last_page};
if(this.tabulatorOptions.pagination)
{
return {data: messageNested, last_page: last_page};
}
else
{
return messageNested;
}
}, },
loadAjaxCall(url, config, params){ loadAjaxCall(url, config, params){
return this.$api.call( return this.$api.call(
@@ -252,7 +267,7 @@ export default {
frozen: true frozen: true
} }
], ],
pagination: true, pagination: false,
paginationMode: "remote", paginationMode: "remote",
paginationSize: 15, paginationSize: 15,
paginationInitialPage: 1, paginationInitialPage: 1,
+8 -6
View File
@@ -82,14 +82,16 @@ export default {
this.$refs.modalMsg.show(); this.$refs.modalMsg.show();
} }
else if (this.openMode == "inSamePage"){ else if (this.openMode == "inSamePage"){
console.log("in same Page");
this.isVisibleDiv = true; this.isVisibleDiv = true;
if(messageId)
this.$refs.templateNewDivMessage.loadReplyData(messageId);
else
this.$refs.templateNewDivMessage.resetForm();
this.$refs.templateNewDivMessage.showTemplate(); this.$nextTick(() => {
if(messageId)
this.$refs.templateNewDivMessage.loadReplyData(messageId);
else
this.$refs.templateNewDivMessage.resetForm();
this.$refs.templateNewDivMessage.showTemplate();
});
} }
else else
console.log("no valid openMode"); console.log("no valid openMode");
@@ -74,6 +74,16 @@ export default {
], ],
'abschlussdokument_lehrgaenge.xml.php': [ 'abschlussdokument_lehrgaenge.xml.php': [
'AbschlussdokumentLehrgaenge' 'AbschlussdokumentLehrgaenge'
],
'microcredential.xml.php' : [
'microcredentialzertifikat_1',
'microcredentialzertifikat_2',
'microcredentialzertifikat_3',
'microcredentialzertifikat_4',
'microcredential_1',
'microcredential_2',
'microcredential_3',
'microcredential_4',
] ]
}, },
documentDropdownObject: {} documentDropdownObject: {}
@@ -1,29 +1,36 @@
let __widgets = {}; import ApiWidget from "../../api/factory/dashboard/widget.js";
let __widgetsStarted = {};
let __path = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard/Widget';
export default { const promises = Vue.ref([]);
getWidget(id) { const stateRef = Vue.ref([]);
return __widgets[id]; const state = Vue.readonly(stateRef);
},
loadWidget(id) {
if (__widgets[id])
return Promise.resolve(__widgets[id]);
if (__widgetsStarted[id])
return __widgetsStarted[id];
if (!__path)
return Promise.reject('Widget could not be loaded because there is no path yet!');
__widgetsStarted[id] = new Promise((resolve, reject) => { export function useCachedWidgetLoader() {
axios.get(__path, {params:{id}}).then(res => { const $api = Vue.inject('$api');
__widgets[id] = res.data.retval; const $fhcAlert = Vue.inject('$fhcAlert');
__widgetsStarted[id] = undefined;
resolve(__widgets[id]); function load(id) {
}).catch(error => reject(error.response.data.retval.error)); if (state.value[id])
}); return Promise.resolve(state.value[id]);
return __widgetsStarted[id];
}, if (!promises.value[id])
setPath(path) { promises.value[id] = new Promise((resolve, reject) => {
__path = path; $api
.call(ApiWidget.get(id))
.then(res => {
stateRef.value[id] = res.data;
promises.value[id] = undefined;
resolve(state.value[id]);
})
.catch($fhcAlert.handleSystemError);
});
return promises.value[id];
} }
return {
state,
actions: {
load
}
};
} }
+70 -29
View File
@@ -1,31 +1,72 @@
export default { /**
/** * Performs a deep merge of objects and returns new object. Does not modify
* Performs a deep merge of objects and returns new object. Does not modify * objects (immutable) and merges arrays via concatenation.
* objects (immutable) and merges arrays via concatenation. *
* * @param {...object} objects - Objects to merge
* @param {...object} objects - Objects to merge * @returns {object} New object with merged key/values
* @returns {object} New object with merged key/values */
*/ function mergeDeep(...objects) {
mergeDeep(...objects) { const isObject = obj => obj && typeof obj === 'object';
const isObject = obj => obj && typeof obj === 'object';
return objects.reduce((prev, obj) => {
return objects.reduce((prev, obj) => { Object.keys(obj).forEach(key => {
Object.keys(obj).forEach(key => { const pVal = prev[key];
const pVal = prev[key]; const oVal = obj[key];
const oVal = obj[key];
if (Array.isArray(pVal) && Array.isArray(oVal)) {
prev[key] = pVal.concat(...oVal);
}
else if (isObject(pVal) && isObject(oVal)) {
prev[key] = this.mergeDeep(pVal, oVal);
}
else {
prev[key] = oVal;
}
});
return prev; if (Array.isArray(pVal) && Array.isArray(oVal)) {
}, {}); prev[key] = pVal.concat(...oVal);
} }
else if (isObject(pVal) && isObject(oVal)) {
prev[key] = this.mergeDeep(pVal, oVal);
}
else {
prev[key] = oVal;
}
});
return prev;
}, {});
}
/**
* Extends VUEs toRaw() function to nested Proxies
* @see https://www.reddit.com/r/javascript/comments/10gzynk/deep_cloning_objects_in_javascript_the_modern_way/
*
* @param object sourceObj - Object to transform
* @returns object
*/
function deepToRaw(sourceObj) {
const objectIterator = input => {
if (Array.isArray(input))
return input.map(objectIterator);
if (Vue.isRef(input) || Vue.isReactive(input) || Vue.isProxy(input))
return objectIterator(Vue.toRaw(input));
if (input && typeof input === 'object') {
/** use custom handling of 'Date' objects to avoid data loss if treating it like any other object.
* reminder:
* typeof (new Date()) ==> 'object'
* Object.keys(new Date()) ==> []
*/
if (input instanceof Date)
return input;
return Object.keys(input).reduce((acc, key) => {
acc[key] = objectIterator(input[key]);
return acc;
}, {});
}
return input;
};
return objectIterator(sourceObj);
}
export {
mergeDeep,
deepToRaw
}
export default {
mergeDeep,
deepToRaw
} }
+8 -8
View File
@@ -430,16 +430,16 @@ export default {
fhcApiAxios.interceptors.response.use( fhcApiAxios.interceptors.response.use(
response => { response => {
if (response.config?.errorHandling == 'off' const errorConfig = get_error_handler(response.config);
|| response.config?.errorHandling === false
|| response.config?.errorHandling == 'fail') if (!errorConfig.success)
return clean_return_value(response); return clean_return_value(response);
// NOTE(chris): loop through errors const errors = popHandleableErrors(errorConfig, response.data.errors);
if (response.data.errors)
response.data.errors = response.data.errors.filter( for (var type in errors) {
err => (response.config[err.type + 'ErrorHandler'] || app.config.globalProperties.$api._defaultErrorHandlers[err.type])(err, response.config) errorConfig.handler[type](errors[type]);
); }
return clean_return_value(response); return clean_return_value(response);
}, },
+1
View File
@@ -144,6 +144,7 @@ foreach($ps->result as $row)
<ROLLE:stichtagsaktiv><![CDATA['.$stichtagsaktiv.']]></ROLLE:stichtagsaktiv> <ROLLE:stichtagsaktiv><![CDATA['.$stichtagsaktiv.']]></ROLLE:stichtagsaktiv>
<ROLLE:aktiv><![CDATA['.$aktiv.']]></ROLLE:aktiv> <ROLLE:aktiv><![CDATA['.$aktiv.']]></ROLLE:aktiv>
<ROLLE:fgm><![CDATA['.$row->fgm.']]></ROLLE:fgm> <ROLLE:fgm><![CDATA['.$row->fgm.']]></ROLLE:fgm>
<ROLLE:faktiv><![CDATA['.($row->faktiv?'Ja':'Nein').']]></ROLLE:faktiv>
</RDF:Description> </RDF:Description>
</RDF:li> </RDF:li>
'; ';
+3 -3
View File
@@ -450,18 +450,18 @@ td.MarkLine
td.HeaderTesttool /*fuer die Button-Optik beim Testtool*/ td.HeaderTesttool /*fuer die Button-Optik beim Testtool*/
{ {
color: #FFFFFF; color: #FFFFFF;
background-color: #00639C; background-color: #71787D;
white-space:nowrap; white-space:nowrap;
line-height: 25px; line-height: 25px;
box-shadow: inset 0 0 2px #FFFFFF; box-shadow: inset 0 0 2px #FFFFFF;
padding: 10px; padding: 0 10px;
width: 170px; width: 170px;
} }
td.HeaderTesttoolSTG /*fuer die Button-Optik der Quereinstiegs-Studiengänge beim Testtool*/ td.HeaderTesttoolSTG /*fuer die Button-Optik der Quereinstiegs-Studiengänge beim Testtool*/
{ {
color: white; color: white;
border: 2px solid #73a9d6; border: 2px solid #73a9d6;
padding: 10px; padding: 0 10px;
max-width: 100px; max-width: 100px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
+5998 -107
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -92,6 +92,7 @@ require_once('dbupdate_3.4/68744_StV_settings.php');
require_once('dbupdate_3.4/62889_reihungstest_ueberwachung_mit_constructor.php'); require_once('dbupdate_3.4/62889_reihungstest_ueberwachung_mit_constructor.php');
require_once('dbupdate_3.4/71399_dashboard_update_widget_paths.php'); require_once('dbupdate_3.4/71399_dashboard_update_widget_paths.php');
require_once('dbupdate_3.4/71645_studvw_messagetab_ladezeit.php'); require_once('dbupdate_3.4/71645_studvw_messagetab_ladezeit.php');
require_once('dbupdate_3.4/71566_studienordnungsdokument_neuer_organisationseinheitstyp_programm.php');
require_once('dbupdate_3.4/70376_lohnguide.php'); require_once('dbupdate_3.4/70376_lohnguide.php');
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
+14 -1
View File
@@ -1,6 +1,19 @@
<?php <?php
if (! defined('DB_NAME')) exit('No direct script access allowed'); if (! defined('DB_NAME')) exit('No direct script access allowed');
if ($result = @$db->db_query("SELECT 1 FROM system.tbl_app WHERE app='lvevaluierung' LIMIT 1"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "INSERT INTO system.tbl_app (app) VALUES ('lvevaluierung');";
if(!$db->db_query($qry))
echo '<strong>system.tbl_app: '.$db->db_last_error().'</strong><br>';
else
echo ' system.tbl_app: lvevaluierung hinzugefügt<br>';
}
}
//Add column evaluierung to lehre.tbl_lehrveranstaltung //Add column evaluierung to lehre.tbl_lehrveranstaltung
if(!@$db->db_query("SELECT evaluierung FROM lehre.tbl_lehrveranstaltung LIMIT 1")) if(!@$db->db_query("SELECT evaluierung FROM lehre.tbl_lehrveranstaltung LIMIT 1"))
{ {
@@ -12,4 +25,4 @@ if(!@$db->db_query("SELECT evaluierung FROM lehre.tbl_lehrveranstaltung LIMIT 1"
echo '<strong>lehre.tbl_lehrveranstaltung '.$db->db_last_error().'</strong><br>'; echo '<strong>lehre.tbl_lehrveranstaltung '.$db->db_last_error().'</strong><br>';
else else
echo '<br>Spalte evaluierung zu Tabelle lehre.tbl_lehrveranstaltung hinzugefügt'; echo '<br>Spalte evaluierung zu Tabelle lehre.tbl_lehrveranstaltung hinzugefügt';
} }
@@ -0,0 +1,15 @@
<?php
// neuen Organisationseinheittyp (Programm) als Zeile hinzufügen
if($result = @$db->db_query("SELECT 1 FROM public.tbl_organisationseinheittyp WHERE organisationseinheittyp_kurzbz= 'Programm';"))
{
if($db->db_num_rows($result) == 0)
{
$qry = "INSERT INTO public.tbl_organisationseinheittyp(organisationseinheittyp_kurzbz, beschreibung, bezeichnung) VALUES ('Programm', 'Programm', 'Programm');";
if(!$db->db_query($qry))
echo '<strong>public.tbl_organisationseinheittyp: '.$db->db_last_error().'</strong><br>';
else
echo '<br>public.tbl_organisationseinheittyp: Zeile Programm hinzugefuegt!<br>';
}
}
+981
View File
@@ -57009,6 +57009,906 @@ I have been informed that I am under no obligation to consent to the transmissio
) )
) )
), ),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'lektorseiteTitle',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'LV-Evaluation | Übersicht LV-Leitung und Lehrende',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'LV-Evaluation | Overview Course Leader and Lecturers',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipEvaluierungebeneGesamt',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Evaluierung der LV erfolgt auf Gesamt-Ebene',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'This course is evaluated at the overall level',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipEvaluierungGruppen',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Evaluierung der LV erfolgt auf Gruppen-Ebene',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'This course is evaluated at group level',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipVerbindlichGewaehlt',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'LV-Evaluierung ist verbindlich vorgesehen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Course evaluation is mandatory',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipVerbindlichAbgewaehlt',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'LV wird nicht evaluiert (Abwahl durch STGL)',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Course evaluation is disabled (cancelled by STGL)',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipAlleStudierendeAngemailt',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Alle Studierenden wurden zur LV-Evaluierung eingeladen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'All students were invited to the course evaluation',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipNichtAlleStudierendenAngemailt',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Studierende müssen noch zur LV-Evaluierung eingeladen werden',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Students still have to be invited to the course evaluation',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipRuecklauf',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Abgeschlossene LV-Evaluierungen / zur LV-Evaluierung eingeladene Studierende',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Completed course evaluations / students invited to course evaluation',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipEvaluationNotAvailable',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Ergebnisse LV-Evaluierung und LV-Reflexion noch nicht verfügbar.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Results of course evaluation and course reflection not yet available.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipReflexionNotAvailable',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Ergebnisse LV-Evaluierung noch nicht verfügbar, LV-Reflexion noch nicht möglich.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Results of course evaluation not yet available, course reflection not yet possible.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipEvaluationAvailable',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Ergebnisse LV- Evaluierung verfügbar, LV-Reflexion durchzuführen.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Results of course evaluation available, course reflection required.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipEvaluationReady',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Ergebnisse LV- Evaluierung und LV-Reflexion.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Results of course evaluation and course reflection.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'verbindlich',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'verbindlich',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'mandatory',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'abgewaehlt',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'abgewählt',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'disabled',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'enddatum',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Enddatum',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Enddate',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'startdatum',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Startdatum',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Startdate',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'startdatum',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Startdatum',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Startdate',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'gespeichertAmVon',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Gespeichert am {date} von {name}',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Saved on {date} von {name}',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'xEingeladeneStudierende',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => '{x} eingeladene Studierende',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => '{x} invited students',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'studierendeEinladen',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Studierende zur LV-Evaluierung einladen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Invite students to course evaluation',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'xEmailsVersandt',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => '{x} E-Mail-Einladungen versandt',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => '{x} invitation emails sent',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'email',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'E-Mail',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'email',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'emailVersandBereit',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Bereit zum Versand der E-Mail-Einladungen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Ready to send inivitation mails',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'bearbeitungNurLehrende',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Bearbeitung nur durch Lehrende*n möglich',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Only lecturer of course can edit',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'bearbeitungNurLvLeitung',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Bearbeitung nur durch LV-Leitung möglich',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Only course leader can edit',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'notAvailableEvaluierungGruppenebene',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'LV-Evaluierung auf Gruppen-Ebene ist nicht verfügbar, da die Zuordnung Studierendenverband zu einer*m Lehrenden nicht eindeutig möglich ist',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Course evaluation at group level is not available, as it is not possible to link a specific student group to one lecturer',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'noChangeGruppenebene',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Entscheidung für Gesamt- oder Gruppen-Ebene kann nicht mehr verändert werden',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Decision for overall or group level can no longer be changed',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'evalPeriodAlreadyStarted',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Evaluierungszeitfenster kann nicht mehr verändert werden, da Studierende bereits eingeladen wurden',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Evaluation response window can no longer be changed, as students have already been invited',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipEvaluierungByLv',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Die Evaluierung der LV erfolgt auf Gesamt-Ebene.<br><br>Das Start-und enddatum der LV-Evaluierung kann geändert bzw. angepasst werden, solange die Studierenden noch nicht eingelanden wurden.<br><br>Der Zugriff für Studierende ist auf dieses Evaluierungsfenster beschränkt.",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => "This course is evaluated at the overall level.<br><br>The start and end dates of the course evaluation can be changed or adjusted as long as the students have not yet been invited.<br><br>Student access is limited to this evaluation response window.",
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipEvaluierungByLe',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Die Evaluierung der LV erfolgt auf Gruppen-Ebene.<br><br>Das Start-und enddatum der LV-Evaluierung kann geändert bzw. angepasst werden, solange die Studierenden noch nicht eingelanden wurden.<br><br>Der Zugriff für Studierende ist auf dieses Evaluierungsfenster beschränkt.",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => "This course is evaluated at the group level.<br><br>The start and end dates of the course evaluation can be changed or adjusted as long as the students have not yet been invited.<br><br>Student access is limited to this evaluation response window.",
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'infoStudierendenlink',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Der Versand der E-Mail-Einladung zur LV-Evaluierung ist nur einmalig möglich. Jede*r Studierende*r erhält einen anonymen Zugangslink.",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => "The email invitation to the course evaluation can only be sent once. Each student receives an anonymous access link.",
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'stglseiteTitle',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'LV-Evaluation | Übersicht Studiengangsleitung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'LV-Evaluation | Overview Program Director',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'kfseiteTitle',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'LV-Evaluation | Übersicht Kompetenzfeldleitung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'LV-Evaluation | Overview Head of Competence Center',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'confirmHeader',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Bitte bestätigen Sie:',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Please confirm:',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'malveSubmitBtn',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'MALVE-STGL abschließen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Close MALVE-STGL',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'malveSubmitConfirmMessage',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Ich habe alle LV-Evaluierungen des Studiengangs {studiengang} im {studiensemester} geprüft. Notwendige Maßnahmen für die STG-Weiterentwicklung wurden abgeleitet.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'I have reviewed all the course evaluations for the {studiengang} degree programme in {studiensemester}. The necessary measures for further developing the STG have been identified.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'evaluationsebene',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Evaluationsebene',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Evaluation level',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'confirmHeader',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Bitte bestätigen Sie:',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Please confirm:',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'geprueft',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Geprüft',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Checked',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipGeprueft',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Optional zur besseren persönlichen Übersicht',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Optional for better personal overview.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'lvKeinQuellkurs',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'LV mit keinem Quellkurs verknüpft',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Course not linked to any (Moodle) source template',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'stgWeiterentwicklungBtn',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'STG-Weiterentwicklung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'STG-Continued Improvement',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipStgWeiterentwicklungBtn',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'MALVE-STGL: Schnittstelle zur Maßnahmenableitung für den STG in OP.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'MALVE-STGL: An interface for deriving measures for the degree programme in OP.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'tooltipLvWeiterentwicklungBtn',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Schnittstelle zur Maßnahmenableitung für die einzelnen LVs in OP.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Interface for deriving measures for individual courses in OP.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'lvevaluierung',
'category' => 'global',
'phrase' => 'endedatumMussInZukunftLiegen',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Das Endedatum muss mindestens {minutes} Minuten in der Zukunft liegen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'The end date must be at least {minutes} minutes in the future',
'description' => '',
'insertvon' => 'system'
)
)
),
// ### DOKUMENTE ERSTELLEN PHRASEN END ### // ### DOKUMENTE ERSTELLEN PHRASEN END ###
// ### Personen zusammenlegen Phrasen BEGIN // ### Personen zusammenlegen Phrasen BEGIN
array( array(
@@ -57196,6 +58096,87 @@ I have been informed that I am under no obligation to consent to the transmissio
) )
), ),
// ### Phrases Dashboard Admin START
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'deleteInfo',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Mit dieser Aktion werden auch alle Voreinstellungen der verbundenen Widgets gelöscht.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'This action will also delete all presets of the connected widgets.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'dashboard',
'phrase' => 'success_savePreset',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Voreinstellung erfolgreich aktualisiert',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Preset successfully updated',
'description' => '',
'insertvon' => 'system'
)
)
), array(
'app' => 'core',
'category' => 'dashboard',
'phrase' => 'alert_deleteWidget',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Sind Sie sicher, dass Sie dieses Widget löschen möchten?',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Are you sure you want to delete this widget?',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'confirm_delete',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Möchten Sie wirklich löschen?',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Do you really want to delete?',
'description' => '',
'insertvon' => 'system'
)
)
),
// ### Phrases Dashboard Admin END
); );
+23 -13
View File
@@ -742,7 +742,7 @@ function _getFunktionscontainer_Funktionscode123456($bisfunktion_arr)
$has_oe_lehrgang = !($studiengang->studiengang_kz > 0 && $studiengang->studiengang_kz < 10000); $has_oe_lehrgang = !($studiengang->studiengang_kz > 0 && $studiengang->studiengang_kz < 10000);
// STG, die nicht BIS-bemeldet werden, ueberspringen // STG, die nicht BIS-bemeldet werden, ueberspringen
if (in_array($studiengang->studiengang_kz, BIS_EXCLUDE_STG)) if (in_array($studiengang->studiengang_kz, BIS_EXCLUDE_STG) || !$studiengang->melderelevant)
{ {
continue; continue;
} }
@@ -825,6 +825,7 @@ function _addFunktionscontainer_Funktionscode7($uid, $funktion_arr, $stichtag)
$entwicklungsteam_arr = array_filter($entwicklungsteam_arr, function ($obj) { $entwicklungsteam_arr = array_filter($entwicklungsteam_arr, function ($obj) {
return return
!in_array($obj->studiengang_kz, BIS_EXCLUDE_STG) && !in_array($obj->studiengang_kz, BIS_EXCLUDE_STG) &&
$obj->melderelevant &&
$obj->studiengang_kz > 0 && $obj->studiengang_kz > 0 &&
$obj->studiengang_kz < 10000; $obj->studiengang_kz < 10000;
}); });
@@ -875,7 +876,6 @@ function _getLehrecontainer($sws_proStg_arr)
$sws_proStg_arr = array_filter($sws_proStg_arr, function ($obj) { $sws_proStg_arr = array_filter($sws_proStg_arr, function ($obj) {
return return
!in_array($obj->studiengang_kz, BIS_EXCLUDE_STG) && !in_array($obj->studiengang_kz, BIS_EXCLUDE_STG) &&
$obj->studiengang_kz > 0 &&
$obj->studiengang_kz < 10000; $obj->studiengang_kz < 10000;
}); });
} }
@@ -886,13 +886,17 @@ function _getLehrecontainer($sws_proStg_arr)
{ {
$is_sommersemester = substr($sws_proStg->studiensemester_kurzbz, 0, 2) == 'SS'; $is_sommersemester = substr($sws_proStg->studiensemester_kurzbz, 0, 2) == 'SS';
$is_wintersemester = substr($sws_proStg->studiensemester_kurzbz, 0, 2) == 'WS'; $is_wintersemester = substr($sws_proStg->studiensemester_kurzbz, 0, 2) == 'WS';
$is_lehrgang = isset($sws_proStg->lgartcode);
$kennzeichen_name = $is_lehrgang ? 'LehrgangNr' : 'StgKz';
// Lehreobjekt generieren // Lehreobjekt generieren
if (empty($lehre_arr) || !lehre_stg_exists($sws_proStg->studiengang_kz, $lehre_arr)) if (empty($lehre_arr) || !lehre_stg_exists($sws_proStg->melde_studiengang_kz, $lehre_arr))
{ {
$lehre_obj = new StdClass(); $lehre_obj = new StdClass();
$lehre_obj->StgKz = setLeadingZero(intval($sws_proStg->studiengang_kz), 4); $lehre_obj->{$kennzeichen_name} = $sws_proStg->melde_studiengang_kz;
//~ $lehre_obj->StgKz = setLeadingZero(intval($sws_proStg->studiengang_kz), 4);
$lehre_obj->SommersemesterSWS = $is_sommersemester ? $sws_proStg->sws : 0.00; $lehre_obj->SommersemesterSWS = $is_sommersemester ? $sws_proStg->sws : 0.00;
$lehre_obj->WintersemesterSWS = $is_wintersemester ? $sws_proStg->sws : 0.00; $lehre_obj->WintersemesterSWS = $is_wintersemester ? $sws_proStg->sws : 0.00;
@@ -901,8 +905,8 @@ function _getLehrecontainer($sws_proStg_arr)
} }
else // Lehrecontainer mit STG schon vorhanden else // Lehrecontainer mit STG schon vorhanden
{ {
$lehre_obj_arr = array_filter($lehre_arr, function (&$obj) use ($sws_proStg) { $lehre_obj_arr = array_filter($lehre_arr, function (&$obj) use ($sws_proStg, $kennzeichen_name) {
return $obj->StgKz == $sws_proStg->studiengang_kz; return isset($obj->{$kennzeichen_name}) && $obj->{$kennzeichen_name} == $sws_proStg->melde_studiengang_kz;
}); });
// SWS ergaenzen // SWS ergaenzen
@@ -1020,9 +1024,14 @@ function _generateXML($person_arr)
foreach ($person->lehre_arr as $lehre) foreach ($person->lehre_arr as $lehre)
{ {
$xml .= '<Lehre>'; $xml .= '<Lehre>';
$xml .= '<StgKz><![CDATA['. $lehre->StgKz. ']]></StgKz>';
$xml .= '<SommersemesterSWS><![CDATA['. $lehre->SommersemesterSWS. ']]></SommersemesterSWS>'; if (isset($lehre->LehrgangNr))
$xml .= '<WintersemesterSWS><![CDATA['. $lehre->WintersemesterSWS. ']]></WintersemesterSWS>'; $xml .= '<LehrgangNr><![CDATA['. $lehre->LehrgangNr. ']]></LehrgangNr>';
else
$xml .= '<StgKz><![CDATA['. $lehre->StgKz. ']]></StgKz>';
$xml .= '<SommersemesterSWS><![CDATA['. number_format($lehre->SommersemesterSWS, 2, '.', ''). ']]></SommersemesterSWS>';
$xml .= '<WintersemesterSWS><![CDATA['. number_format($lehre->WintersemesterSWS, 2, '.', ''). ']]></WintersemesterSWS>';
$xml .= '</Lehre>'; $xml .= '</Lehre>';
} }
@@ -1211,7 +1220,7 @@ function _outputHTML($person_arr)
{ {
echo ' echo '
<tr> <tr>
<td>'. $lehre->StgKz. '</td> <td>'. (isset($lehre->LehrgangNr) ? $lehre->LehrgangNr : $lehre->StgKz). '</td>
<td>'. $lehre->SommersemesterSWS. '</td> <td>'. $lehre->SommersemesterSWS. '</td>
<td>'. $lehre->WintersemesterSWS. '</td> <td>'. $lehre->WintersemesterSWS. '</td>
</tr>'; </tr>';
@@ -1351,15 +1360,16 @@ function verwendung_exists($bisverwendung, $verwendung_arr)
/** /**
* Prueft ob ein Studiengang bereits im Lehre Container vorhanden ist * Prueft ob ein Studiengang bereits im Lehre Container vorhanden ist
* @param $studiengang_kz Studiengangskennzahl * @param $melde_studiengang_kz Studiengangskennzahl
* @param $lehre_arr Array mit Lehre Objekten * @param $lehre_arr Array mit Lehre Objekten
* @return true wenn der Studiengang bereits existiert * @return true wenn der Studiengang bereits existiert
*/ */
function lehre_stg_exists($studiengang_kz, $lehre_arr) function lehre_stg_exists($melde_studiengang_kz, $lehre_arr)
{ {
foreach($lehre_arr as $row) foreach($lehre_arr as $row)
{ {
if($row->StgKz == $studiengang_kz) $kennzeichenName = isset($row->LehrgangNr) ? 'LehrgangNr' : 'StgKz';
if(isset($row->{$kennzeichenName}) && $row->{$kennzeichenName} == $melde_studiengang_kz)
return true; return true;
} }
return false; return false;
+12 -3
View File
@@ -227,7 +227,8 @@ if (isset($_GET['sendform']))
<table class="tablesorter" id="t1"> <table class="tablesorter" id="t1">
<thead> <thead>
<tr> <tr>
<th><span class="tooltip"><img src="../../skin/images/information.png" height="20px" name="infoicon"/> <th><button type="button" class="resetsaved" title="Reset Filter">Reset Filter</button>
<span class="tooltip"><img src="../../skin/images/information.png" height="20px" name="infoicon"/>
'.$tooltiptext.' '.$tooltiptext.'
</span> </span>
</th> </th>
@@ -364,9 +365,10 @@ if (isset($_GET['sendform']))
$("#t1").tablesorter( $("#t1").tablesorter(
{ {
sortList: [[3,0]], sortList: [[3,0]],
widgets: ["zebra", "filter", "stickyHeaders"], widgets: ["saveSort", "zebra", "filter", "stickyHeaders"],
headers: { 0: { filter: false, sorter: false }}, headers: { 0: { filter: false, sorter: false }},
widgetOptions : { filter_functions : { widgetOptions : { filter_saveFilters : true,
filter_functions : {
// Add select menu to this column // Add select menu to this column
8 : { 8 : {
"True" : function(e, n, f, i, $r, c, data) { return /t/.test(e); }, "True" : function(e, n, f, i, $r, c, data) { return /t/.test(e); },
@@ -381,6 +383,13 @@ if (isset($_GET['sendform']))
"False" : function(e, n, f, i, $r, c, data) { return /f/.test(e); } "False" : function(e, n, f, i, $r, c, data) { return /f/.test(e); }
} }
}} }}
});
$('.resetsaved').click(function()
{
$("#t1").trigger("filterReset");
location.reload(forceGet);
return false;
}); });
}); });
@@ -588,7 +588,9 @@ if(isset($_POST['testergebnisanzeigen']) && isset($_POST['prestudent_id']))
{ {
if(is_numeric($_POST['prestudent_id']) && $_POST['prestudent_id']!='') if(is_numeric($_POST['prestudent_id']) && $_POST['prestudent_id']!='')
{ {
$qry="SELECT nachname,vorname,person_id,prestudent_id,tbl_pruefling.pruefling_id,tbl_pruefling_frage.begintime,bezeichnung,kurzbz,tbl_frage.nummer,level, tbl_vorschlag.nummer as antwortnummer, tbl_vorschlag.punkte $qry="SELECT nachname,vorname,person_id,prestudent_id,tbl_pruefling.pruefling_id,
tbl_pruefling_frage.begintime,bezeichnung,kurzbz,tbl_frage.nummer,level,
tbl_vorschlag.nummer as antwortnummer, tbl_vorschlag.punkte, tbl_frage.frage_id
FROM testtool.tbl_antwort FROM testtool.tbl_antwort
JOIN testtool.tbl_vorschlag USING(vorschlag_id) JOIN testtool.tbl_vorschlag USING(vorschlag_id)
JOIN testtool.tbl_frage USING (frage_id) JOIN testtool.tbl_frage USING (frage_id)
@@ -615,6 +617,7 @@ if(isset($_POST['testergebnisanzeigen']) && isset($_POST['prestudent_id']))
<th>Level</th> <th>Level</th>
<th>Antwort #</th> <th>Antwort #</th>
<th>Punkte</th> <th>Punkte</th>
<th>FrageID</th>
</tr> </tr>
</thead> </thead>
<tbody>'; <tbody>';
@@ -632,6 +635,7 @@ if(isset($_POST['testergebnisanzeigen']) && isset($_POST['prestudent_id']))
echo "<td>$row->level</td>"; echo "<td>$row->level</td>";
echo "<td>$row->antwortnummer</td>"; echo "<td>$row->antwortnummer</td>";
echo "<td>$row->punkte</td>"; echo "<td>$row->punkte</td>";
echo "<td>$row->frage_id</td>";
echo '</tr>'; echo '</tr>';
} }
echo '</tbody></table>'; echo '</tbody></table>';
+29 -2
View File
@@ -837,6 +837,25 @@ if(isset($_GET['excel']))
<script src="../../vendor/fgelinas/timepicker/jquery.ui.timepicker.js" type="text/javascript" ></script> <script src="../../vendor/fgelinas/timepicker/jquery.ui.timepicker.js" type="text/javascript" ></script>
<script type="text/javascript"> <script type="text/javascript">
$.tablesorter.addParser({
id: "customDate",
is: function(s) {
//return false;
//use the above line if you don\'t want table sorter to auto detected this parser
// match dd.mm.yyyy e.g. 01.01.2001 as regex
//return /\d{1,4}-\d{1,2}-\d{1,2} \d{1,2}:\d{1,2} .*/.test(s);
return /\d{1,2}.\d{1,2}.\d{1,4}.*/.test(s);
},
// replace regex-wildcards and return new date
format: function(s) {
s = s.replace(/\-/g," ");
s = s.replace(/:/g," ");
s = s.replace(/\./g," ");
s = s.split(" ");
return $.tablesorter.formatFloat(new Date(s[2], s[1]-1, s[0]).getTime());
},
type: "numeric"
});
$(document).ready(function() $(document).ready(function()
{ {
// Check, ob Räume zugeteilt sind oder max_teilnehmer gesetzt ist, wenn "öffentlich" gesetzt wird // Check, ob Räume zugeteilt sind oder max_teilnehmer gesetzt ist, wenn "öffentlich" gesetzt wird
@@ -1007,7 +1026,7 @@ if(isset($_GET['excel']))
{ {
widgets: ["zebra", "filter", "stickyHeaders"], widgets: ["zebra", "filter", "stickyHeaders"],
sortList: [[2,0],[3,0]], sortList: [[2,0],[3,0]],
headers: {0: { sorter: false}}, headers: {0: { sorter: false},10: { sorter: "customDate"},11: { sorter: "customDate"}},
widgetOptions: {filter_cssFilter: [ widgetOptions: {filter_cssFilter: [
"filter_clm_null", "filter_clm_null",
"filter_clm_prestudent_id", "filter_clm_prestudent_id",
@@ -1020,6 +1039,7 @@ if(isset($_GET['excel']))
"filter_clm_studienplan", "filter_clm_studienplan",
"filter_clm_einstiegssemester", "filter_clm_einstiegssemester",
"filter_clm_geburtsdatum", "filter_clm_geburtsdatum",
"filter_clm_anmeldedatum",
"filter_clm_email", "filter_clm_email",
"filter_clm_absolviert"]} "filter_clm_absolviert"]}
}); });
@@ -1072,6 +1092,7 @@ if(isset($_GET['excel']))
'clm_studienplan', 'clm_studienplan',
'clm_einstiegssemester', 'clm_einstiegssemester',
'clm_geburtsdatum', 'clm_geburtsdatum',
"filter_clm_anmeldedatum",
'clm_email', 'clm_email',
'clm_absolviert']; 'clm_absolviert'];
for (var i in arr) for (var i in arr)
@@ -2697,7 +2718,8 @@ if($reihungstest_id!='')
WHERE prestudent_id = tbl_prestudent.prestudent_id WHERE prestudent_id = tbl_prestudent.prestudent_id
AND status_kurzbz = 'Interessent' AND status_kurzbz = 'Interessent'
) LIMIT 1 ) LIMIT 1
) AS orgform_kurzbz ) AS orgform_kurzbz,
tbl_rt_person.anmeldedatum
FROM PUBLIC.tbl_rt_person FROM PUBLIC.tbl_rt_person
JOIN PUBLIC.tbl_person USING (person_id) JOIN PUBLIC.tbl_person USING (person_id)
JOIN PUBLIC.tbl_reihungstest rt ON (rt_id = rt.reihungstest_id) JOIN PUBLIC.tbl_reihungstest rt ON (rt_id = rt.reihungstest_id)
@@ -2786,6 +2808,7 @@ if($reihungstest_id!='')
echo '<div id="clm_studienplan" class="active" onclick="hideColumn(\'clm_studienplan\')">Studienplan</div>'; echo '<div id="clm_studienplan" class="active" onclick="hideColumn(\'clm_studienplan\')">Studienplan</div>';
echo '<div id="clm_einstiegssemester" class="active" onclick="hideColumn(\'clm_einstiegssemester\')">Einstiegssemester</div>'; echo '<div id="clm_einstiegssemester" class="active" onclick="hideColumn(\'clm_einstiegssemester\')">Einstiegssemester</div>';
echo '<div id="clm_geburtsdatum" class="active" onclick="hideColumn(\'clm_geburtsdatum\')">Geburtsdatum</div>'; echo '<div id="clm_geburtsdatum" class="active" onclick="hideColumn(\'clm_geburtsdatum\')">Geburtsdatum</div>';
echo '<div id="clm_anmeldedatum" class="active" onclick="hideColumn(\'clm_anmeldedatum\')">Geburtsdatum</div>';
echo '<div id="clm_email" class="active" onclick="hideColumn(\'clm_email\')">EMail</div>'; echo '<div id="clm_email" class="active" onclick="hideColumn(\'clm_email\')">EMail</div>';
echo '<div id="clm_absolviert" class="active" onclick="hideColumn(\'clm_absolviert\')">Absolvierte Tests <span class="wait"></span></div>'; echo '<div id="clm_absolviert" class="active" onclick="hideColumn(\'clm_absolviert\')">Absolvierte Tests <span class="wait"></span></div>';
//echo '<div id="clm_ergebnis" class="active" onclick="hideColumn(\'clm_ergebnis\')">Ergebnis <span class="wait"></span></div>'; //echo '<div id="clm_ergebnis" class="active" onclick="hideColumn(\'clm_ergebnis\')">Ergebnis <span class="wait"></span></div>';
@@ -2827,6 +2850,7 @@ if($reihungstest_id!='')
<th style="display: table-cell" class="clm_studienplan">Studienplan</th> <th style="display: table-cell" class="clm_studienplan">Studienplan</th>
<th style="display: table-cell" class="clm_einstiegssemester">Einstiegssemester</th> <th style="display: table-cell" class="clm_einstiegssemester">Einstiegssemester</th>
<th style="display: table-cell" class="clm_geburtsdatum">Geburtsdatum</th> <th style="display: table-cell" class="clm_geburtsdatum">Geburtsdatum</th>
<th style="display: table-cell" class="clm_anmeldedatum">Anmeldedatum</th>
<th style="display: table-cell" class="clm_email">EMail</th> <th style="display: table-cell" class="clm_email">EMail</th>
<th style="display: table-cell" class="clm_absolviert">bereits absolvierte Verfahren</th> <th style="display: table-cell" class="clm_absolviert">bereits absolvierte Verfahren</th>
<!--<th style="display: table-cell" class="clm_ergebnis">Ergebnis</th> <!--<th style="display: table-cell" class="clm_ergebnis">Ergebnis</th>
@@ -2946,6 +2970,7 @@ if($reihungstest_id!='')
<td style="display: table-cell" class="clm_studienplan">'.$db->convert_html_chars($studienplan_bezeichnung).' ('.$row->studienplan_id.')</td> <td style="display: table-cell" class="clm_studienplan">'.$db->convert_html_chars($studienplan_bezeichnung).' ('.$row->studienplan_id.')</td>
<td style="display: table-cell" class="clm_einstiegssemester">'.$db->convert_html_chars($row->ausbildungssemester).'</td> <td style="display: table-cell" class="clm_einstiegssemester">'.$db->convert_html_chars($row->ausbildungssemester).'</td>
<td style="display: table-cell" class="clm_geburtsdatum">'.$db->convert_html_chars($row->gebdatum!=''?$datum_obj->convertISODate($row->gebdatum):' ').'</td> <td style="display: table-cell" class="clm_geburtsdatum">'.$db->convert_html_chars($row->gebdatum!=''?$datum_obj->convertISODate($row->gebdatum):' ').'</td>
<td style="display: table-cell" class="clm_anmeldedatum">'.$db->convert_html_chars($row->anmeldedatum!=''?$datum_obj->convertISODate($row->anmeldedatum):' ').'</td>
<td style="display: table-cell; text-align: center" class="clm_email"><a href="mailto:'.$db->convert_html_chars($row->email).'"><img src="../../skin/images/button_mail.gif" name="mail"></a></td> <td style="display: table-cell; text-align: center" class="clm_email"><a href="mailto:'.$db->convert_html_chars($row->email).'"><img src="../../skin/images/button_mail.gif" name="mail"></a></td>
<td style="display: table-cell;" class="clm_absolviert">'.$rt_in_anderen_stg.'</td> <td style="display: table-cell;" class="clm_absolviert">'.$rt_in_anderen_stg.'</td>
</tr>'; </tr>';
@@ -3009,6 +3034,7 @@ if($reihungstest_id!='')
<th style="display: table-cell" class="clm_studienplan">Studienplan</th> <th style="display: table-cell" class="clm_studienplan">Studienplan</th>
<th style="display: table-cell" class="clm_einstiegssemester">Einstiegssemester</th> <th style="display: table-cell" class="clm_einstiegssemester">Einstiegssemester</th>
<th style="display: table-cell" class="clm_geburtsdatum">Geburtsdatum</th> <th style="display: table-cell" class="clm_geburtsdatum">Geburtsdatum</th>
<th style="display: table-cell" class="clm_anmeldedatum">Anmeldedatum</th>
<th style="display: table-cell" class="clm_email">EMail</th> <th style="display: table-cell" class="clm_email">EMail</th>
<th style="display: table-cell" class="clm_absolviert">bereits absolvierte Verfahren</th> <th style="display: table-cell" class="clm_absolviert">bereits absolvierte Verfahren</th>
<!--<th style="display: table-cell" class="clm_ergebnis">Ergebnis</th> <!--<th style="display: table-cell" class="clm_ergebnis">Ergebnis</th>
@@ -3128,6 +3154,7 @@ if($reihungstest_id!='')
<td style="display: table-cell" class="clm_studienplan">'.$db->convert_html_chars($studienplan_bezeichnung).' ('.$row->studienplan_id.')</td> <td style="display: table-cell" class="clm_studienplan">'.$db->convert_html_chars($studienplan_bezeichnung).' ('.$row->studienplan_id.')</td>
<td style="display: table-cell" class="clm_einstiegssemester">'.$db->convert_html_chars($row->ausbildungssemester).'</td> <td style="display: table-cell" class="clm_einstiegssemester">'.$db->convert_html_chars($row->ausbildungssemester).'</td>
<td style="display: table-cell" class="clm_geburtsdatum">'.$db->convert_html_chars($row->gebdatum!=''?$datum_obj->convertISODate($row->gebdatum):' ').'</td> <td style="display: table-cell" class="clm_geburtsdatum">'.$db->convert_html_chars($row->gebdatum!=''?$datum_obj->convertISODate($row->gebdatum):' ').'</td>
<td style="display: table-cell" class="clm_anmeldedatum">'.$db->convert_html_chars($row->anmeldedatum!=''?$datum_obj->convertISODate($row->anmeldedatum):' ').'</td>
<td style="display: table-cell; text-align: center" class="clm_email"><a href="mailto:'.$db->convert_html_chars($row->email).'"><img src="../../skin/images/button_mail.gif" name="mail"></a></td> <td style="display: table-cell; text-align: center" class="clm_email"><a href="mailto:'.$db->convert_html_chars($row->email).'"><img src="../../skin/images/button_mail.gif" name="mail"></a></td>
<td style="display: table-cell;" class="clm_absolviert">'.$rt_in_anderen_stg.'</td>'; <td style="display: table-cell;" class="clm_absolviert">'.$rt_in_anderen_stg.'</td>';
/*echo '<td style="display: table-cell; align: right" class="clm_ergebnis">'.($rtergebnis==0?'-':number_format($rtergebnis,2,'.','')).' %</td> /*echo '<td style="display: table-cell; align: right" class="clm_ergebnis">'.($rtergebnis==0?'-':number_format($rtergebnis,2,'.','')).' %</td>