Compare commits

..

7 Commits

Author SHA1 Message Date
Paolo 16b649c951 Merge branch 'master' into feature-52366/CI_LoginAsUID_verbessern 2026-04-27 13:07:23 +02:00
Paolo 8a567416db Merge branch 'master' into feature-52366/CI_LoginAsUID_verbessern 2026-03-24 09:36:54 +01:00
Paolo a3dffd1fc2 Login AS GUI now uses the UID and not the person_id 2026-03-06 12:16:19 +01:00
Paolo 2460253c19 Merge branch 'master' into feature-52366/CI_LoginAsUID_verbessern 2026-02-23 14:17:36 +01:00
Paolo 62ad0dcd47 GUI improvements 2025-10-21 11:48:10 +02:00
Paolo 9355ee740d LoginAs: added search by person id and sorted results by surname and name 2025-10-20 12:34:08 +02:00
Paolo 2484bf1217 - Added LoginAs to the menu
- Added new page to see your credentials and to get the credentials of someone else using autocomplete (searches by uid, name or surname)
- Refactored the login page on CI side
2025-10-20 11:40:10 +02:00
24 changed files with 802 additions and 468 deletions
+43 -19
View File
@@ -1,11 +1,27 @@
<?php
// Header menu
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if(defined('CIS4') && CIS4) {
$root = APP_ROOT;
} else {
$root = CIS_ROOT;
}
$root = CIS_ROOT;
if (defined('CIS4') && CIS4) $root = APP_ROOT;
// --------------------------------------------------------------------------------------------------------------------
// Head menu
$config['navigation_header'] = array(
'*' => array(
@@ -202,13 +218,20 @@ $config['navigation_header'] = array(
'sort' => 20,
'requiredPermissions' => 'system/developer:r'
),
'anrechnungen' => array(
'link' => site_url('lehre/anrechnung/AdminAnrechnung'),
'description' => 'Anrechnungen',
'expand' => true,
'sort' => 30,
'requiredPermissions' => 'lehre/anrechnungszeitfenster:rw'
),
'anrechnungen' => array(
'link' => site_url('lehre/anrechnung/AdminAnrechnung'),
'description' => 'Anrechnungen',
'expand' => true,
'sort' => 30,
'requiredPermissions' => 'lehre/anrechnungszeitfenster:rw'
),
'loginas' => array(
'link' => site_url('system/Login/loginAs'),
'description' => 'Login as',
'expand' => true,
'sort' => 40,
'requiredPermissions' => 'admin:rw'
),
'dashboardadmin' => array(
'link' => site_url('dashboard/Admin'),
'description' => 'Dashboard Admin',
@@ -236,12 +259,12 @@ $config['navigation_menu']['Vilesci/index'] = array(
);
$config['navigation_menu']['Vilesci/index'] = array(
'dashboard' => array(
'link' => '#',
'description' => 'Dashboard',
'icon' => 'dashboard',
'sort' => 1
)
'dashboard' => array(
'link' => '#',
'description' => 'Dashboard',
'icon' => 'dashboard',
'sort' => 1
)
);
$config['navigation_menu']['organisation/Reihungstest/index'] = array(
@@ -383,3 +406,4 @@ $config['navigation_menu']['apps'] = [
'requiredPermissions' => array('lehre/lehrauftrag_bestellen:r', 'lehre/lehrauftrag_erteilen:r')
]
];
@@ -0,0 +1,137 @@
<?php
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*
*/
class Login extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct(array(
'loginLDAP' => self::PERM_ANONYMOUS,
'loginASByUid' => 'admin:rw',
'loginASByPersonId' => 'admin:rw',
'whoAmI' => self::PERM_ANONYMOUS,
'searchUser' => 'admin:rw'
));
}
/**
* Called with HTTP POST via ajax to login using the LDAP authentication
*/
public function loginLDAP()
{
$username = $this->input->post('username');
$password = $this->input->post('password');
$this->load->library('AuthLib', array(false)); // without authentication otherwise loooooop!
$login = $this->authlib->loginLDAP($username, $password);
// If login is success then retrieves the desired page
if (isSuccess($login)) $this->terminateWithSuccess($this->authlib->getLandingPage());
$this->terminateWithError(getError($login)); // returns the error code
}
/**
* Called with HTTP POST via ajax to login as another user specified by uid
*/
public function loginASByUid()
{
$uid = $this->input->post('uid');
// With authentication -> you must be already logged to gain another identity
$this->load->library('AuthLib');
$loginAS = $this->authlib->loginASByUID($uid);
// Got it!
if (isSuccess($loginAS)) $this->terminateWithSuccess(true);
// Returns the error code
$this->terminateWithError(getError($loginAS));
}
/**
* Called with HTTP POST via ajax to login as another user specified by person id
*/
public function loginASByPersonId()
{
$person_id = $this->input->post('person_id');
// With authentication -> you must be already logged to gain another identity
$this->load->library('AuthLib');
$loginAS = $this->authlib->loginASByPersonId($person_id);
// Got it!
if (isSuccess($loginAS)) $this->terminateWithSuccess(true);
// Returns the error code
$this->terminateWithError(getError($loginAS));
}
/**
* Called with HTTP GET via ajax to show which login cretentials are in use
*/
public function whoAmI()
{
// With authentication -> you must be already logged to gain another identity
$this->load->library('AuthLib');
$this->terminateWithSuccess($this->authlib->getAuthObj());
}
/**
* Search for a user in database checking the name, surname or uid
*/
public function searchUser()
{
$query = strtolower('%'.$this->input->get('query').'%');
$this->load->model('person/Benutzer_model', 'BenutzerModel');
$dataset = $this->BenutzerModel->execReadOnlyQuery('
SELECT p.person_id,
b.uid,
p.nachname,
p.vorname,
b.uid,
p.person_id || \' - \' || b.uid || \' - \' || p.nachname || \' \' || p.vorname AS label
FROM public.tbl_person p
LEFT JOIN public.tbl_benutzer b ON(b.person_id = p.person_id)
WHERE b.aktiv = TRUE
AND (p.nachname ILIKE ? OR p.vorname ILIKE ? OR b.uid ILIKE ? OR p.person_id::text LIKE ?)
ORDER BY p.nachname, p.vorname
',
array($query, $query, $query, $query)
);
if (isError($dataset)) $this->terminateWithError(getError($dataset));
$this->terminateWithSuccess($dataset);
}
}
@@ -78,32 +78,52 @@ class Dokumente extends FHCAPI_Controller
$this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Studiengang_kz']), self::ERROR_TYPE_GENERAL);
$resultPreDoc = $this->_getPrestudentDokumente($prestudent_id);
$arrayAccepted = [];
$person_id = $this->_getPersonId($prestudent_id);
$mergedArray = [];
$docNames = array_map(function ($item) {
return $item->dokument_kurzbz;
}, $resultPreDoc);
foreach ($resultPreDoc as $pre)
foreach($docNames as $doc)
{
$result = $this->AkteModel->getAktenFAS($person_id, $pre->dokument_kurzbz, $studiengang_kz, $prestudent_id, true);
$result = $this->AkteModel->getAktenFAS($person_id, $doc, $studiengang_kz, $prestudent_id, true);
if (isError($result))
{
return $this->terminateWithError($result, self::ERROR_TYPE_GENERAL);
}
if (hasData($result))
{
foreach (getData($result) as $doc)
$data = getData($result);
foreach ($data as $value)
{
$merged = clone $doc;
$merged->docdatum = $pre->docdatum;
$merged->insertvonma = $pre->insertvonma;
$merged->bezeichnung = $pre->bezeichnung;
$mergedArray[] = $merged;
array_push($arrayAccepted, $value);
}
}
else
{
$mergedArray[] = $pre;
}
//Mapping with document_kurzbz
$preDocMap = [];
foreach ($resultPreDoc as $pre) {
$preDocMap[$pre->dokument_kurzbz] = $pre;
}
$mergedArray = [];
foreach ($arrayAccepted as $doc) {
$merged = clone $doc;
if (isset($preDocMap[$doc->dokument_kurzbz])) {
$merged->docdatum = $preDocMap[$doc->dokument_kurzbz]->docdatum;
$merged->insertvonma = $preDocMap[$doc->dokument_kurzbz]->insertvonma;
$merged->bezeichnung = $preDocMap[$doc->dokument_kurzbz]->bezeichnung;
} else {
$merged->akzeptiertdatum = null;
$merged->akzeptiertvon = null;
}
$mergedArray[] = $merged;
}
$this->terminateWithSuccess($mergedArray);
@@ -48,8 +48,7 @@ class Konto extends FHCAPI_Controller
// Load language phrases
$this->loadPhrases([
'konto',
'lehre'
'konto'
]);
}
@@ -113,7 +112,7 @@ class Konto extends FHCAPI_Controller
*
* @return void
*/
public function getBuchungstypen($studiensemester_kurzbz = null)
public function getBuchungstypen()
{
$this->load->model('crm/Buchungstyp_model', 'BuchungstypModel');
@@ -123,7 +122,6 @@ class Konto extends FHCAPI_Controller
$data = $this->getDataOrTerminateWithError($result);
$this->_getOEHBeitrag($data, $studiensemester_kurzbz);
$this->terminateWithSuccess($data);
}
@@ -496,43 +494,4 @@ class Konto extends FHCAPI_Controller
$this->terminateWithSuccess();
}
private function _getOEHBeitrag(&$data, $studiensemester_kurzbz = null)
{
if (is_null($studiensemester_kurzbz))
{
$this->load->library('VariableLib', ['uid' => getAuthUID()]);
$studiensemester_akt = $this->variablelib->getVar('semester_aktuell');
}
else
{
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
if ($this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz))
$studiensemester_akt = $studiensemester_kurzbz;
else
$this->terminateWithError($this->p->t('lehre', 'error_noStudiensemester'));
}
$this->load->model('codex/Oehbeitrag_model', 'OehbeitragModel');
$oehBeitrag = $this->OehbeitragModel->getByStudiensemester($studiensemester_akt);
$oehStandardbetrag = null;
if (hasData($oehBeitrag))
{
$oeh = getData($oehBeitrag)[0];
$summe = ($oeh->studierendenbeitrag + $oeh->versicherung) * -1;
$oehStandardbetrag = number_format((float)$summe, 2, '.', '');
}
if ($oehStandardbetrag !== null)
{
$data = array_map(function ($buchungstyp) use ($oehStandardbetrag) {
if (isset($buchungstyp->buchungstyp_kurzbz) && (strtolower($buchungstyp->buchungstyp_kurzbz) === 'oeh'))
{
$buchungstyp->standardbetrag = $oehStandardbetrag;
}
return $buchungstyp;
}, $data);
}
}
}
+26 -69
View File
@@ -1,4 +1,20 @@
<?php
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (!defined('BASEPATH')) exit('No direct script access allowed');
@@ -8,12 +24,14 @@ if (!defined('BASEPATH')) exit('No direct script access allowed');
class Login extends FHC_Controller
{
/**
*
* Object initialization
*/
public function __construct()
{
parent::__construct();
}
{
parent::__construct(array(
'loginAs' => 'admin:rw'
));
}
/**
* Displays a login page with username and password
@@ -24,72 +42,11 @@ class Login extends FHC_Controller
}
/**
* Called with HTTP POST via ajax to login using the LDAP authentication
* Displays a login page with username and password
*/
public function loginLDAP()
{
$username = $this->input->post('username');
$password = $this->input->post('password');
$this->load->library('AuthLib', array(false)); // without authentication otherwise loooooop!
$login = $this->authlib->loginLDAP($username, $password);
if (isSuccess($login))
{
$this->outputJsonSuccess($this->authlib->getLandingPage()); // if login is success then retrieves the desired page
}
else
{
$this->outputJsonError(getCode($login)); // returns the error code
}
}
/**
* Called with HTTP POST via ajax to login as another user specified by uid
*/
public function loginASByUid()
{
$uid = $this->input->get('uid');
// With authentication -> you must be already logged to gain another identity
$this->load->library('AuthLib');
$loginAS = $this->authlib->loginASByUID($uid);
$this->outputJson($loginAS); // returns the error code
}
/**
* Called with HTTP POST via ajax to login as another user specified by person id
*/
public function loginASByPersonId()
{
$person_id = $this->input->get('person_id');
// With authentication -> you must be already logged to gain another identity
$this->load->library('AuthLib');
$loginAS = $this->authlib->loginASByPersonId($person_id);
if (isSuccess($loginAS))
{
$this->outputJsonSuccess(true); // obtained!
}
else
{
$this->outputJsonSuccess(getCode($loginAS)); // returns the error code
};
}
/**
* To login into the system with email and code as credentials
*/
public function emailCode()
{
}
/**
* To login into the system using SSO
*/
public function sso()
public function loginAs()
{
$this->load->view('system/login/loginAs');
}
}
-1
View File
@@ -417,7 +417,6 @@ abstract class Notiz_Controller extends FHCAPI_Controller
$notiz_id = $this->input->post('notiz_id');
$this->NotizModel->addSelect('campus.tbl_dms_version.*');
$this->NotizModel->addSelect($this->NotizModel->escape(base_url('content/notizdokdownload.php?id=')) . ' || public.tbl_notiz_dokument.dms_id AS preview');
$this->NotizModel->addJoin('public.tbl_notiz_dokument', 'ON (public.tbl_notiz_dokument.notiz_id = public.tbl_notiz.notiz_id)');
$this->NotizModel->addJoin('campus.tbl_dms_version', 'ON (public.tbl_notiz_dokument.dms_id = campus.tbl_dms_version.dms_id)');
-53
View File
@@ -263,59 +263,6 @@ class ProfilLib{
$element->mailto = "mailto:" . $element->gruppe_kurzbz . "@" . DOMAIN;
return $element;
}, $mailverteiler_res);
$this->ci->load->model("crm/Student_model", "StudentModel");
$this->ci->StudentModel->addJoin("tbl_benutzer", "tbl_benutzer.uid = tbl_student.student_uid");
$this->ci->StudentModel->addJoin("tbl_person", "tbl_benutzer.person_id = tbl_person.person_id");
$this->ci->StudentModel->addJoin("tbl_studiengang", "tbl_student.studiengang_kz = tbl_studiengang.studiengang_kz");
$this->ci->StudentModel->addSelect("matr_nr, semester, verband, gruppe, kurzbzlang");
$studentResult = $this->ci->StudentModel->loadWhere(["student_uid" => $uid]);
if (isError($studentResult)) {
return error(getData($studentResult));
}
$studentResultData = getData($studentResult);
$studentData = null;
if (is_array($studentResultData) && count($studentResultData)) {
$studentData = $studentResultData[0];
}
if ($studentData && $studentData->matr_nr) {
$this->ci->load->library("phrasesLib", ["profil"], "phrases");
$standardCourseVerteiler = trim($studentData->kurzbzlang) . "_STD";
$mailverteiler_res[] = [
"beschreibung" => $this->ci->phrases->t('profil', 'alleStudentenVon') . " " . $standardCourseVerteiler,
"gruppe_kurzbz" => $standardCourseVerteiler,
"mailto" => "mailto:" . strtolower($standardCourseVerteiler) . "@" . DOMAIN,
];
$semesterVerteiler = trim($studentData->kurzbzlang) . trim($studentData->semester);
$mailverteiler_res[] = [
"beschreibung" => $this->ci->phrases->t('profil', 'alleStudentenVon') . " " . $semesterVerteiler,
"gruppe_kurzbz" => $semesterVerteiler,
"mailto" => "mailto:" . strtolower($semesterVerteiler) . "@" . DOMAIN,
];
if ($studentData->verband && strlen(trim($studentData->verband))) {
$verbandVerteiler = $semesterVerteiler . trim($studentData->verband);
$mailverteiler_res[] = [
"beschreibung" => $this->ci->phrases->t('profil', 'alleStudentenVon') . " " . $verbandVerteiler,
"gruppe_kurzbz" => $verbandVerteiler,
"mailto" => "mailto:" . strtolower($verbandVerteiler) . "@" . DOMAIN,
];
if ($studentData->gruppe && strlen(trim($studentData->gruppe))) {
$gruppeVerteiler = $verbandVerteiler . trim($studentData->gruppe);
$mailverteiler_res[] = [
"beschreibung" => $this->ci->phrases->t('profil', 'alleStudentenVon') . " " . $gruppeVerteiler,
"gruppe_kurzbz" => $gruppeVerteiler,
"mailto" => "mailto:" . strtolower($gruppeVerteiler) . "@" . DOMAIN,
];
}
}
}
return $mailverteiler_res;
}
@@ -0,0 +1,37 @@
<?php
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
$includesArray = array(
'title' => 'Login',
'axios027' => true,
'bootstrap5' => true,
'fontawesome6' => true,
'vue3' => true,
'primevue3' => true,
'phrases' => array('uid', 'global'),
'navigationcomponent' => true,
'customJSModules' => array('public/js/LoginAs.js'),
);
$this->load->view('templates/FHC-Header', $includesArray);
?>
<div id="main"></div>
<?php $this->load->view('templates/FHC-Footer', $includesArray); ?>
@@ -1,44 +1,39 @@
<?php
$this->load->view(
'templates/FHC-Header',
array(
'title' => 'Login',
'jquery3' => true,
'jqueryui1' => true,
'bootstrap3' => true,
'fontawesome4' => true,
'sbadmintemplate3' => true,
'ajaxlib' => true,
'dialoglib' => true,
'customCSSs' => 'public/css/Login.css',
'customJSs' => 'public/js/Login.js'
)
);
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
$includesArray = array(
'title' => 'Login',
'axios027' => true,
'bootstrap5' => true,
'fontawesome6' => true,
'vue3' => true,
'filtercomponent' => true,
'navigationcomponent' => true,
'tabulator5' => true,
'primevue3' => true,
'phrases' => array('uid', 'global'),
'customJSModules' => array('public/js/Login.js'),
);
$this->load->view('templates/FHC-Header', $includesArray);
?>
<div id="main"></div>
<div class="login-form">
<?php $this->load->view('templates/FHC-Footer', $includesArray); ?>
<p class="text-center">
<img src="<?php echo base_url('public/images/logo-300x160.png'); ?>" >
</p>
<br>
<div class="form-group">
<input id="username" type="text" class="form-control" placeholder="Username" required="required">
</div>
<div class="form-group">
<input id="password" type="password" class="form-control" placeholder="Password" required="required">
</div>
<div class="form-group">
<button id="btnLogin" ype="submit" class="btn btn-primary btn-block">Log in</button>
</div>
<p class="text-center"><a href="#">Forgot Password?</a></p>
</div>
<?php $this->load->view('templates/FHC-Footer'); ?>
+1 -49
View File
@@ -25,7 +25,6 @@
*/
require_once(dirname(__FILE__).'/basis_db.class.php');
require_once(dirname(__FILE__).'/'.EXT_FKT_PATH.'/generateZahlungsreferenz.inc.php');
require_once(dirname(__FILE__).'/variable.class.php');
class konto extends basis_db
{
@@ -433,8 +432,6 @@ class konto extends basis_db
$qry.=" ORDER BY beschreibung";
$oehBeitrag = $this->_getOEHBeitrag();
if($this->db_query($qry))
{
while($row = $this->db_fetch_object())
@@ -443,15 +440,7 @@ class konto extends basis_db
$typ->buchungstyp_kurzbz = $row->buchungstyp_kurzbz;
$typ->beschreibung = $row->beschreibung;
if (strtolower($typ->buchungstyp_kurzbz) === 'oeh' && $oehBeitrag)
{
$typ->standardbetrag = $oehBeitrag;
}
else
{
$typ->standardbetrag = $row->standardbetrag;
}
$typ->standardbetrag = $row->standardbetrag;
$typ->standardtext = $row->standardtext;
$typ->credit_points = $row->credit_points;
$typ->aktiv = $this->db_parse_bool($row->aktiv);
@@ -1001,43 +990,6 @@ class konto extends basis_db
return false;
}
}
private function _getOEHBeitrag()
{
if(!is_user_logged_in())
{
return false;
}
$variablen_obj = new variable();
$variablen_obj->loadVariables(get_uid());
$qry = "WITH semstart AS (
SELECT start FROM public.tbl_studiensemester
WHERE studiensemester_kurzbz = '". $this->db_escape($variablen_obj->variable->semester_aktuell) . "'
)
SELECT * FROM bis.tbl_oehbeitrag oehb
JOIN public.tbl_studiensemester semvon ON oehb.von_studiensemester_kurzbz = semvon.studiensemester_kurzbz
LEFT JOIN public.tbl_studiensemester sembis ON oehb.bis_studiensemester_kurzbz = sembis.studiensemester_kurzbz
JOIN semstart ON semstart.start::date >= semvon.start::date AND (sembis.studiensemester_kurzbz IS NULL OR semstart.start::date <= sembis.start::date)
ORDER BY semvon.start
LIMIT 1";
if ($this->db_query($qry))
{
if($row = $this->db_fetch_object())
{
$summe = ($row->studierendenbeitrag + $row->versicherung) * -1;
return number_format((float)$summe, 2, '.', '');
}
return false;
}
else
{
$this->errormsg = 'Fehler bei der Abfrage aufgetreten';
return false;
}
}
}
?>
+4
View File
@@ -197,6 +197,10 @@ html.fs_huge {
margin-bottom: -1px;
}
.tiny-90 div.tox.tox-tinymce {
height: 90% !important;
}
/* slim begin */
.stv .form-label {
margin-bottom: .15rem;
+73 -49
View File
@@ -1,55 +1,79 @@
/**
* To login via LDAP
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
function loginLDAP()
{
// Ajax call to login with LDAP
FHC_AjaxClient.ajaxCallPost(
"system/Login/loginLDAP",
{
username: $("#username").val(),
password: $("#password").val()
},
{
successCallback: function(data, textStatus, jqXHR) {
if (FHC_AjaxClient.isError(data))
{
if (FHC_AjaxClient.getError(data) == 10)
import PluginsPhrasen from '../js/plugins/Phrasen.js';
import ApiLogin from '../js/api/factory/login.js';
const loginApp = Vue.createApp({
data: function() {
return {
username: '',
password: ''
};
},
components: {
},
methods: {
loginLDAP: function() {
this.$api
.call(ApiLogin.loginLDAP({
username: this.username,
password: this.password
}))
.then((response) => {
// If property data exists
if (Object.hasOwn(response, 'data'))
{
FHC_DialogLib.alertError("Username not foud");
// If property data is a string
if (typeof response.data === 'string' || response.data instanceof String)
{
// If property data is a valid URL
try {
let url = new URL(response.data);
// If here the URL contained in response.data is fine
// and can be used to switch to the landing page
document.location.href = response.data;
} catch (_) {}
}
}
if (FHC_AjaxClient.getError(data) == 2)
{
FHC_DialogLib.alertError("Wrong password");
}
}
else
{
$(location).attr("href", FHC_AjaxClient.getData(data));
}
},
errorCallback: function(jqXHR, textStatus, errorThrown) {
FHC_DialogLib.alertError(textStatus);
}
}
);
}
/**
* When JQuery is up
*/
$(document).ready(function() {
$("#btnLogin").click(loginLDAP);
$("#username").keydown(function(e) {
if (e.keyCode == 13) loginLDAP();
})
$("#password").keydown(function(e) {
if (e.keyCode == 13) loginLDAP();
})
})
.catch((error) => {
console.error(error);
});
}
},
template: `
<div class="d-flex align-items-center justify-content-center">
<div>
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" name="username" id="username" v-model="username">
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" name="password" id="password" v-model="password">
</div>
<div class="d-flex align-items-center justify-content-center">
<button type="button" class="btn btn-primary" @click="loginLDAP">Login</button>
</div>
</div>
</div>
`
});
loginApp.use(PluginsPhrasen).mount('#main');
+168
View File
@@ -0,0 +1,168 @@
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import PluginsPhrasen from '../js/plugins/Phrasen.js';
import ApiLogin from '../js/api/factory/login.js';
import {CoreNavigationCmpt} from '../js/components/navigation/Navigation.js';
import PvAutoComplete from "../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js";
const loginAsApp = Vue.createApp({
data: function() {
return {
person_id: '',
username: '',
surname: '',
name: '',
selectedUser: null,
filteredUsers: [],
appSideMenuEntries: {}
};
},
components: {
CoreNavigationCmpt,
PvAutoComplete
},
methods: {
loginAs: function() {
if (this.selectedUser != null)
{
this.$api
.call(ApiLogin.loginASByUid({
uid: this.selectedUser.uid
}))
.then((response) => {
location.reload();
})
.catch((error) => {
console.error(error);
});
}
},
logout: function() {
this.$api
.call(ApiLogin.logout())
.then((response) => {
location.reload();
})
.catch((error) => {
console.error(error);
});
},
searchUser: function(event) {
if (event.query.length >= 3)
{
this.$api
.call(ApiLogin.searchUser(event.query))
.then(result => {
this.filteredUsers = result.data.retval;
})
.catch((error) => {
console.error(error);
});
}
}
},
mounted: function() {
this.$api
.call(ApiLogin.whoAmI())
.then((response) => {
// If property data exists
if (Object.hasOwn(response, 'data'))
{
if (response.data != null && Object.hasOwn(response.data, 'person_id'))
{
this.person_id = response.data.person_id;
this.username = response.data.username;
this.surname = response.data.surname;
this.name = response.data.name;
}
else
{
this.person_id = 'Not logged';
this.username = 'Not logged';
this.surname = 'Not logged';
this.name = 'Not logged';
}
}
})
.catch((error) => {
console.error(error);
});
},
template: `
<!-- Navigation component -->
<core-navigation-cmpt v-bind:add-side-menu-entries="appSideMenuEntries"></core-navigation-cmpt>
<div style="width: 700px !important">
<div class="card" style="padding: 20px;">
<div class="mb-3">
Who am I?
</div>
<div class="mb-3">
<div class="d-inline-flex align-items-center">
<label for="person_id" class="form-label" style="width: 150px !important">Person ID</label>
<input type="text" style="width: 400px !important" disabled="disabled" class="form-control" id="person_id" v-model="person_id">
</div>
</div>
<div class="mb-3">
<div class="d-inline-flex align-items-center">
<label for="username" class="form-label" style="width: 150px !important">UID</label>
<input type="text" style="width: 400px !important" disabled="disabled" class="form-control" id="username" v-model="username">
</div>
</div>
<div class="mb-3">
<div class="d-inline-flex align-items-center">
<label for="surname" class="form-label" style="width: 150px !important">Surname</label>
<input type="text" style="width: 400px !important" disabled="disabled" class="form-control" id="surname" v-model="surname">
</div>
</div>
<div class="mb-3">
<div class="d-inline-flex align-items-center">
<label for="name" class="form-label" style="width: 150px !important">Name</label>
<input type="text" style="width: 400px !important" disabled="disabled" class="form-control" id="name" v-model="name">
</div>
</div>
<div class="d-flex align-items-center justify-content-center">
<button type="button" class="btn btn-primary" @click="logout">Logout</button>
</div>
</div>
<div class="card" style="padding: 20px;">
<div class="mb-3">
Who I want to be?
</div>
<div class="mb-3">
<div class="d-inline-flex align-items-center">
<PvAutoComplete inputStyle="width: 600px;" v-model="selectedUser" optionLabel="label" :suggestions="filteredUsers" @complete="searchUser" placeholder="Search user..." />
</div>
</div>
<div class="d-flex align-items-center justify-content-center">
<button type="button" class="btn btn-primary" @click="loginAs">Login as</button>
</div>
</div>
</div>
`
});
loginAsApp.
use(PluginsPhrasen).
use(primevue.config.default, {
zIndex: {
overlay: 1100
}
}).
mount('#main');
+59
View File
@@ -0,0 +1,59 @@
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export default {
loginLDAP(params) {
return {
method: 'post',
url: '/api/frontend/v1/Login/loginLDAP',
params
};
},
loginASByUid(params) {
return {
method: 'post',
url: '/api/frontend/v1/Login/loginASByUid',
params
};
},
loginASByPersonId(params) {
return {
method: 'post',
url: '/api/frontend/v1/Login/loginASByPersonId',
params
};
},
whoAmI() {
return {
method: 'get',
url: '/api/frontend/v1/Login/whoAmI'
};
},
logout() {
return {
method: 'get',
url: '/system/Logout'
};
},
searchUser(query) {
return {
method: 'get',
url: '/api/frontend/v1/Login/searchUser?query=' + encodeURIComponent(query)
};
}
};
+3 -15
View File
@@ -38,10 +38,6 @@ export default {
};
},
insert(params) {
if(params.betrag)
{
params.betrag = params.betrag.replace(',', '.');
}
return {
method: 'post',
url: 'api/frontend/v1/stv/konto/insert',
@@ -56,10 +52,6 @@ export default {
};
},
edit(params) {
if(params.betrag)
{
params.betrag = params.betrag.replace(',', '.');
}
return {
method: 'post',
url: 'api/frontend/v1/stv/konto/update',
@@ -73,14 +65,10 @@ export default {
params: { buchungsnr }
};
},
getBuchungstypen(studiensemester_kurzbz) {
let url = 'api/frontend/v1/stv/konto/getBuchungstypen'
if (!!studiensemester_kurzbz)
url = url + '/' + encodeURIComponent(studiensemester_kurzbz);
getBuchungstypen() {
return {
method: 'get',
url: url
url: 'api/frontend/v1/stv/konto/getBuchungstypen'
};
},
}
};
@@ -63,7 +63,7 @@ export default {
const vm = this;
tinymce.init({
target: this.$refs.editor.$refs.input, //Important: not selector: to enable multiple import of component
min_height: 300,
//height: 800,
//plugins: ['lists'],
toolbar: 'styleselect | bold italic underline | alignleft aligncenter alignright alignjustify | link',
plugins: 'link',
@@ -313,7 +313,7 @@ export default {
<div class="row">
<div class="col-sm-8">
<form-form class="row g-3 mt-2 align-content-start" ref="formMessage">
<form-form class="row g-3 mt-2 h-100" ref="formMessage">
<div class="row mb-3">
@@ -338,7 +338,7 @@ export default {
</div>
<!--Tiny MCE-->
<div class="row mb-3 tiny-90">
<div class="row mb-3 h-100 tiny-90">
<form-input
ref="editor"
:label="$p.t('global','nachricht') + ' *'"
@@ -62,7 +62,7 @@ export default {
const vm = this;
tinymce.init({
target: this.$refs.editor.$refs.input, //Important: not selector: to enable multiple import of component
min_height: 300,
//height: 800,
//plugins: ['lists'],
toolbar: 'styleselect | bold italic underline | alignleft aligncenter alignright alignjustify | link',
plugins: 'link',
@@ -30,7 +30,6 @@ export default {
personId: null,
layoutColumnsOnNewData: false,
height: '400',
arePhrasesLoaded: false
}
},
methods: {
@@ -196,7 +195,7 @@ export default {
],
formatter: (cell, formatterParams) => {
const key = formatterParams[cell.getValue()];
return this.$p?.t?.('messages', key) || key;
return this.$p.t('messages', key);
},
},
{
@@ -306,6 +305,8 @@ export default {
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['global', 'person', 'stv', 'messages', 'ui', 'notiz']);
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
@@ -356,12 +357,6 @@ export default {
});*/
},
created(){
this.$p
.loadCategory(['global', 'person', 'stv', 'messages', 'ui', 'notiz'])
.then(() => {
this.arePhrasesLoaded = true;
});
if(this.typeId != 'person_id' && Array.isArray(this.id) && this.id.length === 1) {
const params = {
id: this.id,
@@ -386,7 +381,6 @@ export default {
<!--table-->
<div class="col-sm-6 pt-1">
<core-filter-cmpt
v-if="arePhrasesLoaded"
ref="table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
@@ -419,7 +413,6 @@ export default {
<div class="col-sm-12 pt-6">
<core-filter-cmpt
ref="table"
v-if="arePhrasesLoaded"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
@@ -83,8 +83,6 @@ export default {
});
},
open() {
this.getBuchungstypen(this.currentSemester);
this.data = {
buchungstyp_kurzbz: '',
betrag: '-0.00',
@@ -107,7 +105,7 @@ export default {
const text = typ.standardtext || '';
const creditpoints = typ.credit_points || '';
if (!this.data.betrag || this.data.betrag == '-0.00' || this.data.betrag !== amount)
if (!this.data.betrag || this.data.betrag == '-0.00')
this.data.betrag = amount;
if (!this.data.buchungstext)
@@ -115,18 +113,7 @@ export default {
if (this.config.showCreditpoints && (this.data.credit_points == '0.00' || this.data.credit_points === null))
this.data.credit_points = creditpoints;
},
getBuchungstypen(studiensemester_kurzbz)
{
this.$api
.call(ApiKonto.getBuchungstypen(studiensemester_kurzbz))
.then(result => {
this.lists.buchungstypen = result.data;
if (this.data.buchungstyp_kurzbz)
this.checkDefaultBetrag(this.data.buchungstyp_kurzbz);
})
.catch(this.$fhcAlert.handleSystemError);
},
}
},
template: `
<core-form ref="form" class="stv-details-konto-edit" @submit.prevent="save">
@@ -179,7 +166,6 @@ export default {
<form-input
type="select"
v-model="data.studiensemester_kurzbz"
@change="getBuchungstypen(data.studiensemester_kurzbz)"
name="studiensemester_kurzbz"
:label="$p.t('lehre/studiensemester')"
>
+13 -13
View File
@@ -41,8 +41,8 @@ export default {
),
ajaxResponse: (url, params, response) => response.data,
columns: [
{title: "Typ", field: "type", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title: "Betrag", field: "betrag", headerFilter: true,
{title: "Typ", field: "type"},
{title: "Betrag", field: "betrag",
formatter: function(cell) {
let value = cell.getValue();
if (value == null) {
@@ -51,14 +51,14 @@ export default {
return parseFloat(value).toFixed(2);
}
},
{title: "Bezeichnung", field: "bezeichnung", headerFilter: true},
{title: "Studiensemester", field: "studiensemester_kurzbz", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title: "Pruefung_id", field: "pruefung_id", visible: false, headerFilter: true},
{title: "mitarbeiter_uid", field: "mitarbeiter_uid", visible: false, headerFilter: true},
{title: "projektarbeit_id", field: "projektarbeit_id", visible: false, headerFilter: true},
{title: "lehreinheit_id", field: "lehreinheit_id", visible: true, headerFilter: true},
{title: "betreuerart_kurzbz", field: "betreuerart_kurzbz", visible: false, headerFilter: true},
{title: "vertrag_id", field: "vertrag_id", visible: false, headerFilter: true}, //just for testing
{title: "Bezeichnung", field: "bezeichnung"},
{title: "Studiensemester", field: "studiensemester_kurzbz"},
{title: "Pruefung_id", field: "pruefung_id", visible: false},
{title: "mitarbeiter_uid", field: "mitarbeiter_uid", visible: false},
{title: "projektarbeit_id", field: "projektarbeit_id", visible: false},
{title: "lehreinheit_id", field: "lehreinheit_id", visible: true},
{title: "betreuerart_kurzbz", field: "betreuerart_kurzbz", visible: false},
{title: "vertrag_id", field: "vertrag_id", visible: false}, //just for testing
{
title: 'Aktionen', field: 'actions',
minWidth: 50,
@@ -110,10 +110,10 @@ export default {
],
layout: 'fitColumns',
layoutColumnsOnNewData: false,
height: '250',
height: '200',
selectableRowsRangeMode: 'click',
selectableRows: true,
persistenceID: 'core-contracts-details-2026050501'
persistenceID: 'core-contracts-details-2026021701'
},
tabulatorEvents: [
{
@@ -137,7 +137,7 @@ export default {
setHeader('type', this.$p.t('global', 'typ'));
setHeader('bezeichnung', this.$p.t('ui', 'bezeichnung'));
setHeader('lehreinheit_id', this.$p.t('lehre', 'lehreinheit_id'));
setHeader('lehreinheit_id', this.$p.t('ui', 'lehreinheit_id'));
setHeader('betrag', this.$p.t('ui', 'betrag'));
setHeader('studiensemester_kurzbz', this.$p.t('lehre', 'studiensemester'));
setHeader('mitarbeiter_uid', this.$p.t('ui', 'mitarbeiter_uid'));
+8 -11
View File
@@ -47,13 +47,12 @@ export default {
this.endpoint.getStatiOfContract(this.person_id, this.vertrag_id)
),
ajaxResponse: (url, params, response) => response.data,
persistenceID: 'core-contracts-status-2026050501',
persistenceID: 'core-contracts-status-2026021701',
columns: [
{title: "Status", field: "bezeichnung", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title: "Status", field: "bezeichnung"},
{
title: "Datum",
field: "datum",
headerFilter: true,
formatter: function (cell) {
const dateStr = cell.getValue();
const date = new Date(dateStr); // Convert to Date object
@@ -67,15 +66,14 @@ export default {
});
}
},
{title: "vertrag_id", field: "vertrag_id", visible: false, headerFilter: true},
{title: "Vertragsstatus", field: "vertragsstatus_kurzbz", visible: false, headerFilter: true},
{title: "User", field: "mitarbeiter_uid", visible: false, headerFilter: true},
{title: "insertvon", field: "insertvon", visible: false, headerFilter: true},
{title: "vertrag_id", field: "vertrag_id", visible: false},
{title: "Vertragsstatus", field: "vertragsstatus_kurzbz", visible: false},
{title: "User", field: "mitarbeiter_uid", visible: false},
{title: "insertvon", field: "insertvon", visible: false},
{
title: "insertamum",
field: "insertamum",
visible: false,
headerFilter: true,
formatter: function (cell) {
const dateStr = cell.getValue();
const date = new Date(dateStr);
@@ -89,12 +87,11 @@ export default {
});
}
},
{title: "updatevon", field: "updatevon", visible: false, headerFilter: true},
{title: "updatevon", field: "updatevon", visible: false},
{
title: "updateamum",
field: "updateamum",
visible: false,
headerFilter: true,
formatter: function (cell) {
const dateStr = cell.getValue();
const date = new Date(dateStr);
@@ -151,7 +148,7 @@ export default {
],
layout: 'fitColumns',
layoutColumnsOnNewData: false,
height: '250',
height: '200',
selectableRowsRangeMode: 'click',
selectableRows: true,
},
@@ -30,11 +30,10 @@ export default {
),
ajaxResponse: (url, params, response) => response.data,
columns: [
{title: "Typ", field: "type", width: 100, headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title: "Typ", field: "type", width: 100},
{
title: "Betrag",
field: "betrag1",
headerFilter: true,
formatter: function(cell) {
let value = cell.getValue();
if (value == null) {
@@ -42,29 +41,28 @@ export default {
}
return parseFloat(value).toFixed(2);
}},
{title: "Bezeichnung", field: "bezeichnung", width: 150, headerFilter: true},
{title: "Studiensemester", field: "studiensemester_kurzbz", width: 160, headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title: "mitarbeiter_uid", field: "mitarbeiter_uid", visible: false, headerFilter: true},
{title: "projektarbeit_id", field: "projektarbeit_id", visible: false, headerFilter: true},
{title: "lehreinheit_id", field: "lehreinheit_id", visible: true, headerFilter: true},
{title: "betreuerart_kurzbz", field: "betreuerart_kurzbz", visible: false, headerFilter: true},
{title: "Vertragsstunden", field: "vertragsstunden", visible: false, headerFilter: true},
{title: "vertrag_id", field: "vertrag_id", visible: false, headerFilter: true}, //just for testing
{title: "Bezeichnung", field: "bezeichnung", width: 150},
{title: "Studiensemester", field: "studiensemester_kurzbz", width: 160},
{title: "mitarbeiter_uid", field: "mitarbeiter_uid", visible: false},
{title: "projektarbeit_id", field: "projektarbeit_id", visible: false},
{title: "lehreinheit_id", field: "lehreinheit_id", visible: true},
{title: "betreuerart_kurzbz", field: "betreuerart_kurzbz", visible: false},
{title: "Vertragsstunden", field: "vertragsstunden", visible: false},
{title: "vertrag_id", field: "vertrag_id", visible: false}, //just for testing
{
title: "VertragsstundenStudiensemester",
field: "vertragsstunden_studiensemester_kurzbz",
visible: false,
headerFilter: true
visible: false
},
],
layout: 'fitColumns',
layoutColumnsOnNewData: false,
height: 250,
height: 150,
selectableRowsRangeMode: 'click',
selectableRows: true,
selectableRowsRollingSelection: false, //only allow multiselect with STRG
index: "lehreinheit_id",
persistenceID: 'core-contracts-unassigned-2026050501'
persistenceID: 'core-contracts-unassigned-2026021701'
},
tabulatorEvents: [
{
@@ -102,7 +100,7 @@ export default {
setHeader('type', this.$p.t('global', 'typ'));
setHeader('bezeichnung', this.$p.t('ui', 'bezeichnung'));
setHeader('lehreinheit_id', this.$p.t('lehre', 'lehreinheit_id'));
setHeader('lehreinheit_id', this.$p.t('ui', 'lehreinheit_id'));
setHeader('betrag1', this.$p.t('ui', 'betrag'));
setHeader('studiensemester_kurzbz', this.$p.t('lehre', 'studiensemester'));
setHeader('mitarbeiter_uid', this.$p.t('ui', 'mitarbeiter_uid'));
+137 -26
View File
@@ -20,6 +20,9 @@ export default {
ContractStati
},
inject: {
/* cisRoot: {
from: 'cisRoot'
},*/
hasSchreibrechte: {
from: 'hasSchreibrechte',
default: false
@@ -51,9 +54,9 @@ export default {
),
ajaxResponse: (url, params, response) => response.data,
columns: [
{title: "Bezeichnung", field: "bezeichnung", width: 300, headerFilter: true},
{title: "Bezeichnung", field: "bezeichnung", width: 300},
{
title: "Betrag", field: "betrag", width: 100, headerFilter: true,
title: "Betrag", field: "betrag", width: 100,
formatter: function (cell) {
let value = cell.getValue();
@@ -63,13 +66,12 @@ export default {
return parseFloat(value).toFixed(2);
}
},
{title: "Vertragstyp", field: "vertragstyp_bezeichnung", width: 125, headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title: "Status", field: "status", width: 100, headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title: "Vertragstyp", field: "vertragstyp_bezeichnung", width: 125},
{title: "Status", field: "status", width: 100},
{
title: "Vertragsdatum",
field: "vertragsdatum",
width: 128,
headerFilter: true,
formatter: function (cell) {
const dateStr = cell.getValue();
const date = new Date(dateStr);
@@ -80,11 +82,11 @@ export default {
});
}
},
{title: "VertragId", field: "vertrag_id", visible: false, headerFilter: true},
{title: "Vertragsstunden", field: "vertragsstunden", visible: false, headerFilter: true},
{title: "VertragsstundenStudiensemester", field: "vertragsstunden_studiensemester_kurzbz", visible: false, headerFilter: true},
{title: "Anmerkung", field: "anmerkung", visible: false, headerFilter: true},
{title: "isAbgerechnet", field: "isabgerechnet", visible: false, headerFilter: true},
{title: "VertragId", field: "vertrag_id", visible: false},
{title: "Vertragsstunden", field: "vertragsstunden", visible: false},
{title: "VertragsstundenStudiensemester", field: "vertragsstunden_studiensemester_kurzbz", visible: false},
{title: "Anmerkung", field: "anmerkung", visible: false},
{title: "isAbgerechnet", field: "isabgerechnet", visible: false},
{
title: 'Aktionen', field: 'actions',
minWidth: 150,
@@ -138,13 +140,11 @@ export default {
columns: true,
filter: false //to avoids js errors
},
persistenceID: 'core-contracts-2026050501',
persistenceID: 'core-contracts-2026021701',
};
return options;
},
tabulatorEvents() {
const vm = this;
const events = [
{
event: 'tableBuilt',
@@ -177,11 +177,28 @@ export default {
setHeader('actions', this.$p.t('global', 'aktionen'));
}
},
/* {
//is just enabled for ADDON Injection KU: MultiprintHonorarvertrag
//(maybe enable also for ADDON FH Burgenland: MultiAccept later)
event: 'rowClick',
handler: (e, row) => {
if (this.dataPrintHonorar != null && this.dataPrintHonorar.multiselect != null) {
const selectedContract = row.getData().vertrag_id;
const status = row.getData().status;
const bezeichnung = row.getData().bezeichnung;
this.toggleRowClick(selectedContract, status, bezeichnung);
}
}
},*/
{
event: 'rowClick',
handler: function (e, row) {
handler: (e, row) => {
if (!this.dataPrintHonorar?.multiselect) return;
const { vertrag_id, status, bezeichnung, vertragstyp_bezeichnung } = row.getData();
vm.toggleRowClick(e, vertrag_id, status, bezeichnung, vertragstyp_bezeichnung);
this.toggleRowClick(e, vertrag_id, status, bezeichnung, vertragstyp_bezeichnung);
}
},
{
@@ -225,6 +242,8 @@ export default {
person_id() {
this.$refs.table.reloadTable();
this.arraySelectedContracts = [];
/* if(this.dataPrintHonorar?.multiselect)
this.dataPrintHonorar.multiselect = [];*/
},
},
methods: {
@@ -251,6 +270,7 @@ export default {
)
.then(result => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
//window.scrollTo(0, 0);
this.reload();
this.contractSelected.vertrag_id = null;
})
@@ -498,9 +518,19 @@ export default {
'content/pdfExport.php?xml=' + this.dataPrintHonorar.xml + '&xsl=' + this.dataPrintHonorar.xsl + '&mitarbeiter_uid=' + this.mitarbeiter_uid + vertragString + '&output=pdf&uid=' + this.mitarbeiter_uid;
window.open(linkToPdf, '_blank');
},
/* toggleRowClick(contractId, status, bezeichnung) {
const index = this.arraySelectedContracts.findIndex(
([id]) => id === contractId
);
if (index !== -1) {
this.arraySelectedContracts.splice(index, 1);
} else {
this.arraySelectedContracts.push([contractId, status, bezeichnung]);
}
},*/
toggleRowClick(event, vertrag_id, status, bezeichnung, vertragstyp_bezeichnung) {
if (!this.dataPrintHonorar?.multiselect) return;
const isMulti = this.dataPrintHonorar?.multiselect === true;
const isCtrl = event.ctrlKey || event.metaKey;
const entry = {
@@ -510,29 +540,28 @@ export default {
vertragstyp_bezeichnung
};
// allow MultiSelect just in case event multiActionPrintHonorarvertrag
const allowMultiClick = isMulti && isCtrl;
if (!allowMultiClick) {
// Single click
if (!isCtrl) {
this.arraySelectedContracts = [entry];
//just mark last selected row as selected
this.$refs.table.tabulator.deselectRow();
this.$refs.table.tabulator.selectRow(vertrag_id);
return;
}
// CTRL / CMD → toggle
const index = this.arraySelectedContracts.findIndex(
e => e.vertrag_id === vertrag_id
);
if (index === -1) {
this.arraySelectedContracts.push(entry);
//this.arraySelectedContracts.push([entry.vertrag_id, entry.status, entry.bezeichnung, entry.vertragstyp_bezeichnung]);
} else {
this.arraySelectedContracts.splice(index, 1);
}
}
},
/* clearSelection(){
this.arraySelectedContracts = [];
this.$refs.table.tabulator.deselectRow();
}*/
},
created() {
Promise.all([
@@ -558,6 +587,88 @@ export default {
});
this.getFormattedDate();
},
/*
TODO(Manu) delete after check
<div class="row mb-3">
<form-input
type="DatePicker"
:label="$p.t('vertrag/datum_vertrag')"
name="vertragsdatum"
v-model="formData.vertragsdatum"
auto-apply
:enable-time-picker="false"
format="dd.MM.yyyy"
preview-format="dd.MM.yyyy"
:teleport="true"
>
</form-input>
</div>
<div class="row mb-3">
<form-input
type="text"
:label="$p.t('ui/bezeichnung')"
name="bezeichnung"
v-model="formData.bezeichnung"
>
</form-input>
</div>
<div class="row mb-3">
<form-input
type="select"
:label="$p.t('global/typ')"
v-model="formData.vertragstyp_kurzbz"
name="vertragstyp_kurzbz"
>
<option :value="null">-- {{$p.t('fehlermonitoring', 'keineAuswahl')}} --</option>
<option
v-for="entry in listContractTypes"
:key="entry.vertragstyp_kurzbz"
:value="entry.vertragstyp_kurzbz"
>
{{entry.bezeichnung}}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
:label="$p.t('ui/betrag')"
name="betrag"
v-model="formData.betrag"
>
</form-input>
</div>
<div class="row mb-3" v-if="!statusNew">
<form-input
type="text"
:label="$p.t('ui/stunden') + ' (' + $p.t('vertrag/vertrag_urfassung')+ ')'"
name="vertragsstunden"
v-model="formData.vertragsstunden"
disabled
>
</form-input>
</div>
<div class="row mb-3" v-if="!statusNew">
<form-input
type="text"
:label="$p.t('lehre/studiensemester') + ' (' + $p.t('vertrag/vertrag_urfassung')+ ')'"
name="vertragsstunden_studiensemester_kurzbz"
v-model="formData.vertragsstunden_studiensemester_kurzbz"
disabled
>
</form-input>
</div>
<div class="row mb-3">
<form-input
type="textarea"
:label="$p.t('global/anmerkung')"
name="anmerkung"
v-model="formData.anmerkung"
>
</form-input>
</div>
*/
template: `
<div class="core-contracts h-100 d-flex flex-column">
+2 -23
View File
@@ -29562,8 +29562,7 @@ array(
'insertvon' => 'system'
)
)
),
array(
), array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'sem_short',
@@ -29583,26 +29582,6 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'alleStudentenVon',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Alle StudentInnen aus',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'All students from',
'description' => '',
'insertvon' => 'system'
)
)
),
//Profil Phrasen ende
// LvPlan Phrasen start
array(
@@ -41782,7 +41761,7 @@ array(
array(
'app' => 'core',
'category' => 'abgabetool',
'phrase' => 'c4fehlerAktualitaetProjektarbeit',
'phrase' => 'c4fehlerAktualitaetProjektarbeit ',
'insertvon' => 'system',
'phrases' => array(
array(