Compare commits

...

134 Commits

Author SHA1 Message Date
adisposkofh 5592b69dc0 Merge branch 'cis40_2026-02_rc' into feature-76669/optimizing-menu-load 2026-04-28 13:00:27 +02:00
adisposkofh fc1303affd moved menu url to backend; minimized getMenu response size 2026-04-28 09:25:32 +02:00
chfhtw 863aaf62ec provide default value for inject 2026-04-27 16:26:51 +02:00
chfhtw 293352ac3c move apps/Cis.js -> apps/Cis/Cis.js 2026-04-27 16:26:32 +02:00
chfhtw 736e91224e show phrase instead of content for bookmark widget in admin mode 2026-04-27 15:43:58 +02:00
chfhtw 9bc564dbf9 min size & delete loading id when adding widgets 2026-04-27 15:35:29 +02:00
chfhtw 6d0ab0d4aa bugfix: empty data fields cause error in gridlogic (this happens when items are rearranged but not processed in index order) 2026-04-27 15:20:24 +02:00
chfhtw 7ae34e0640 hide everything in adminMode 2026-04-27 11:04:46 +02:00
chfhtw e9b5438a24 code quality & cleanup 2026-04-27 11:01:37 +02:00
chfhtw 51be318edc replace Api calls with UrlStore 2026-04-27 10:26:48 +02:00
chfhtw ca79b0c4d8 make sharedFiltered a computed and rename it to filteredBookmarks & get rid of unnecessary functions 2026-04-27 10:15:44 +02:00
chfhtw dfe05cbfa8 make tagsArrayAC a computed and rename it to availableTags 2026-04-27 10:03:36 +02:00
chfhtw 9109ead81c bogfix: missing primevue config in dashboard admin app 2026-04-27 09:44:11 +02:00
chfhtw 746f8bc736 bugfix: overwriting const 2026-04-27 09:37:59 +02:00
chfhtw 9c03b89ab5 get rid of tagsArrayMS and filterInput because they are unnecessary 2026-04-27 09:37:33 +02:00
chfhtw 5798e960a2 Use config update for widgets as intended instead of a proprietary solution 2026-04-27 09:37:17 +02:00
chfhtw 98730ce612 Overwrite config in update because otherwise deleted fields would be overwritten by the previous state 2026-04-27 09:02:58 +02:00
Andreas Österreicher 552faefa51 Merge branch 'feature-76108/microdegree_abschlussurkunde' 2026-04-27 08:55:08 +02:00
chfhtw 804716edb2 remove unused inject 2026-04-24 11:27:46 +02:00
chfhtw 332a47475e fix template 2026-04-24 11:25:36 +02:00
chfhtw 132edce701 code quality 2026-04-24 11:24:25 +02:00
chfhtw 6ce0cf6209 remove unused function 2026-04-24 10:56:00 +02:00
adisposkofh 53bce69c6f added missing template fallback to lv plan if no event 2026-04-23 17:53:40 +02:00
adisposkofh 20c68d675c added missing roomsearch input spacing 2026-04-23 17:45:09 +02:00
adisposkofh dfc8fdf44f added missing action buttons to search results 2026-04-23 17:33:55 +02:00
adisposkofh 4a26d7a89a minor fix 2026-04-23 16:58:54 +02:00
chfhtw 16f57a1bce bugfixes 2026-04-23 16:37:51 +02:00
Harald Bamberger 1cf3c18841 Merge branch 'feature-68530/Dashboard_Cleanup_Admin' into cis40_2026-02_rc 2026-04-23 16:34:59 +02:00
Harald Bamberger 1dd9ff0daf Merge branch 'feature-76385/mobile-optimization-lv-plan' into cis40_2026-02_rc 2026-04-23 16:33:24 +02:00
Harald Bamberger ff876afbb3 Merge branch 'feature-76464/reworking-use-of-controller-viewdata' into cis40_2026-02_rc 2026-04-23 14:20:19 +02:00
adisposkofh e89ab9b92f minor naming change 2026-04-23 13:57:27 +02:00
Harald Bamberger 4e59173d6c Merge branch 'feature-76385/mobile-optimization-sidenav-covers-other-elements' into cis40_2026-02_rc 2026-04-23 13:26:04 +02:00
Harald Bamberger df28d7331b Merge branch 'feature-76385/mobile-optimization-profile-bottom-spacing' into cis40_2026-02_rc 2026-04-23 13:25:18 +02:00
Harald Bamberger 2d0a2f3024 Merge branch 'feature-76385/mobile-optimization-news-page' into cis40_2026-02_rc 2026-04-23 13:24:25 +02:00
Harald Bamberger 781e145721 Merge branch 'feature-76385/mobile-optimization-header' into cis40_2026-02_rc 2026-04-23 13:20:31 +02:00
Harald Bamberger e984425e36 Merge branch 'feature-68530/Dashboard_Cleanup_Admin' into cis40_2026-02_rc 2026-04-23 12:09:09 +02:00
adisposkofh 2eb58e3346 reworked how 'compactible event types' are defined 2026-04-23 11:43:25 +02:00
Harald Bamberger ab4039dbbd Merge branch 'feature-69105/CIS4_MeineLVs_AnzeigeZeugnisnote_Und_Durchschnitt' into cis40_2026-02_rc 2026-04-23 11:24:37 +02:00
Harald Bamberger 2c1702a20b Merge branch 'feature-68957/CIS4_Dashboard_Bookmark_Widget_Sort_Tags' into cis40_2026-02_rc 2026-04-23 11:19:17 +02:00
Harald Bamberger 72a5f35b0e Merge branch 'feature-62450/Studium_Phrasen' into cis40_2026-02_rc 2026-04-23 11:16:16 +02:00
Harald Bamberger 076ae15abd Merge branch 'bug-75998/menuepunkt_fallback_wenn_die_sprache_nicht_uebereinstimmt' into cis40_2026-02_rc 2026-04-23 11:03:44 +02:00
Harald Bamberger 2dd5a95232 Merge branch 'master' into cis40_2026-02_rc 2026-04-23 10:41:41 +02:00
Harald Bamberger 954397f028 Merge branch 'feature-70376/Lohnguide' 2026-04-22 18:52:51 +02:00
Harald Bamberger 80faa61c91 Merge branch 'master' into feature-70376/Lohnguide 2026-04-22 18:46:57 +02:00
Werner Masik 961ede66a9 lohnguide db update changed 2026-04-22 18:40:22 +02:00
adisposkofh e57846566e displaying 'compacted' events in mobile monthview lv plan 2026-04-22 14:35:06 +02:00
Andreas Österreicher 6fec8382b5 Merge branch 'feature-76554/Personalmeldung_alt_bei_Lehre_nicht_melderelevant_rausfiltern' 2026-04-22 09:15:23 +02:00
Andreas Österreicher 4eb076d115 Fixed Saving of Entwicklungsteam 2026-04-21 17:34:36 +02:00
adisposkofh 5171a7b7b3 minor fix 2026-04-21 17:22:32 +02:00
Harald Bamberger 7427aa87ea Merge branch 'feature-76545/findAbgabenNewOrUpdatedSinceByAbgabedatumFixTimestampDateComparison' 2026-04-21 17:01:04 +02:00
Johann Hoffmann 85043e57db added missing parenthesis 2026-04-21 17:00:07 +02:00
adisposkofh b41c8acddd adapted other/stgorg lvplans to mobile view changes 2026-04-21 16:04:30 +02:00
adisposkofh 06bc1ebcd4 Merge branch 'feature-69146/CIS4_Anzeige_LVPLan_Studiengang_Semester_Verband_Gruppe' into feature-76385/mobile-optimization-lv-plan 2026-04-21 15:55:27 +02:00
adisposkofh 999827b3ec in mobile replacing lvplan weekview with listview 2026-04-21 15:49:51 +02:00
Johann Hoffmann 5beddbccb4 changed the where clause to a simpler = CURRENT_DATE comparison -> works the same if the job runs daily and fetches updates of one day/date; has to be changed back in case we want to find updates in a range larger than 1 day in an interval larger than 1 day; 2026-04-21 14:18:45 +02:00
kindlm e2ae9b88c8 Merge remote-tracking branch 'origin/master' 2026-04-21 12:56:30 +02:00
kindlm ca3abf9154 Small Style-Fix in Testtool
To make headings stand out more clearly from the buttons
2026-04-21 12:56:02 +02:00
adisposkofh b90dabeb2c minor fix 2026-04-21 11:23:17 +02:00
adisposkofh 2f1edfeeab minor fix 2026-04-21 11:21:50 +02:00
adisposkofh fac320bfce minor fix 2026-04-21 11:20:38 +02:00
adisposkofh 6c2820f900 minor fix 2026-04-21 11:19:36 +02:00
Alexei Karpenko f863c6d728 personalmeldung legacy system: melderelevant is checked for lehre, bugfix: lehre is correctly added if studiengang already has sws 2026-04-20 18:24:30 +02:00
Harald Bamberger 1a37273a9e Merge branch 'feature-63349/Cis_Menu_Aktiv_Flag' into cis40_2026-02_rc 2026-04-20 18:08:46 +02:00
Harald Bamberger d14b9e2ab5 Merge branch 'feature-69146/CIS4_Anzeige_LVPLan_Studiengang_Semester_Verband_Gruppe' into cis40_2026-02_rc 2026-04-20 18:02:23 +02:00
Harald Bamberger d926e4165b Merge branch 'master' into cis40_2026-02_rc 2026-04-20 17:52:24 +02:00
Harald Bamberger 8c88ae401b Merge branch 'bug-69145/Cis4_Studiengaenge_Studentenansicht_Vertiefungen_weggefiltert' into cis40_2026-02_rc 2026-04-20 17:45:41 +02:00
Harald Bamberger 92a2053b42 Merge branch 'feature-40870/BUG_Studstatus_unpause_order_should_be_DESC' 2026-04-20 17:30:14 +02:00
adisposkofh 536e66eed9 added padding at the bottom of the profile page 2026-04-20 16:39:28 +02:00
Harald Bamberger 6e103480cc Merge branch 'master' into feature-63349/Cis_Menu_Aktiv_Flag 2026-04-20 16:00:28 +02:00
adisposkofh 6a53c9fae4 fixed multiple news page issues 2026-04-20 15:54:24 +02:00
Harald Bamberger 8d815d40b6 Merge branch 'master' into feature-62450/Studium_Phrasen 2026-04-20 14:49:48 +02:00
adisposkofh 414d8bd383 minor fix 2026-04-20 11:40:26 +02:00
adisposkofh eb8b4986d7 fixed issue with sidenav covering search results/usermenu 2026-04-20 11:36:53 +02:00
kindlm 70602be54e SaveSort und Filter-Reset in RaumÜbersicht 2026-04-20 11:30:17 +02:00
kindlm dac71f597a Spalte Anmeldedatum in RT-Übersicht
Spalte Frage_ID in RT-Administration
JQuery und Tabelsorter aus Include in Service_Uebersicht
2026-04-20 11:24:59 +02:00
Johann Hoffmann 3a646ffe77 adapt AbgabeJob relevant queries so they compare with the pgsql date function CURRENT_DATE instead of NOW() to avoid the cutoff from uploads the happened "yesterday but more than 24 hours ago"; also added another "abgabedatum < CURRENT_DATE" condition, to avoid sending this exact case as updates 2 days in a row; 2026-04-20 11:06:06 +02:00
adisposkofh 1a813e52ce minor fix 2026-04-20 09:50:41 +02:00
Harald Bamberger afa765c4ba Merge branch 'master' into feature-69105/CIS4_MeineLVs_AnzeigeZeugnisnote_Und_Durchschnitt 2026-04-20 09:41:12 +02:00
adisposkofh 16b238124a cleaned up search icon conditional render 2026-04-20 09:24:51 +02:00
adisposkofh 4def45907b reworked searchbar animation 2026-04-17 18:10:05 +02:00
adisposkofh 202e6e88d2 redid searchbar animation with handmade transitions 2026-04-17 17:44:23 +02:00
adisposkofh 3b2473039f code formatting 2026-04-17 16:37:58 +02:00
adisposkofh 59d1ca3409 animated searchbar display in header 2026-04-17 16:36:54 +02:00
adisposkofh 1d26303333 fixed mobile header/searchbar appearance 2026-04-17 15:59:32 +02:00
adisposkofh 8f73489073 code cleanup 2026-04-17 14:51:20 +02:00
Harald Bamberger 1c2491385f Merge branch 'master' into feature-68957/CIS4_Dashboard_Bookmark_Widget_Sort_Tags 2026-04-17 10:38:00 +02:00
adisposkofh 94f742187e Merge branch 'feature-76150/CIS4-LV-Plan-von-anderen-Personen-anzeigen' into feature-76464/reworking-use-of-controller-viewdata 2026-04-16 16:12:33 +02:00
adisposkofh 70ebe34f1f merge develop into -- manual conflict resolution 2026-04-16 16:09:03 +02:00
adisposkofh 917a9ee707 minor fix 2026-04-16 15:55:43 +02:00
adisposkofh d577ac6d54 naming cleanup 2026-04-16 15:54:12 +02:00
adisposkofh 2aecc6e0f2 removed use of viewData object as passed by controller thru routerview to component -- component fetches data instead 2026-04-16 15:49:16 +02:00
adisposkofh 354e1ccdf4 reworked calendar sync appearance 2026-04-15 12:04:29 +02:00
adisposkofh cdc279b5da added 'other lv plan' permission check in api controller 2026-04-14 16:28:01 +02:00
adisposkofh d003bfa7f1 code formatting 2026-04-14 15:51:37 +02:00
adisposkofh 343a82b89c removed obsolete import 2026-04-14 14:53:33 +02:00
adisposkofh cc23fb0f39 fixed issue where max_semester could not be read before a degree program was selected 2026-04-14 14:29:02 +02:00
adisposkofh 7edddd0566 removed redundant OverviewLvPlan page (same as StgOrgLvPlan) 2026-04-14 14:19:38 +02:00
adisposkofh ebafc4576f implemented profile calendar sync section 2026-04-14 14:08:16 +02:00
ma0048 3ce3eff022 fehlendes mapping hinzugefuegt 2026-04-14 09:30:45 +02:00
adisposkofh 7daaf79fcc implemented permission for viewing other users' lv plans 2026-04-13 15:44:09 +02:00
adisposkofh f1c3c8296f implemented 'OtherLvPlan' to view other users' timetables 2026-04-13 10:41:41 +02:00
ma0048 ea0a249612 micro degree abschlussdokumente hinzugefuegt 2026-04-13 09:14:27 +02:00
chfhtw f06e59b362 Merge branch 'master' into feature-69146/CIS4_Anzeige_LVPLan_Studiengang_Semester_Verband_Gruppe 2026-04-08 14:13:02 +02:00
ma0048 9fd73ca34d menu anzeigen auch wenn englischer eintrag nicht vorhanden ist 2026-03-17 13:05:23 +01:00
ma0068 2681a85fa5 Studium.php added fallback if no lehrverband found for semester 2026-02-20 11:05:57 +01:00
ma0068 9a0716af44 Studium.php added fallback if no lehrverband found for semester 2026-02-20 09:18:48 +01:00
ma0068 dc642cfbe7 merge master into cis40_2026-02-rc 2026-02-19 16:44:01 +01:00
ma0068 b98e831300 StgOrgLvPlan: add DEFAULT_MODE_LV_PLAN to OverviewLvPlan 2026-02-19 16:31:28 +01:00
ma0068 0d73824727 StgOrgLvPlan: fix bug sem, ensure loading dropdowns in case of propsViewData 2026-02-19 11:18:19 +01:00
Harald Bamberger 1a0a5c652b Merge branch 'master' into feature-69105/CIS4_MeineLVs_AnzeigeZeugnisnote_Und_Durchschnitt 2026-02-10 15:48:48 +01:00
Harald Bamberger f4f645b103 Merge branch 'master' into cis40_2026-02_rc 2026-02-03 17:17:09 +01:00
Harald Bamberger 37395b70f6 Merge branch 'feature-69146/CIS4_Anzeige_LVPLan_Studiengang_Semester_Verband_Gruppe' into cis40_2026-02_rc 2026-02-03 11:56:16 +01:00
Harald Bamberger a65cb3b03c Merge branch 'bug-69145/Cis4_Studiengaenge_Studentenansicht_Vertiefungen_weggefiltert' into cis40_2026-02_rc 2026-02-03 11:50:10 +01:00
ma0068 779641e8e7 Merge branch 'master' into bug-69145/Cis4_Studiengaenge_Studentenansicht_Vertiefungen_weggefiltert 2026-02-02 17:11:28 +01:00
ma0068 a5188ce24a new component AverageGrade
showing no grades if zeugnisnote is not existing yet
2026-01-22 10:45:15 +01:00
ma0068 39a96885d2 refactor v-for to avoid warning multiple keys, add name 2026-01-08 17:28:03 +01:00
ma0068 96dbec3218 refactor: use new api and factory 2026-01-08 10:33:53 +01:00
ma0068 4b29e7bf8d - refactoring js to avoid click events on option elements for working in chrome
- backend: load fallback parameters later if needed to avoid warning
2026-01-08 09:41:04 +01:00
ma0068 ed69bd74ed apply validation logic for dropdowns also for OverviewLvPlan.js 2025-12-03 10:39:41 +01:00
ma0068 f0641ddd6d refactor Dropdown
- load dropdown only if dropdown before was chosen
- validation if field in between is empty
- ensure no null value gets into route
2025-12-02 17:30:11 +01:00
Harald Bamberger 6c2a2e4665 work in progress, add reset function to EventLoader composable, to be able to reload when external parameters are changed 2025-11-27 18:15:38 +01:00
ma0068 5e929df966 stgOrg: calendar and dropdown selection on one page 2025-11-25 12:01:49 +01:00
ma0068 455e0533fe Merge branch 'master' into feature-69146/CIS4_Anzeige_LVPLan_Studiengang_Semester_Verband_Gruppe 2025-11-21 08:53:36 +01:00
ma0068 a8c4fc7607 CIS4 #69146 Lvplan for View StG/Semester/Verband/Gruppe
- new API Endpoint lvPlan/eventsStgOrg
        - new view Cis/LvPlan/StgOrg
        - new component LvPlan/StgOrg.js
        - new overview Cis/OverviewLvPlan with Dropdown
	- phrases
2025-11-20 14:06:12 +01:00
ma0068 a26bdfe385 add filter function for Tags, refactoring and restyling, add phrases and dbupdate 2025-11-13 09:19:35 +01:00
ma0068 df63b8c1b7 use PvAutocomplete instead Chips, start FilterTags 2025-11-05 11:09:16 +01:00
ma0068 707d00b280 add functionality insert, update and delete Tags of Bookmarks 2025-10-31 13:53:16 +01:00
ma0068 9c79d728e8 import component primevue.chips 2025-10-29 11:52:46 +01:00
ma0068 9c8a1f564e add sort functionality to bookmark widget 2025-10-29 09:52:11 +01:00
Harald Bamberger 4014083238 Merge branch 'master' into feature-62450/Studium_Phrasen 2025-09-15 07:38:11 +02:00
SimonGschnell f69016883c update(Cis Menu Content aktiv): changes the menu content query so that only aktiv content is filtered and returned to the frontend 2025-08-26 12:00:56 +02:00
SimonGschnell c10baca137 update(Cis Menu deactive content): filters all content out of the cis menu that has not set the active flag 2025-08-18 13:44:35 +02:00
SimonGschnell 60e403a28b update(Studium Phrases): inserts phrases for the studium component 2025-08-11 14:18:51 +02:00
cgfhtw d542cf7720 s&d 2024-08-14 16:20:47 +02:00
103 changed files with 5653 additions and 1657 deletions
+3
View File
@@ -64,6 +64,9 @@ $route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1';
$route['Cis/LvPlan/.*'] = 'Cis/LvPlan/index/$1';
$route['Cis/MyLvPlan/.*'] = 'Cis/MyLvPlan/index/$1';
$route['Cis/MyLv/.*'] = 'Cis/MyLv/index/$1';
$route['Cis/OtherLvPlan/.*'] = 'Cis/OtherLvPlan/index/$1';
//Route for LV Plan Stg/Semester/Verband/Gruppe
$route['Cis/StgOrgLvPlan/.*'] = 'Cis/StgOrgLvPlan/index/$1';
$route['Abgabetool/Assistenz'] = 'Cis/Abgabetool/Assistenz';
$route['Abgabetool/Assistenz/(:any)'] = 'Cis/Abgabetool/Assistenz/$1';
+5 -26
View File
@@ -31,12 +31,8 @@ class Abgabetool extends Auth_Controller
{
// TODO: routing from index based on berechtigung?
$viewData = array(
'uid'=>getAuthUID(),
);
if(defined('CIS4') && CIS4) {
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Abgabetool']);
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'Abgabetool']);
} else {
$this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'Abgabetool']);
}
@@ -44,12 +40,8 @@ class Abgabetool extends Auth_Controller
public function Student($student_uid_prop = '')
{
$viewData = array(
'uid'=>getAuthUID(),
);
if(defined('CIS4') && CIS4) {
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'AbgabetoolStudent']);
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'AbgabetoolStudent']);
} else {
$this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'AbgabetoolStudent', 'student_uid_prop' => $student_uid_prop]);
}
@@ -57,12 +49,8 @@ class Abgabetool extends Auth_Controller
public function Mitarbeiter()
{
$viewData = array(
'uid'=>getAuthUID(),
);
if(defined('CIS4') && CIS4) {
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'AbgabetoolMitarbeiter']);
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'AbgabetoolMitarbeiter']);
} else {
$this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'AbgabetoolMitarbeiter']);
}
@@ -70,13 +58,8 @@ class Abgabetool extends Auth_Controller
public function Assistenz($stg_kz_prop = '')
{
$viewData = array(
'uid'=>getAuthUID(),
);
if(defined('CIS4') && CIS4) {
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'AbgabetoolAssistenz']);
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'AbgabetoolAssistenz']);
} else {
$this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'AbgabetoolAssistenz', 'stg_kz_prop' => $stg_kz_prop]);
}
@@ -84,12 +67,8 @@ class Abgabetool extends Auth_Controller
public function Deadlines()
{
$viewData = array(
'uid'=>getAuthUID(),
);
if(defined('CIS4') && CIS4) {
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'DeadlinesOverview']);
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'DeadlinesOverview']);
} else {
$this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'DeadlinesOverview']);
}
+1 -7
View File
@@ -28,12 +28,6 @@ class LvPlan extends Auth_Controller
*/
public function index()
{
$viewData = array(
'uid'=>getAuthUID(),
'timezone' => $this->config->item('timezone')
);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'LvPlan']);
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'LvPlan']);
}
}
+1 -6
View File
@@ -26,11 +26,6 @@ class MyLv extends Auth_Controller
*/
public function index()
{
$viewData = array(
);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'MyLv']);
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'MyLv']);
}
}
+2 -8
View File
@@ -27,13 +27,7 @@ class MyLvPlan extends Auth_Controller
* @return void
*/
public function index()
{
$viewData = array(
'uid'=>getAuthUID(),
'timezone' => $this->config->item('timezone')
);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'MyLvPlan']);
{
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'MyLvPlan']);
}
}
@@ -0,0 +1,34 @@
<?php
if (!defined('BASEPATH'))
exit('No direct script access allowed');
/**
*
*/
class OtherLvPlan extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct([
'index' => ['basis/other_lv_plan:r']
]);
// Load Config
$this->load->config('calendar');
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @return void
*/
public function index()
{
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'OtherLvPlan']);
}
}
+19 -39
View File
@@ -55,15 +55,7 @@ class Profil extends Auth_Controller
*/
public function index()
{
$this->load->library('ProfilLib');
$profil_data = $this->profillib->getView(getAuthUID());
$profil_data = hasData($profil_data) ? getData($profil_data) : null;
$viewData = array(
'editable'=>true,
'profil_data' => $profil_data,
);
$this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'profilIndex']);
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'profilIndex']);
}
/**
@@ -73,23 +65,13 @@ class Profil extends Auth_Controller
*/
public function View($uid)
{
$this->load->library('ProfilLib');
$profil_data = $this->profillib->getView($uid);
$profil_data = hasData($profil_data) ? getData($profil_data) : null;
$viewData = array (
'uid' => $uid,
'profil_data'=>$profil_data,
);
if($uid == getAuthUID()){
$viewData['editable'] = true;
}
$this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'profilViewUid']);
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'profilViewUid']);
}
/**
* checks whether a specific userID is a mitarbeiter or not (foreword declaration of the function isMitarbeiter in Mitarbeiter_model.php)
* checks whether a specific userID is a student or not (foreword declaration of the function isStudent in Student_model.php)
* @access public
* @param $uid the userID used to check if it is a mitarbeiter
* @param $uid the userID used to check if it is a student
* @return boolean
*/
public function isStudent($uid)
@@ -119,7 +101,7 @@ class Profil extends Auth_Controller
}
/**
* gets the adressen that are marked as zustell from the currenlty logged in user
* gets the adressen that are marked as zustell from the currently logged in user
* @access public
* @return array a list of adresse_id's
*/
@@ -262,23 +244,23 @@ class Profil extends Auth_Controller
$this->GemeindeModel->addDistinct();
$this->GemeindeModel->addSelect(["name"]);
if ($nation == "A") {
if (isset($zip) && $zip > 999 && $zip < 32000) {
if (isset($zip) && $zip > 999 && $zip < 32000) {
$gemeinde_res = $this->GemeindeModel->loadWhere(['plz' => $zip]);
if (isError($gemeinde_res)) {
show_error("error while trying to query bis.tbl_gemeinde");
}
$gemeinde_res = hasData($gemeinde_res) ? getData($gemeinde_res) : null;
$gemeinde_res = array_map(function ($obj) {
return $obj->name;
}, $gemeinde_res);
echo json_encode($gemeinde_res);
} else {
echo json_encode(error("ortschaftskennziffer code was not valid"));
$gemeinde_res = $this->GemeindeModel->loadWhere(['plz' => $zip]);
if (isError($gemeinde_res)) {
show_error("error while trying to query bis.tbl_gemeinde");
}
$gemeinde_res = hasData($gemeinde_res) ? getData($gemeinde_res) : null;
$gemeinde_res = array_map(function ($obj) {
return $obj->name;
}, $gemeinde_res);
echo json_encode($gemeinde_res);
} else {
echo json_encode(error("ortschaftskennziffer code was not valid"));
}
} else {
echo json_encode(error("Nation was not 'A' (Austria)"));
echo json_encode(error("Nation was not 'A' (Austria)"));
}
}
@@ -750,6 +732,4 @@ class Profil extends Auth_Controller
$zutrittskarte_ausgegebenam = str_replace("-", ".", $zutrittskarte_ausgegebenam);
return $zutrittskarte_ausgegebenam;
}
}
+1 -6
View File
@@ -25,11 +25,6 @@ class Raumsuche extends Auth_Controller
*/
public function index()
{
$viewData = array(
'uid'=>getAuthUID(),
);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Raumsuche']);
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'Raumsuche']);
}
}
@@ -0,0 +1,33 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*
*/
class StgOrgLvPlan extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct([
'index' => ['basis/cis:r']
]);
// Load Config
$this->load->config('calendar');
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @return void
*/
public function index()
{
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'StgOrgLvPlan']);
}
}
+1 -4
View File
@@ -29,10 +29,7 @@ class Studium extends Auth_Controller
*/
public function index()
{
$viewData = array(
);
$this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'studium']);
$this->load->view('CisRouterView/CisRouterView.php',['route' => 'studium']);
}
+6 -15
View File
@@ -1,6 +1,7 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
if (!defined('BASEPATH'))
exit('No direct script access allowed');
/**
*
@@ -13,9 +14,9 @@ class Cis4 extends Auth_Controller
public function __construct()
{
parent::__construct(
array(
'index' => 'basis/cis:r'
)
array(
'index' => 'basis/cis:r'
)
);
// Load Config
@@ -30,16 +31,6 @@ class Cis4 extends Auth_Controller
*/
public function index()
{
$this->load->model('person/Person_model', 'PersonModel');
$personData = getData($this->PersonModel->getByUid(getAuthUID()))[0];
$viewData = array(
'uid' => getAuthUID(),
'name' => $personData->vorname,
'person_id' => $personData->person_id,
'timezone' => $this->config->item('timezone')
);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'FhcDashboard']);
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'FhcDashboard']);
}
}
@@ -18,6 +18,8 @@
if (! defined('BASEPATH')) exit('No direct script access allowed');
use \DateTime as DateTime;
class Bookmark extends FHCAPI_Controller
{
@@ -28,111 +30,162 @@ class Bookmark extends FHCAPI_Controller
{
parent::__construct([
'getBookmarks' => self::PERM_LOGGED,
'delete' => self::PERM_LOGGED,
'insert' => self::PERM_LOGGED,
'delete' => self::PERM_LOGGED,
'insert' => self::PERM_LOGGED,
'update' => self::PERM_LOGGED,
]);
'changeOrder' => self::PERM_LOGGED
]);
$this->load->model('dashboard/Bookmark_model', 'BookmarkModel');
$this->uid = getAuthUID();
$this->pid = getAuthPersonID();
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
* gets the bookmarks associated to a user
/**
* gets the bookmarks associated to a user
* @access public
* @return void
*/
public function getBookmarks()
{
$this->BookmarkModel->addOrder("bookmark_id");
$this->BookmarkModel->addOrder("sort");
$bookmarks = $this->BookmarkModel->loadWhere(["uid"=>$this->uid]);
$bookmarks = $this->getDataOrTerminateWithError($bookmarks);
$bookmarks = $this->getDataOrTerminateWithError($bookmarks);
$this->terminateWithSuccess($bookmarks);
}
/**
* deletes bookmark from associated user
* @access public
* @return void
*/
public function delete($bookmark_id)
{
$bookmark = $this->BookmarkModel->load($bookmark_id);
$bookmark = current($this->getDataOrTerminateWithError($bookmark));
// only delete bookmark if the user is the owner of the bookmark
if($bookmark->uid == $this->uid || $this->permissionlib->isBerechtigt('admin')){
$delete_result = $this->BookmarkModel->delete($bookmark_id);
$delete_result = $this->getDataOrTerminateWithError($delete_result);
$this->terminateWithSuccess($delete_result);
}else{
$this->_outputAuthError(['delete' => ['admin:rw']]);
}
}
/**
* inserts new bookmark into the bookmark table
* @access public
* @return void
*/
public function insert()
{
// form validation
$this->load->library('form_validation');
$this->form_validation->set_rules('url', 'URL', 'required|valid_url|max_length[511]');
$this->form_validation->set_rules('title', 'Title', 'required|max_length[255]');
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$url = $this->input->post('url',true);
$title = $this->input->post('title',true);
$tag = $this->input->post('tag', true);
$insert_into_result = $this->BookmarkModel->insert(['uid'=>$this->uid, 'url'=>$url, 'title'=>$title,'tag'=>$tag, 'insertvon'=>$this->uid, 'updateamum'=>NULL, 'updatevon'=>NULL]);
$insert_into_result = $this->getDataOrTerminateWithError($insert_into_result);
$this->terminateWithSuccess($insert_into_result);
}
$this->terminateWithSuccess($bookmarks);
}
/**
* updates bookmark in the bookmark table
* deletes bookmark from associated user
* @access public
* @return void
*/
public function update($bookmark_id)
public function delete($bookmark_id)
{
// form validation
$this->load->library('form_validation');
$this->form_validation->set_rules('url', 'URL', 'required|valid_url|max_length[511]');
$this->form_validation->set_rules('title', 'Title', 'required|max_length[255]');
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$bookmark = $this->BookmarkModel->load($bookmark_id);
$url = $this->input->post('url',true);
$title = $this->input->post('title',true);
$bookmark = current($this->getDataOrTerminateWithError($bookmark));
// only delete bookmark if the user is the owner of the bookmark
if ($bookmark->uid == $this->uid || $this->permissionlib->isBerechtigt('admin')) {
$delete_result = $this->BookmarkModel->delete($bookmark_id);
$delete_result = $this->getDataOrTerminateWithError($delete_result);
$this->terminateWithSuccess($delete_result);
} else {
$this->_outputAuthError(['delete' => ['admin:rw']]);
}
}
/**
* inserts new bookmark into the bookmark table
* @access public
* @return void
*/
public function insert()
{
// form validation
$this->load->library('form_validation');
$this->form_validation->set_rules('url', 'URL', 'required|valid_url|max_length[511]');
$this->form_validation->set_rules('title', 'Title', 'required|max_length[255]');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$url = $this->input->post('url', true);
$title = $this->input->post('title', true);
$tag = $this->input->post('tag', true);
if (is_array($tag)) {
$tag = json_encode($tag); // convert PHP array to JSON string
}
$sort = $this->input->post('sort', true);
$insert_into_result = $this->BookmarkModel->insert([
'uid' => $this->uid,
'url' => $url,
'title' => $title,
'tag' => $tag,
'insertvon' => $this->uid,
'updateamum' => null,
'updatevon' => null,
'sort' => $sort
]);
$insert_into_result = $this->getDataOrTerminateWithError($insert_into_result);
$this->terminateWithSuccess($insert_into_result);
}
/**
* updates bookmark in the bookmark table
* @access public
* @return void
*/
public function update($bookmark_id)
{
// form validation
$this->load->library('form_validation');
$this->form_validation->set_rules('url', 'URL', 'required|valid_url|max_length[511]');
$this->form_validation->set_rules('title', 'Title', 'required|max_length[255]');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$url = $this->input->post('url', true);
$title = $this->input->post('title', true);
$tag = $this->input->post('tag', true);
if (is_array($tag)) {
$tag = json_encode($tag);
}
$now = new DateTime();
$now = $now->format('Y-m-d H:i:s');
$update_result = $this->BookmarkModel->update($bookmark_id,['url'=>$url, 'title'=>$title,'updateamum'=>$now]);
$update_result = $this->BookmarkModel->update($bookmark_id, [
'url' => $url,
'title' => $title,
'tag' => $tag,
'updateamum' => $now
]);
$update_result = $this->getDataOrTerminateWithError($update_result);
$update_result = $this->getDataOrTerminateWithError($update_result);
$this->terminateWithSuccess($update_result);
$this->terminateWithSuccess($update_result);
}
}
/**
* changes sort of two bookmarks in the bookmark table
* @access public
* @return void
*/
public function changeOrder($bookmark_id1, $bookmark_id2)
{
$update_result = [];
$result1 = $this->BookmarkModel->load($bookmark_id1);
$data1 = $this->getDataOrTerminateWithError($result1);
$sort1 = current($data1)->sort;
$result2 = $this->BookmarkModel->load(["bookmark_id"=>$bookmark_id2]);
$data2 = $this->getDataOrTerminateWithError($result2);
$sort2 = current($data2)->sort;
$update_result1 = $this->BookmarkModel->update($bookmark_id1, [
'sort' => $sort2
]);
$update_result[] = $this->getDataOrTerminateWithError($update_result1);
$update_result2 = $this->BookmarkModel->update($bookmark_id2, [
'sort' => $sort1
]);
$update_result[] = $this->getDataOrTerminateWithError($update_result2);
$this->terminateWithSuccess($update_result);
}
}
@@ -27,7 +27,7 @@ class Cis4FhcApi extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getViewData' => self::PERM_LOGGED,
'dashboardViewData' => self::PERM_LOGGED,
]);
}
@@ -36,17 +36,22 @@ class Cis4FhcApi extends FHCAPI_Controller
// Public methods
/**
* fetches ViewData
*/
public function getViewData()
* retrieves view data for dashboard view
* @access public
* @param $uid the userID for which profile is being viewed, null or missing value implies one's own profile
*/
public function dashboardViewData()
{
$this->load->model('person/Person_model','PersonModel');
$personData = getData($this->PersonModel->getByUid(getAuthUID()))[0];
$this->load->config('calendar');
$viewData = array(
'uid' => getAuthUID(),
'name' => $personData->vorname,
'person_id' => $personData->person_id
'person_id' => $personData->person_id,
'timezone' => $this->config->item('timezone'),
);
$this->terminateWithSuccess($viewData);
@@ -16,12 +16,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
if (!defined('BASEPATH'))
exit('No direct script access allowed');
class CisMenu extends FHCAPI_Controller
{
/**
* Object initialization
*/
@@ -31,28 +32,95 @@ class CisMenu extends FHCAPI_Controller
'getMenu' => self::PERM_LOGGED,
]);
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
/**
* fetches the menu for CIS from the database based on the userLanguage
*/
public function getMenu()
public function getMenu()
{
$this->load->model('content/Content_model', 'ContentModel');
$this->load->config('cis');
$cis4_content_id =$this->config->item('cis_menu_root_content_id');
$result = $this->ContentModel->getMenu($cis4_content_id, getAuthUID(),getUserLanguage());
$cis4_content_id = $this->config->item('cis_menu_root_content_id');
$result = $this->ContentModel->getMenu($cis4_content_id, getAuthUID(), getUserLanguage());
$result = $this->getDataOrTerminateWithError($result);
$menu = $result->childs ?? [];
$this->terminateWithSuccess($menu);
}
$menu = $this->generateUrlsForMenuItems($menu);
$this->terminateWithSuccess($menu);
}
private function generateUrlsForMenuItems($menuItems)
{
return array_map(
function ($menuItem) {
return $this->generateUrlForMenuItem($menuItem);
},
$menuItems
);
}
private function generateUrlForMenuItem($menuItem)
{
$menuItem->url = $this->menuItemUrlHelper($menuItem);
unset($menuItem->content);
if ($menuItem->childs && count($menuItem->childs)) {
$menuItem->childs = $this->generateUrlsForMenuItems($menuItem->childs);
}
return $menuItem;
}
private function menuItemUrlHelper($menuItem)
{
if ($menuItem->template_kurzbz !== 'redirect') {
return site_url("/CisVue/Cms/content/" . $menuItem->content_id);
}
if (!$menuItem->content || !mb_strlen($menuItem->content)) {
return '';
}
$doc = new DOMDocument();
$doc->loadXML($menuItem->content);
$urlElem = $doc->getElementsByTagName('url')->item(0);
if (!$urlElem) {
return '';
}
$url = $urlElem->textContent;
if (strpos($url, '../cms/news.php') !== false) {
$newsRegex = '/^\.\.\/cms\/news\.php/';
$url = preg_replace($newsRegex, site_url("/CisVue/Cms/news"), $url);
}
if (strpos($url, '../cms/content.php?') !== false) {
$contentRegex = '/^\.\.\/cms\/content\.php\?content_id=([0-9]+)/';
$matches = [];
preg_match($contentRegex, $url, $matches);
$url = site_url('/CisVue/Cms/content/' . $matches[1]);
}
if (strpos($url, '../index.ci.php') !== false) {
$indexRegex = '/^\.\.\/index\.ci\.php/';
$url = preg_replace($indexRegex, site_url(), $url);
}
if (strpos($url, '../') !== false) {
$relativeRegex = '/^\.\.\//';
$url = preg_replace($relativeRegex, base_url(), $url);
}
return $url;
}
}
@@ -16,7 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
if (!defined('BASEPATH'))
exit('No direct script access allowed');
use CI3_Events as Events;
use \DateTime as DateTime;
@@ -33,19 +34,25 @@ class LvPlan extends FHCAPI_Controller
parent::__construct([
'getRoomplan' => self::PERM_LOGGED,
'Stunden' => self::PERM_LOGGED,
'getReservierungen' => self::PERM_LOGGED,
'Stunden' => self::PERM_LOGGED,
'getReservierungen' => self::PERM_LOGGED,
'LvPlanEvents' => self::PERM_LOGGED,
'eventsPersonal' => self::PERM_LOGGED,
'eventsLv' => self::PERM_LOGGED,
'getLehreinheitStudiensemester' => self::PERM_LOGGED,
'studiensemesterDateInterval' => self::PERM_LOGGED,
'getLvPlanForStudiensemester' => self::PERM_LOGGED,
'getLv' => self::PERM_LOGGED
'getLv' => self::PERM_LOGGED,
'eventsStgOrg' => self::PERM_LOGGED,
'fetchFerienEvents' => self::PERM_LOGGED,
'getStudiengaenge' => self::PERM_LOGGED,
'getLehrverband' => self::PERM_LOGGED,
'permissionOtherLvPlan' => self::PERM_LOGGED,
'compactibleEventTypes' => self::PERM_LOGGED,
]);
$this->load->library('LogLib');
$this->loglib->setConfigs(array(
$this->load->library('LogLib');
$this->loglib->setConfigs(array(
'classIndex' => 5,
'functionIndex' => 5,
'lineIndex' => 4,
@@ -53,17 +60,17 @@ class LvPlan extends FHCAPI_Controller
'dbExecuteUser' => 'RESTful API'
));
$this->load->library('form_validation');
$this->load->library('form_validation');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
* fetches LvPlan and Moodle events together
* @access public
*
*/
* fetches LvPlan and Moodle events together
* @access public
*
*/
public function LvPlanEvents()
{
$hasLv = $this->input->post('lv_id');
@@ -83,24 +90,30 @@ class LvPlan extends FHCAPI_Controller
// form validation
$this->form_validation->set_rules('start_date', "start_date", "required");
$this->form_validation->set_rules('end_date', "end_date", "required");
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
// storing the post parameter in local variables
$start_date = $this->input->post('start_date', true);
$end_date = $this->input->post('end_date', true);
$uid = $this->input->post('uid', true);
// disallow accessing other user's lv plan if missing permission
if ($uid && $uid !== getAuthUID() && !$this->permissionlib->isBerechtigt('basis/other_lv_plan')) {
$this->terminateWithError("Missing permission to view other users' timetables!");
}
// fetching lvplan events
$result = $this->stundenplanlib->getEventsUser($start_date, $end_date);
$result = $this->stundenplanlib->getEventsUser($start_date, $end_date, $uid);
$lvplanEvents = $this->getDataOrTerminateWithError($result);
// fetching moodle events
$moodleEvents = $this->fetchMoodleEvents($start_date, $end_date);
$moodleEvents = $uid ? [] : $this->fetchMoodleEvents($start_date, $end_date);
// fetching ferien events
$ferienEvents = $this->fetchFerienEvents($start_date, $end_date);
$ferienEvents = $this->fetchFerienEvents($start_date, $end_date, $uid);
$this->terminateWithSuccess(array_merge(
$lvplanEvents,
@@ -109,6 +122,45 @@ class LvPlan extends FHCAPI_Controller
));
}
/**
* fetches LvPlan for studiengang / semester / verband / gruppe
*
* @access public
*/
public function eventsStgOrg()
{
$this->load->library('StundenplanLib');
// form validation
$this->form_validation->set_rules('start_date', "start_date", "required");
$this->form_validation->set_rules('end_date', "end_date", "required");
//$this->form_validation->set_rules('stg_kz', "stg_kz", "required"); //no validation show empty calendar
if (!$this->form_validation->run()) {
$this->terminateWithValidationErrors($this->form_validation->error_array());
$stgOrgEvents = [];
$ferienEvents = [];
} else {
$start_date = $this->input->post('start_date', true);
$end_date = $this->input->post('end_date', true);
$stg_kz = $this->input->post('stg_kz', true);
$sem = $this->input->post('sem', true);
$verband = $this->input->post('verband', true);
$gruppe = $this->input->post('gruppe', true);
$result = $this->stundenplanlib->getEventsStgOrg($start_date, $end_date, $stg_kz, $sem, $verband, $gruppe);
$stgOrgEvents = $this->getDataOrTerminateWithError($result);
$result = $this->stundenplanlib->fetchFerienTageEvents($start_date, $end_date, $stg_kz);
$ferienEvents = $this->getDataOrTerminateWithError($result);
}
$this->terminateWithSuccess(array_merge(
$stgOrgEvents,
$ferienEvents
));
}
/**
* fetches LvPlan and Ferien events together for the lv
*
@@ -122,7 +174,7 @@ class LvPlan extends FHCAPI_Controller
$this->form_validation->set_rules('start_date', "start_date", "required");
$this->form_validation->set_rules('end_date', "end_date", "required");
$this->form_validation->set_rules('lv_id', "lv_id", "required|integer");
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
@@ -137,7 +189,6 @@ class LvPlan extends FHCAPI_Controller
// fetching ferien events
$ferienEvents = $this->fetchFerienEvents($start_date, $end_date);
$this->terminateWithSuccess(array_merge(
$lvplanEvents,
@@ -146,40 +197,42 @@ class LvPlan extends FHCAPI_Controller
}
//TODO: delete this function if we don't use the old calendar export endpoints anymore
public function studiensemesterDateInterval($date){
$this->load->model('organisation/Studiensemester_model','StudiensemesterModel');
$studiensemester =$this->StudiensemesterModel->getByDate(date_format(date_create($date),'Y-m-d'));
$studiensemester =current($this->getDataOrTerminateWithError($studiensemester));
public function studiensemesterDateInterval($date)
{
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$studiensemester = $this->StudiensemesterModel->getByDate(date_format(date_create($date), 'Y-m-d'));
$studiensemester = current($this->getDataOrTerminateWithError($studiensemester));
$this->terminateWithSuccess($studiensemester);
}
public function getLvPlanForStudiensemester($studiensemester,$lvid){
public function getLvPlanForStudiensemester($studiensemester, $lvid)
{
$this->load->library('StundenplanLib');
$this->load->model('organisation/Studiensemester_model','StudiensemesterModel');
$studiensemester_result = $this->StudiensemesterModel->loadWhere(["studiensemester_kurzbz"=>$studiensemester]);
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$studiensemester_result = $this->StudiensemesterModel->loadWhere(["studiensemester_kurzbz" => $studiensemester]);
$studiensemester_result = current($this->getDataOrTerminateWithError($studiensemester_result));
$timespan_start = new DateTime($studiensemester_result->start);
$timespan_ende = new DateTime($studiensemester_result->ende);
$lvplan = $this->stundenplanlib->getStundenplan(date_format($timespan_start, 'Y-m-d'),date_format($timespan_ende, 'Y-m-d'), $lvid);
$lvplan = $this->stundenplanlib->getStundenplan(date_format($timespan_start, 'Y-m-d'), date_format($timespan_ende, 'Y-m-d'), $lvid);
$this->terminateWithSuccess($lvplan);
}
/**
* fetches Stunden layout from database
* @access public
*
*/
public function Stunden()
}
/**
* fetches Stunden layout from database
* @access public
*
*/
public function Stunden()
{
$this->load->model('ressource/Stunde_model', 'StundeModel');
$this->StundeModel->addOrder('stunde', 'ASC');
$stunden = $this->StundeModel->load();
$stunden = $this->getDataOrTerminateWithError($stunden);
$stunden = $this->getDataOrTerminateWithError($stunden);
$this->terminateWithSuccess($stunden);
}
@@ -210,10 +263,10 @@ class LvPlan extends FHCAPI_Controller
$roomplan_data = $this->stundenplanlib->getRoomplan($ort_kurzbz, $start_date, $end_date);
$roomplan_data = $this->getDataOrTerminateWithError($roomplan_data);
$this->terminateWithSuccess($roomplan_data);
}
/**
* gets the reservierungen of a room if the ort_kurzbz parameter is
* supplied otherwise gets the reservierungen of the lvplan of a student
@@ -226,25 +279,27 @@ class LvPlan extends FHCAPI_Controller
{
$this->form_validation->set_rules('start_date', "StartDate", "required");
$this->form_validation->set_rules('end_date', "EndDate", "required");
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
// storing the post parameter in local variables
$start_date = $this->input->post('start_date', true);
$end_date = $this->input->post('end_date', true);
$uid = $this->input->post('uid', true);
// get data
$this->load->library('StundenplanLib');
$result = $this->stundenplanlib->getReservierungen($start_date, $end_date, $ort_kurzbz);
$result = $this->stundenplanlib->getReservierungen($start_date, $end_date, $ort_kurzbz, $uid);
$result = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($result);
}
public function getLehreinheitStudiensemester($lehreinheit_id){
public function getLehreinheitStudiensemester($lehreinheit_id)
{
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$this->LehreinheitModel->addSelect(["studiensemester_kurzbz"]);
$result = $this->LehreinheitModel->load($lehreinheit_id);
@@ -287,6 +342,68 @@ class LvPlan extends FHCAPI_Controller
return $this->terminateWithSuccess(current($result));
}
public function getStudiengaenge()
{
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$this->StudiengangModel->addOrder('typ');
$this->StudiengangModel->addOrder('kurzbz');
$result = $this->StudiengangModel->loadWhere([
'aktiv' => true
]);
$data = $this->getDataOrTerminateWithError($result);
return $this->terminateWithSuccess($data);
}
public function getLehrverband($studiengang_kz, $semester = null, $verband = null)
{
$this->load->model('organisation/Lehrverband_model', 'LehrverbandModel');
$where = [
'aktiv' => true,
'studiengang_kz' => $studiengang_kz,
];
if ($semester !== null && $semester !== 'null' && $semester !== 'undefined') {
$where['semester'] = $semester;
}
if ($verband !== null && $verband !== 'null' && $verband !== 'undefined') {
$where['verband'] = $verband;
}
$this->LehrverbandModel->addOrder('studiengang_kz');
$this->LehrverbandModel->addOrder('semester');
$this->LehrverbandModel->addOrder('verband');
$this->LehrverbandModel->addOrder('gruppe');
$result = $this->LehrverbandModel->loadWhere($where);
$data = $this->getDataOrTerminateWithError($result);
return $this->terminateWithSuccess($data);
}
/**
* Checks if the current user has permission to view other users' timetables
*
* @return void
*/
public function permissionOtherLvPlan()
{
$this->terminateWithSuccess($this->permissionlib->isBerechtigt('basis/other_lv_plan'));
}
/**
* get event types which can be compacted in lv plan display
*
* @return void
*/
public function compactibleEventTypes()
{
$this->terminateWithSuccess(["lehreinheit", "reservierung"]);
}
/**
* fetch moodle events
*
@@ -299,19 +416,19 @@ class LvPlan extends FHCAPI_Controller
$this->load->config('calendar');
$tz = new DateTimeZone($this->config->item('timezone'));
$start = new DateTime($start_date);
$start->setTimezone($tz);
$end = new DateTime($end_date);
$end->setTimezone($tz);
$end->modify('+1 day -1 second');
$moodle_events = [];
Events::trigger(
'moodleCalendarEvents',
function & () use (&$moodle_events) {
function &() use (&$moodle_events) {
return $moodle_events;
},
[
@@ -331,23 +448,23 @@ class LvPlan extends FHCAPI_Controller
* @param string $end_date
* @return array
*/
private function fetchFerienEvents($start_date, $end_date)
private function fetchFerienEvents($start_date, $end_date, $uid = null)
{
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$this->load->model('education/Studentlehrverband_model', 'StudentLehrverbandModel');
$currentStudiensemester = $this->StudiensemesterModel->getByDate($start_date);
$currentStudiensemester = $this->getDataOrTerminateWithError($currentStudiensemester);
if ($currentStudiensemester) {
$studentsemester_kurzbz = current($currentStudiensemester)->studiensemester_kurzbz;
$studiengang = $this->StudentLehrverbandModel->loadWhere([
"student_uid" => getAuthUID(),
"student_uid" => $uid ?? getAuthUID(),
"studiensemester_kurzbz" => $studentsemester_kurzbz
]);
$studiengang = $this->getDataOrTerminateWithError($studiengang);
if ($studiengang)
$studiengang_kz = current($studiengang)->studiengang_kz;
else
@@ -357,7 +474,7 @@ class LvPlan extends FHCAPI_Controller
}
$ferienEvents = $this->stundenplanlib->fetchFerienTageEvents($start_date, $end_date, $studiengang_kz);
return $this->getDataOrTerminateWithError($ferienEvents);
}
}
@@ -0,0 +1,76 @@
<?php
/**
* Copyright (C) 2024 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (!defined('BASEPATH'))
exit('No direct script access allowed');
class OtherLvPlan extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'otherLvPlanViewData' => ['basis/other_lv_plan:r'],
]);
$this->load->model('ressource/mitarbeiter_model', 'MitarbeiterModel');
$this->load->model('person/Benutzer_model', 'BenutzerModel');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
* retrieves viewData for other lv plan view
* @access public
* @param $uid the userID for which the other lv plan is being viewed
*/
public function otherLvPlanViewData($uid)
{
$isMitarbeiterResult = $this->MitarbeiterModel->isMitarbeiter($uid);
$isMitarbeiter = getData($isMitarbeiterResult);
$isStudent = !$isMitarbeiter;
$this->BenutzerModel->addSelect(["foto", "vorname", "nachname"]);
$this->BenutzerModel->addJoin("tbl_person", "person_id");
$personResult = $this->BenutzerModel->load([$uid]);
$person = hasData($personResult) ? getData($personResult) : null;
$viewData = [
"user_data" => [
"username" => $uid,
"is_student" => $isStudent,
"is_mitarbeiter" => $isMitarbeiter,
"foto" => $person[0]->foto,
"vorname" => $person[0]->vorname,
"nachname" => $person[0]->nachname,
],
];
$this->terminateWithSuccess($viewData);
}
// -----------------------------------------------------------------------------------------------------------------
// Private methods
}
@@ -16,7 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
if (!defined('BASEPATH'))
exit('No direct script access allowed');
class Profil extends FHCAPI_Controller
{
@@ -27,13 +28,13 @@ class Profil extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'fotoSperre' => self::PERM_LOGGED,
'fotoSperre' => self::PERM_LOGGED,
'getGemeinden' => self::PERM_LOGGED,
'getAllNationen' => self::PERM_LOGGED,
'isMitarbeiter' => self::PERM_LOGGED,
'profilViewData' => self::PERM_LOGGED,
]);
$this->load->library('PermissionLib');
$this->load->model('ressource/mitarbeiter_model', 'MitarbeiterModel');
@@ -48,28 +49,37 @@ class Profil extends FHCAPI_Controller
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function profilViewData($uid=null){
/**
* retrieves view data for profile view
* @access public
* @param $uid the userID for which profile is being viewed, null or missing value implies one's own profile
*/
public function profilViewData($uid = null)
{
$authUid = getAuthUID();
$isProfilOfAuthUser = !$uid || $uid === $authUid;
$this->load->library('ProfilLib');
$editable = false;
if(isset($uid) && $uid != null){
$profil_data = $this->profillib->getView($uid);
if($uid == getAuthUID()){
$editable = true;
}
}else{
$editable = true;
$profil_data = $this->profillib->getView(getAuthUID());
$profileData = $this->profillib->getView($uid ?? $authUid);
$profileData = hasData($profileData) ? getData($profileData) : null;
$viewData = [
'editable' => $isProfilOfAuthUser,
'profil_data' => $profileData,
'permissions' => [
'basis/other_lv_plan' => $this->permissionlib->isBerechtigt(('basis/other_lv_plan'))
]
];
if ($isProfilOfAuthUser) {
$viewData['calendar_sync_urls'] = $this->getCalendarSyncUrlData();
}
$profil_data = hasData($profil_data) ? getData($profil_data) : null;
$viewData = array(
'editable'=>$editable,
'profil_data' => $profil_data,
);
$this->terminateWithSuccess($viewData);
}
/**
/**
* update column foto_sperre in public.tbl_person
* @access public
* @param boolean $value new value for the column
@@ -77,9 +87,9 @@ class Profil extends FHCAPI_Controller
*/
public function fotoSperre($value)
{
if(!isset($value)){
$this->terminateWithError("Missing parameter", self::ERROR_TYPE_GENERAL);
}
if (!isset($value)) {
$this->terminateWithError("Missing parameter", self::ERROR_TYPE_GENERAL);
}
$res = $this->PersonModel->update($this->pid, ["foto_sperre" => $value]);
if (isError($res)) {
@@ -87,10 +97,10 @@ class Profil extends FHCAPI_Controller
}
$this->PersonModel->addSelect("foto_sperre");
$res = $this->PersonModel->load($this->pid);
$res = $this->getDataOrTerminateWithError($res);
$this->terminateWithSuccess(current($res));
$res = $this->getDataOrTerminateWithError($res);
$this->terminateWithSuccess(current($res));
}
/**
@@ -109,7 +119,7 @@ class Profil extends FHCAPI_Controller
if (isError($nation_res)) {
$this->terminateWithError("error while trying to query table codex.tbl_nation", self::ERROR_TYPE_GENERAL);
}
$nation_res = $this->getDataOrTerminateWithError($nation_res);
$this->terminateWithSuccess($nation_res);
@@ -117,30 +127,30 @@ class Profil extends FHCAPI_Controller
public function getGemeinden($nation, $zip)
{
if(!isset($nation) || !isset($zip)){
if (!isset($nation) || !isset($zip)) {
echo json_encode(error("Missing parameters"));
return;
}
$this->load->model('codex/Gemeinde_model', "GemeindeModel");
$gemeinde_res = $this->GemeindeModel->getGemeindeByPlz($zip);
if (isError($gemeinde_res)) {
$this->terminateWithError(getError($gemeinde_res),self::ERROR_TYPE_GENERAL);
$this->terminateWithError(getError($gemeinde_res), self::ERROR_TYPE_GENERAL);
}
$gemeinde_res = $this->getDataOrTerminateWithError($gemeinde_res);
/* $gemeinde_res = array_map(function ($obj) {
return $obj->ortschaftsname;
}, $gemeinde_res); */
$this->terminateWithSuccess($gemeinde_res);
$this->terminateWithSuccess($gemeinde_res);
}
/**
* checks whether a specific userID is a mitarbeiter or not (foreword declaration of the function isMitarbeiter in Mitarbeiter_model.php)
* @access public
@@ -150,23 +160,48 @@ class Profil extends FHCAPI_Controller
public function isMitarbeiter($uid)
{
if(!$uid) $this->terminateWithError("No uid provided", self::ERROR_TYPE_GENERAL);
if (!$uid)
$this->terminateWithError("No uid provided", self::ERROR_TYPE_GENERAL);
$result = $this->MitarbeiterModel->isMitarbeiter($uid);
if (isError($result)) {
$this->terminateWithError("error when calling Mitarbeiter_model function isMitarbeiter with uid " . $uid, self::ERROR_TYPE_GENERAL);
}
$result = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($result);
}
// -----------------------------------------------------------------------------------------------------------------
// Private methods
/**
* gets the identifier, phrase, and url for each calendar sync option
* @access private
* @return array array of arrays, where each child array is a sync option
*/
private function getCalendarSyncUrlData()
{
return [
[
"identifier" => "cal_dav",
"labelPhrase" => "profil/calendar_sync_cal_dav",
"url" => APP_ROOT . "webdav/lvplan.php/calendars/" . $this->uid . "/LVPlan-" . $this->uid,
],
[
"identifier" => "cal_dav_principal",
"labelPhrase" => "profil/calendar_sync_cal_dav_principal",
"url" => APP_ROOT . "webdav/lvplan.php/principals/" . $this->uid,
],
[
"identifier" => "i_cal",
"labelPhrase" => "profil/calendar_sync_i_cal",
"url" => APP_ROOT . "webdav/google.php?cal=" . encryptData($this->uid, LVPLAN_CYPHER_KEY) . "&" . microtime(true),
],
];
}
}
@@ -0,0 +1,64 @@
<?php
/**
* Copyright (C) 2024 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (!defined('BASEPATH'))
exit('No direct script access allowed');
class StgOrgLvPlan extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'stgOrgLvPlanViewData' => self::PERM_LOGGED,
]);
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
* fetches view data for stg org lv plan
* @access public
*/
public function stgOrgLvPlanViewData()
{
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$this->StudiengangModel->addOrder('typ');
$this->StudiengangModel->addOrder('kurzbz');
$result = $this->StudiengangModel->loadWhere([
'aktiv' => true
]);
$studiengaenge = $this->getDataOrTerminateWithError($result);
$viewData = array(
'studiengaenge' => $studiengaenge,
);
$this->terminateWithSuccess($viewData);
}
// -----------------------------------------------------------------------------------------------------------------
// Private methods
}
@@ -62,21 +62,36 @@ class Studium extends FHCAPI_Controller
if($this->getDataOrTerminateWithError($this->StudentModel->isStudent(getAuthUID()))){
$studentLehrverband =$this->StudentlehrverbandModel->loadWhere(["student_uid" => getAuthUID(), "studiensemester_kurzbz" => $aktuelles_studiensemester->studiensemester_kurzbz]);
$studentLehrverband = current($this->getDataOrTerminateWithError($studentLehrverband));
$student_studiensemester = $studentLehrverband->studiensemester_kurzbz;
$student_studiengang = $studentLehrverband->studiengang_kz;
$student_semester = $studentLehrverband->semester;
//TODO(Manu) check if use Fallback or just comment out all paramschecks?
//add Fallback: if no LehrverbandData of actual semester, get Data of previous one
if(!hasData($studentLehrverband))
{
$result= $this->StudiensemesterModel->getPreviousFrom($aktuelles_studiensemester->studiensemester_kurzbz);
$data = $this->getDataOrTerminateWithError($result);
$vorheriges_studiensemester = current($data)->studiensemester_kurzbz;
$studentLehrverband =$this->StudentlehrverbandModel->loadWhere(["student_uid" => getAuthUID(), "studiensemester_kurzbz" => $vorheriges_studiensemester]);
}
$studentLehrverband = current(getData($studentLehrverband));
$student_studienplan = $this->getStudienPlanFromPrestudentStatus(getAuthPersonId())->studienplan_id;
if(!isset($parameter_studiensemester))
$parameter_studiensemester = $student_studiensemester;
if(!isset($parameter_studiengang))
$parameter_studiengang = $student_studiengang;
if(!isset($parameter_semester))
$parameter_semester = $student_semester;
if(!isset($parameter_studiensemester)) {
$student_studiensemester = $studentLehrverband->studiensemester_kurzbz;
$parameter_studiensemester = $student_studiensemester;
}
if(!isset($parameter_studiengang)) {
$student_studiengang = $studentLehrverband->studiengang_kz;
$parameter_studiengang = $student_studiengang;
}
if(!isset($parameter_semester)) {
$student_semester = $studentLehrverband->semester;
$parameter_semester = $student_semester;
}
if(!isset($parameter_studienplan))
$parameter_studienplan = $student_studienplan;
$parameter_studienplan = $student_studienplan;
}
if(isset($parameter_studiensemester)){
@@ -96,8 +111,7 @@ class Studium extends FHCAPI_Controller
// fetch studiensemester
$allStudienSemester = $this->getDataOrTerminateWithError($this->StudiensemesterModel->load());
if(isset($parameter_studiensemester) && !empty(array_filter($allStudienSemester, function($studiensemester) use($parameter_studiensemester){
return $studiensemester->studiensemester_kurzbz == $parameter_studiensemester->studiensemester_kurzbz;
}))){
@@ -216,6 +230,8 @@ class Studium extends FHCAPI_Controller
$studienplaene = array_map(function($studienplan){
$orgform = current($this->getDataOrTerminateWithError($this->OrgformModel->loadWhere(["orgform_kurzbz" => $studienplan->orgform_kurzbz])));
$studienplan->orgform_bezeichnung = $orgform->bezeichnung;
// bezeichnung_mehrsprachig
$studienplan->orgform_bezeichnung_english = $orgform->bezeichnung_mehrsprachig[1];
return $studienplan;
},$studienplaene);
return $studienplaene;
+517
View File
@@ -0,0 +1,517 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/*
|--------------------------------------------------------------------------
| Base Site URL
|--------------------------------------------------------------------------
|
| URL to your CodeIgniter root. Typically this will be your base URL,
| WITH a trailing slash:
|
| http://example.com/
|
| If this is not set then CodeIgniter will try guess the protocol, domain
| and path to your installation. However, you should always configure this
| explicitly and never rely on auto-guessing, especially in production
| environments.
|
*/
$config['base_url'] = 'https://c3p0.dev.technikum-wien.at/ma1434/core/FHC-Core/';
/*
|--------------------------------------------------------------------------
| Index File
|--------------------------------------------------------------------------
|
| Typically this will be your index.php file, unless you've renamed it to
| something else. If you are using mod_rewrite to remove the page set this
| variable so that it is blank.
|
*/
$config['index_page'] = 'index.ci.php';
/*
|--------------------------------------------------------------------------
| URI PROTOCOL
|--------------------------------------------------------------------------
|
| This item determines which server global should be used to retrieve the
| URI string. The default setting of 'REQUEST_URI' works for most servers.
| If your links do not seem to work, try one of the other delicious flavors:
|
| 'REQUEST_URI' Uses $_SERVER['REQUEST_URI']
| 'QUERY_STRING' Uses $_SERVER['QUERY_STRING']
| 'PATH_INFO' Uses $_SERVER['PATH_INFO']
|
| WARNING: If you set this to 'PATH_INFO', URIs will always be URL-decoded!
*/
$config['uri_protocol'] = 'REQUEST_URI';
/*
|--------------------------------------------------------------------------
| URL suffix
|--------------------------------------------------------------------------
|
| This option allows you to add a suffix to all URLs generated by CodeIgniter.
| For more information please see the user guide:
|
| http://codeigniter.com/user_guide/general/urls.html
*/
$config['url_suffix'] = '';
/*
|--------------------------------------------------------------------------
| Default Language
|--------------------------------------------------------------------------
|
| This determines which set of language files should be used. Make sure
| there is an available translation if you intend to use something other
| than english.
|
*/
$config['language'] = '';
/*
|--------------------------------------------------------------------------
| Default Character Set
|--------------------------------------------------------------------------
|
| This determines which character set is used by default in various methods
| that require a character set to be provided.
|
| See http://php.net/htmlspecialchars for a list of supported charsets.
|
*/
$config['charset'] = 'UTF-8';
/*
|--------------------------------------------------------------------------
| Enable/Disable System Hooks
|--------------------------------------------------------------------------
|
| If you would like to use the 'hooks' feature you must enable it by
| setting this variable to TRUE (boolean). See the user guide for details.
|
*/
$config['enable_hooks'] = FALSE;
/*
|--------------------------------------------------------------------------
| Class Extension Prefix
|--------------------------------------------------------------------------
|
| This item allows you to set the filename/classname prefix when extending
| native libraries. For more information please see the user guide:
|
| http://codeigniter.com/user_guide/general/core_classes.html
| http://codeigniter.com/user_guide/general/creating_libraries.html
|
*/
$config['subclass_prefix'] = 'FHC_';
/*
|--------------------------------------------------------------------------
| Composer auto-loading
|--------------------------------------------------------------------------
|
| Enabling this setting will tell CodeIgniter to look for a Composer
| package auto-loader script in application/vendor/autoload.php.
|
| $config['composer_autoload'] = TRUE;
|
| Or if you have your vendor/ directory located somewhere else, you
| can opt to set a specific path as well:
|
| $config['composer_autoload'] = '/path/to/vendor/autoload.php';
|
| For more information about Composer, please visit http://getcomposer.org/
|
| Note: This will NOT disable or override the CodeIgniter-specific
| autoloading (application/config/autoload.php)
*/
$config['composer_autoload'] = FALSE;
/*
|--------------------------------------------------------------------------
| Allowed URL Characters
|--------------------------------------------------------------------------
|
| This lets you specify which characters are permitted within your URLs.
| When someone tries to submit a URL with disallowed characters they will
| get a warning message.
|
| As a security measure you are STRONGLY encouraged to restrict URLs to
| as few characters as possible. By default only these are allowed: a-z 0-9~%.:_-
|
| Leave blank to allow all characters -- but only if you are insane.
|
| The configured value is actually a regular expression character group
| and it will be executed as: ! preg_match('/^[<permitted_uri_chars>]+$/i
|
| DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!
|
*/
$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-';
/*
|--------------------------------------------------------------------------
| Enable Query Strings
|--------------------------------------------------------------------------
|
| By default CodeIgniter uses search-engine friendly segment based URLs:
| example.com/who/what/where/
|
| By default CodeIgniter enables access to the $_GET array. If for some
| reason you would like to disable it, set 'allow_get_array' to FALSE.
|
| You can optionally enable standard query string based URLs:
| example.com?who=me&what=something&where=here
|
| Options are: TRUE or FALSE (boolean)
|
| The other items let you set the query string 'words' that will
| invoke your controllers and its functions:
| example.com/index.php?c=controller&m=function
|
| Please note that some of the helpers won't work as expected when
| this feature is enabled, since CodeIgniter is designed primarily to
| use segment based URLs.
|
*/
$config['allow_get_array'] = TRUE;
$config['enable_query_strings'] = FALSE;
$config['controller_trigger'] = 'c';
$config['function_trigger'] = 'm';
$config['directory_trigger'] = 'd';
/*
|--------------------------------------------------------------------------
| Error Logging Threshold
|--------------------------------------------------------------------------
|
| You can enable error logging by setting a threshold over zero. The
| threshold determines what gets logged. Threshold options are:
|
| 0 = Disables logging, Error logging TURNED OFF
| 1 = Error Messages (including PHP errors)
| 2 = Debug Messages
| 3 = Informational Messages
| 4 = All Messages
|
| You can also pass an array with threshold levels to show individual error types
|
| array(2) = Debug Messages, without Error Messages
|
| For a live site you'll usually only enable Errors (1) to be logged otherwise
| your log files will fill up very fast.
|
*/
$config['log_threshold'] = 1;
/*
|--------------------------------------------------------------------------
| Error Logging Directory Path
|--------------------------------------------------------------------------
|
| Leave this BLANK unless you would like to set something other than the default
| application/logs/ directory. Use a full server path with trailing slash.
|
*/
$config['log_path'] = '';
/*
|--------------------------------------------------------------------------
| Log File Extension
|--------------------------------------------------------------------------
|
| The default filename extension for log files. The default 'php' allows for
| protecting the log files via basic scripting, when they are to be stored
| under a publicly accessible directory.
|
| Note: Leaving it blank will default to 'php'.
|
*/
$config['log_file_extension'] = 'log';
/*
|--------------------------------------------------------------------------
| Log File Permissions
|--------------------------------------------------------------------------
|
| The file system permissions to be applied on newly created log files.
|
| IMPORTANT: This MUST be an integer (no quotes) and you MUST use octal
| integer notation (i.e. 0700, 0644, etc.)
*/
$config['log_file_permissions'] = 0644;
/*
|--------------------------------------------------------------------------
| Date Format for Logs
|--------------------------------------------------------------------------
|
| Each item that is logged has an associated date. You can use PHP date
| codes to set your own date formatting
|
*/
$config['log_date_format'] = 'Y-m-d H:i:s';
/*
|--------------------------------------------------------------------------
| Error Views Directory Path
|--------------------------------------------------------------------------
|
| Leave this BLANK unless you would like to set something other than the default
| application/views/errors/ directory. Use a full server path with trailing slash.
|
*/
$config['error_views_path'] = '';
/*
|--------------------------------------------------------------------------
| Cache Directory Path
|--------------------------------------------------------------------------
|
| Leave this BLANK unless you would like to set something other than the default
| application/cache/ directory. Use a full server path with trailing slash.
|
*/
$config['cache_path'] = '';
/*
|--------------------------------------------------------------------------
| Cache Include Query String
|--------------------------------------------------------------------------
|
| Whether to take the URL query string into consideration when generating
| output cache files. Valid options are:
|
| FALSE = Disabled
| TRUE = Enabled, take all query parameters into account.
| Please be aware that this may result in numerous cache
| files generated for the same page over and over again.
| array('q') = Enabled, but only take into account the specified list
| of query parameters.
|
*/
$config['cache_query_string'] = FALSE;
/*
|--------------------------------------------------------------------------
| Encryption Key
|--------------------------------------------------------------------------
|
| If you use the Encryption class, you must set an encryption key.
| See the user guide for more info.
|
| http://codeigniter.com/user_guide/libraries/encryption.html
|
*/
$config['encryption_key'] = '';
/*
|--------------------------------------------------------------------------
| Session Variables
|--------------------------------------------------------------------------
|
| 'sess_driver'
|
| The storage driver to use: files, database, redis, memcached
|
| 'sess_cookie_name'
|
| The session cookie name, must contain only [0-9a-z_-] characters
|
| 'sess_expiration'
|
| The number of SECONDS you want the session to last.
| Setting to 0 (zero) means expire when the browser is closed.
|
| 'sess_save_path'
|
| The location to save sessions to, driver dependent.
|
| For the 'files' driver, it's a path to a writable directory.
| WARNING: Only absolute paths are supported!
|
| For the 'database' driver, it's a table name.
| Please read up the manual for the format with other session drivers.
|
| IMPORTANT: You are REQUIRED to set a valid save path!
|
| 'sess_match_ip'
|
| Whether to match the user's IP address when reading the session data.
|
| 'sess_time_to_update'
|
| How many seconds between CI regenerating the session ID.
| NOTE: Keep it as it is to prevent security issues (https://en.wikipedia.org/wiki/Session_fixation)
|
| 'sess_regenerate_destroy'
|
| Whether to destroy session data associated with the old session ID
| when auto-regenerating the session ID. When set to FALSE, the data
| will be later deleted by the garbage collector.
|
| Other session cookie settings are shared with the rest of the application,
| except for 'cookie_prefix' and 'cookie_httponly', which are ignored here.
|
*/
$config['sess_driver'] = 'files';
$config['sess_cookie_name'] = 'sess_ci_session';
$config['sess_expiration'] = 1800; // Session expires every 30 minutes
$config['sess_save_path'] = NULL;
$config['sess_match_ip'] = FALSE;
$config['sess_time_to_update'] = 300;
$config['sess_regenerate_destroy'] = FALSE;
/*
|--------------------------------------------------------------------------
| Cookie Related Variables
|--------------------------------------------------------------------------
|
| 'cookie_prefix' = Set a cookie name prefix if you need to avoid collisions
| 'cookie_domain' = Set to .your-domain.com for site-wide cookies
| 'cookie_path' = Typically will be a forward slash
| 'cookie_secure' = Cookie will only be set if a secure HTTPS connection exists.
| 'cookie_httponly' = Cookie will only be accessible via HTTP(S) (no javascript)
|
| Note: These settings (with the exception of 'cookie_prefix' and
| 'cookie_httponly') will also affect sessions.
|
*/
$config['cookie_prefix'] = '';
$config['cookie_domain'] = '';
$config['cookie_path'] = '/';
$config['cookie_secure'] = FALSE;
$config['cookie_httponly'] = FALSE;
/*
|--------------------------------------------------------------------------
| Standardize newlines
|--------------------------------------------------------------------------
|
| Determines whether to standardize newline characters in input data,
| meaning to replace \r\n, \r, \n occurrences with the PHP_EOL value.
|
| This is particularly useful for portability between UNIX-based OSes,
| (usually \n) and Windows (\r\n).
|
*/
$config['standardize_newlines'] = FALSE;
/*
|--------------------------------------------------------------------------
| Global XSS Filtering
|--------------------------------------------------------------------------
|
| Determines whether the XSS filter is always active when GET, POST or
| COOKIE data is encountered
|
| WARNING: This feature is DEPRECATED and currently available only
| for backwards compatibility purposes!
|
*/
$config['global_xss_filtering'] = FALSE;
/*
|--------------------------------------------------------------------------
| Cross Site Request Forgery
|--------------------------------------------------------------------------
| Enables a CSRF cookie token to be set. When set to TRUE, token will be
| checked on a submitted form. If you are accepting user data, it is strongly
| recommended CSRF protection be enabled.
|
| 'csrf_token_name' = The token name
| 'csrf_cookie_name' = The cookie name
| 'csrf_expire' = The number in seconds the token should expire.
| 'csrf_regenerate' = Regenerate token on every submission
| 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks
*/
$config['csrf_protection'] = FALSE;
$config['csrf_token_name'] = 'csrf_test_name';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
$config['csrf_expire'] = 7200;
$config['csrf_regenerate'] = TRUE;
$config['csrf_exclude_uris'] = array();
/*
|--------------------------------------------------------------------------
| Output Compression
|--------------------------------------------------------------------------
|
| Enables Gzip output compression for faster page loads. When enabled,
| the output class will test whether your server supports Gzip.
| Even if it does, however, not all browsers support compression
| so enable only if you are reasonably sure your visitors can handle it.
|
| Only used if zlib.output_compression is turned off in your php.ini.
| Please do not use it together with httpd-level output compression.
|
| VERY IMPORTANT: If you are getting a blank page when compression is enabled it
| means you are prematurely outputting something to your browser. It could
| even be a line of whitespace at the end of one of your scripts. For
| compression to work, nothing can be sent before the output buffer is called
| by the output class. Do not 'echo' any values with compression enabled.
|
*/
$config['compress_output'] = FALSE;
/*
|--------------------------------------------------------------------------
| Master Time Reference
|--------------------------------------------------------------------------
|
| Options are 'local' or any PHP supported timezone. This preference tells
| the system whether to use your server's local time as the master 'now'
| reference, or convert it to the configured one timezone. See the 'date
| helper' page of the user guide for information regarding date handling.
|
*/
$config['time_reference'] = 'local';
/*
|--------------------------------------------------------------------------
| Rewrite PHP Short Tags
|--------------------------------------------------------------------------
|
| If your PHP installation does not have short tag support enabled CI
| can rewrite the tags on-the-fly, enabling you to utilize that syntax
| in your view files. Options are TRUE or FALSE (boolean)
|
| Note: You need to have eval() enabled for this to work.
|
*/
$config['rewrite_short_tags'] = FALSE;
/*
|--------------------------------------------------------------------------
| Reverse Proxy IPs
|--------------------------------------------------------------------------
|
| If your server is behind a reverse proxy, you must whitelist the proxy
| IP addresses from which CodeIgniter should trust headers such as
| HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP in order to properly identify
| the visitor's IP address.
|
| You can use both an array or a comma-separated list of proxy addresses,
| as well as specifying whole subnets. Here are a few examples:
|
| Comma-separated: '10.0.1.200,192.168.5.0/24'
| Array: array('10.0.1.200', '192.168.5.0/24')
*/
$config['proxy_ips'] = '';
/*
|--------------------------------------------------------------------------
| FHComplete Build Version
|--------------------------------------------------------------------------
|
| Version Number of the Current Build
| This is used to invalidate Cache for JS and CSS Files
|
| Example: 2019102901
*/
$config['fhcomplete_build_version'] = '2019102903';
+122
View File
@@ -0,0 +1,122 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
defined('DB_HOST') OR require_once './config/system.config.inc.php'; // For CLI-Migrations
/*
| -------------------------------------------------------------------
| DATABASE CONNECTIVITY SETTINGS
| -------------------------------------------------------------------
| This file will contain the settings needed to access your database.
|
| For complete instructions please consult the 'Database Connection'
| page of the User Guide.
|
| -------------------------------------------------------------------
| EXPLANATION OF VARIABLES
| -------------------------------------------------------------------
|
| ['dsn'] The full DSN string describe a connection to the database.
| ['hostname'] The hostname of your database server.
| ['username'] The username used to connect to the database
| ['password'] The password used to connect to the database
| ['database'] The name of the database you want to connect to
| ['dbdriver'] The database driver. e.g.: mysqli.
| Currently supported:
| cubrid, ibase, mssql, mysql, mysqli, oci8,
| odbc, pdo, postgre, sqlite, sqlite3, sqlsrv
| ['dbprefix'] You can add an optional prefix, which will be added
| to the table name when using the Query Builder class
| ['pconnect'] TRUE/FALSE - Whether to use a persistent connection
| ['db_debug'] TRUE/FALSE - Whether database errors should be displayed.
| ['cache_on'] TRUE/FALSE - Enables/disables query caching
| ['cachedir'] The path to the folder where cache files should be stored
| ['char_set'] The character set used in communicating with the database
| ['dbcollat'] The character collation used in communicating with the database
| NOTE: For MySQL and MySQLi databases, this setting is only used
| as a backup if your server is running PHP < 5.2.3 or MySQL < 5.0.7
| (and in table creation queries made with DB Forge).
| There is an incompatibility in PHP with mysql_real_escape_string() which
| can make your site vulnerable to SQL injection if you are using a
| multi-byte character set and are running versions lower than these.
| Sites using Latin-1 or UTF-8 database character set and collation are unaffected.
| ['swap_pre'] A default table prefix that should be swapped with the dbprefix
| ['encrypt'] Whether or not to use an encrypted connection.
|
| 'mysql' (deprecated), 'sqlsrv' and 'pdo/sqlsrv' drivers accept TRUE/FALSE
| 'mysqli' and 'pdo/mysql' drivers accept an array with the following options:
|
| 'ssl_key' - Path to the private key file
| 'ssl_cert' - Path to the public key certificate file
| 'ssl_ca' - Path to the certificate authority file
| 'ssl_capath' - Path to a directory containing trusted CA certificats in PEM format
| 'ssl_cipher' - List of *allowed* ciphers to be used for the encryption, separated by colons (':')
| 'ssl_verify' - TRUE/FALSE; Whether verify the server certificate or not ('mysqli' only)
|
| ['compress'] Whether or not to use client compression (MySQL only)
| ['stricton'] TRUE/FALSE - forces 'Strict Mode' connections
| - good for ensuring strict SQL while developing
| ['ssl_options'] Used to set various SSL options that can be used when making SSL connections.
| ['failover'] array - A array with 0 or more data for connections if the main should fail.
| ['save_queries'] TRUE/FALSE - Whether to "save" all executed queries.
| NOTE: Disabling this will also effectively disable both
| $this->db->last_query() and profiling of DB queries.
| When you run a query, with this setting set to TRUE (default),
| CodeIgniter will store the SQL statement for debugging purposes.
| However, this may cause high memory usage, especially if you run
| a lot of SQL queries ... disable this to avoid that problem.
|
| The $active_group variable lets you choose which connection group to
| make active. By default there is only one group (the 'default' group).
|
| The $query_builder variables lets you determine whether or not to load
| the query builder class.
*/
$active_group = 'default';
$query_builder = TRUE;
$db['default'] = array(
'dsn' => '',
'hostname' => DB_HOST,
'username' => DB_USER,
'password' => DB_PASSWORD,
'port' => DB_PORT,
'database' => DB_NAME,
'dbdriver' => 'postgre',
'dbprefix' => '',
'pconnect' => DB_CONNECT_PERSISTENT,
'db_debug' => (ENVIRONMENT !== 'production'),
'cache_on' => FALSE,
'cachedir' => '',
'char_set' => 'utf8',
'dbcollat' => 'utf8_general_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE
);
$db['system'] = array(
'dsn' => '',
'hostname' => DB_HOST,
'username' => 'fhcomplete',
'password' => 'Fhcomplet3Onc4p1',
'database' => DB_NAME,
'port' => DB_PORT,
'dbschema' => 'public',
'dbdriver' => 'postgre',
'dbprefix' => '',
'pconnect' => DB_CONNECT_PERSISTENT,
'db_debug' => (ENVIRONMENT !== 'production'),
'cache_on' => FALSE,
'cachedir' => '',
'char_set' => 'utf8',
'dbcollat' => 'utf8_general_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE
);
+1 -1
View File
@@ -128,7 +128,7 @@ class AntragLib
return $this->_ci->StudierendenantragstatusModel->resumeAntraegeForAbmeldungStgl($antrag_id);
}
// 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);
$result = $this->_ci->StudierendenantragstatusModel->loadWhere([
'studierendenantrag_id' => $antrag_id,
+27 -6
View File
@@ -40,13 +40,16 @@ class StundenplanLib
* @return stdClass
* @access public
*/
public function getEventsUser($start, $end)
public function getEventsUser($start, $end, $uid = null)
{
$this->_ci =& get_instance();
$this->_ci->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$uid = getAuthUID();
if (!$uid) {
$uid = getAuthUID();
}
if (is_null($uid))
return error("No UID");
@@ -217,7 +220,7 @@ class StundenplanLib
* @param string $ort_kurzbz
* @return stdClass
*/
public function getReservierungen($start_date, $end_date, $ort_kurzbz = '')
public function getReservierungen($start_date, $end_date, $ort_kurzbz = '', $uid = null)
{
$this->_ci =& get_instance();
@@ -228,14 +231,14 @@ class StundenplanLib
$this->_ci->load->model('ressource/Reservierung_model', 'ReservierungModel');
$this->_ci->load->model('ressource/Stundenplan_model', 'StundenplanModel');
$is_mitarbeiter = getData($this->_ci->MitarbeiterModel->isMitarbeiter(getAuthUID()));
$is_mitarbeiter = getData($this->_ci->MitarbeiterModel->isMitarbeiter($uid ?? getAuthUID()));
if ($is_mitarbeiter && empty($ort_kurzbz)) {
// request for personal lvplan show only reservations of logged in user
$reservierungen = $this->_ci->ReservierungModel->getReservierungenMitarbeiter($start_date, $end_date);
$reservierungen = $this->_ci->ReservierungModel->getReservierungenMitarbeiter($start_date, $end_date, $uid);
} else {
// querying the reservierungen
$reservierungen = $this->_ci->ReservierungModel->getReservierungen($start_date, $end_date, $ort_kurzbz);
$reservierungen = $this->_ci->ReservierungModel->getReservierungen($start_date, $end_date, $ort_kurzbz, $uid);
}
if (isError($reservierungen))
@@ -445,6 +448,24 @@ class StundenplanLib
return success($ferienEventsFlattened);
}
public function getEventsStgOrg( $start, $end, $stg_kz, $sem, $verband, $gruppe)
{
$this->_ci =& get_instance();
$this->_ci->load->model('ressource/Stundenplan_model', 'StundenplanModel');
$stundenplan_data = $this->_ci->StundenplanModel->getStundenplanStudiengang($start, $end, $stg_kz, $sem, $verband, $gruppe);
if (isError($stundenplan_data))
return $stundenplan_data;
$stundenplan_data = getData($stundenplan_data) ?? [];
$function_error = $this->expandObjectInformation($stundenplan_data);
if ($function_error)
return $function_error;
return success($stundenplan_data);
}
// start of the private functions ########################################################################################################
// function used to sort an array of studiensemester strings
+3 -3
View File
@@ -234,9 +234,9 @@ class Content_model extends DB_Model
FROM
campus.tbl_content c1
LEFT JOIN
campus.tbl_contentsprache s1 ON c1.content_id=s1.content_id AND s1.sprache=?
campus.tbl_contentsprache s1 ON c1.content_id=s1.content_id AND s1.sprache=? AND sichtbar=true
WHERE
sichtbar=true
c1.aktiv = true
) s2
LEFT JOIN
campus.tbl_contentsprache s3 USING(content_id, sprache)
@@ -277,7 +277,7 @@ class Content_model extends DB_Model
JOIN
campus.tbl_contentsprache s USING(contentsprache_id)
LEFT JOIN
campus.tbl_contentchild k ON(m.content_id=k.content_id)
campus.tbl_contentchild k ON(m.content_id=k.content_id) and c.aktiv = true
WHERE EXISTS (
SELECT 1
FROM campus.tbl_contentgruppe
@@ -11,8 +11,4 @@ class Bookmark_model extends DB_Model
$this->dbTable = 'dashboard.tbl_bookmark';
$this->pk = 'bookmark_id';
}
}
@@ -79,10 +79,10 @@ class Paabgabe_model extends DB_Model
JOIN public.tbl_benutzer ON (public.tbl_benutzer.uid = student_uid)
JOIN public.tbl_person USING (person_id)
WHERE (campus.tbl_paabgabe.insertamum >= NOW() - INTERVAL ?
OR campus.tbl_paabgabe.updateamum >= NOW() - INTERVAL ?)
AND campus.tbl_paabgabe.paabgabetyp_kurzbz IN ?";
WHERE (campus.tbl_paabgabe.insertamum::date = CURRENT_DATE - INTERVAL ?
OR campus.tbl_paabgabe.updateamum::date = CURRENT_DATE - INTERVAL ?)
AND campus.tbl_paabgabe.paabgabetyp_kurzbz IN ?";
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)
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) {
$query .= " AND campus.tbl_paabgabe.paabgabetyp_kurzbz IN ?";
@@ -18,10 +18,10 @@ class Reservierung_model extends DB_Model
*
* @return stdClass
*/
public function getReservierungen($start_date, $end_date, $ort_kurzbz = null)
public function getReservierungen($start_date, $end_date, $ort_kurzbz = null, $uid = null)
{
$lvplan_reservierungen_query="SELECT r.* , stund.beginn, stund.ende,
$lvplan_reservierungen_query = "SELECT r.* , stund.beginn, stund.ende,
CASE
WHEN r.gruppe_kurzbz IS NOT NULL THEN r.gruppe_kurzbz
ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(r.semester AS varchar),'/'),COALESCE(CAST(r.verband AS varchar),'/'))
@@ -35,7 +35,7 @@ class Reservierung_model extends DB_Model
LEFT JOIN public.tbl_studiensemester ss2 ON slv.studiensemester_kurzbz = ss2.studiensemester_kurzbz AND ss2.start <=r.datum AND ss2.ende >= r.datum
WHERE datum >= ? AND datum <= ? AND (ss1.studiensemester_kurzbz IS NOT NULL
OR ss2.studiensemester_kurzbz IS NOT NULL)";
$raum_reservierungen_query = "SELECT res.*, beginn, ende,
CASE
WHEN res.gruppe_kurzbz IS NOT NULL THEN res.gruppe_kurzbz
@@ -46,9 +46,9 @@ class Reservierung_model extends DB_Model
JOIN lehre.tbl_stunde ON lehre.tbl_stunde.stunde = res.stunde
WHERE res.ort_kurzbz = ? AND datum >= ? AND datum <= ?";
$subquery = is_null($ort_kurzbz)? $lvplan_reservierungen_query:$raum_reservierungen_query;
$query_result= $this->execReadOnlyQuery("
$subquery = is_null($ort_kurzbz) ? $lvplan_reservierungen_query : $raum_reservierungen_query;
$query_result = $this->execReadOnlyQuery("
SELECT
'reservierung' as type, beginn, ende, datum,
COALESCE(titel, beschreibung) as topic,
@@ -59,15 +59,15 @@ class Reservierung_model extends DB_Model
FROM
(
". $subquery ."
" . $subquery . "
) AS subquery
GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung
ORDER BY datum, beginn
", is_null($ort_kurzbz) ?[getAuthUID(), getAuthUID(),$start_date,$end_date]: [$ort_kurzbz, $start_date, $end_date]);
", is_null($ort_kurzbz) ? [$uid ?? getAuthUID(), $uid ?? getAuthUID(), $start_date, $end_date] : [$ort_kurzbz, $start_date, $end_date]);
return $query_result;
}
@@ -76,7 +76,7 @@ class Reservierung_model extends DB_Model
*
* @return stdClass
*/
public function getReservierungenMitarbeiter($start_date, $end_date)
public function getReservierungenMitarbeiter($start_date, $end_date, $uid = null)
{
$raum_reservierungen_query = "SELECT res.*, beginn, ende,
@@ -91,8 +91,8 @@ class Reservierung_model extends DB_Model
$subquery = $raum_reservierungen_query;
$query_result= $this->execReadOnlyQuery("
$query_result = $this->execReadOnlyQuery("
SELECT
'reservierung' as type, beginn, ende, datum,
COALESCE(titel, beschreibung) as topic,
@@ -103,13 +103,13 @@ class Reservierung_model extends DB_Model
FROM
(
". $subquery ."
" . $subquery . "
) AS subquery
GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung
ORDER BY datum, beginn
", [getAuthUID(), $start_date, $end_date]);
", [$uid ?? getAuthUID(), $start_date, $end_date]);
return $query_result;
@@ -129,9 +129,9 @@ class Reservierung_model extends DB_Model
$this->addJoin('public.tbl_studiensemester ss2', 'slv.studiensemester_kurzbz=ss2.studiensemester_kurzbz AND ss2.start<=r.datum AND ss2.ende>=r.datum', 'LEFT');
$this->db->or_where('ss1.studiensemester_kurzbz IS NOT NULL', null, false);
$this->db->or_where('ss2.studiensemester_kurzbz IS NOT NULL', null, false);
$query = $this->db->get_compiled_select('campus.vw_reservierung r');
return $this->execQuery($query, [$uid, $uid]);
}
@@ -388,6 +388,84 @@ class Stundenplan_model extends DB_Model
ORDER BY datum, beginn", [$start_date, $end_date, $ma_uid]);
}
/**
* queries Stundenplan and filters by studiengang, semester, verband gruppe
*
* @return void
*/
public function getStundenplanStudiengang($start_date, $end_date, $stg_kz, $sem, $verband, $gruppe) {
$qry_params = [$start_date, $end_date, $stg_kz];
$qry = "
SELECT
'lehreinheit' as type, beginn, ende, datum,
CONCAT(lehrfach,'-',lehrform) as topic,
array_agg(DISTINCT lektor) as lektor,
array_agg(DISTINCT (gruppe,verband,semester,studiengang_kz,gruppen_kuerzel)) as gruppe,
string_agg(DISTINCT ort_kurzbz, '/') as ort_kurzbz,
array_agg(DISTINCT lehreinheit_id) as lehreinheit_id,
titel, lehrfach, lehrform, lehrfach_bez, organisationseinheit, farbe, lehrveranstaltung_id
FROM
(
SELECT unr,datum,beginn, ende,
CASE
WHEN sp.mitarbeiter_kurzbz IS NOT NULL THEN sp.mitarbeiter_kurzbz
ELSE sp.lektor
END as lektor,
CASE
WHEN gruppe_kurzbz IS NOT NULL THEN gruppe_kurzbz
ELSE CONCAT(UPPER(sp.stg_typ),UPPER(sp.stg_kurzbz),'-',COALESCE(CAST(sp.semester AS varchar),'/'),COALESCE(CAST(sp.verband AS varchar),'/'))
END as gruppen_kuerzel,
(SELECT bezeichnung
FROM public.tbl_organisationseinheit
WHERE oe_kurzbz IN(
SELECT oe_kurzbz
FROM lehre.tbl_lehrveranstaltung
WHERE lehrveranstaltung_id = sp.lehrveranstaltung_id
)) as organisationseinheit,
sp.ort_kurzbz, sp.studiengang_kz, sp.titel,sp.lehreinheit_id,sp.lehrfach_id,sp.anmerkung,fix,lehrveranstaltung_id,stg_kurzbzlang,stg_bezeichnung,stg_typ,fachbereich_kurzbz,lehrfach,lehrfach_bez,farbe,lehrform,anmerkung_lehreinheit,gruppe, verband, semester,stg_kurzbz
FROM (
SELECT sp.*
FROM lehre.vw_stundenplan sp
WHERE
sp.datum >= ?
AND sp.datum <= ?
) sp
JOIN lehre.tbl_stunde ON lehre.tbl_stunde.stunde = sp.stunde
WHERE studiengang_kz = ? ";
if($sem != NULL)
{
$qry_params[] = $sem;
$qry .= " AND (semester = ? OR semester IS NULL)";
}
if($verband != NULL)
{
$qry_params[] = $verband;
$qry .= " AND (verband = ? OR verband IS NULL OR verband = '0' OR verband = '')";
}
if($gruppe != NULL)
{
$qry_params[] = $gruppe;
$qry .= " AND (gruppe = ? OR gruppe IS NULL OR gruppe = '0' OR gruppe = '') ";
}
$qry.= " AND (
gruppe_kurzbz is null OR EXISTS(
SELECT 1
FROM
public.tbl_gruppe WHERE gruppe_kurzbz = sp.gruppe_kurzbz AND direktinskription = false
)
)";
$qry.= " ) as subquery
GROUP BY unr, datum, beginn, ende, titel, lehrform, lehrfach, lehrfach_bez, organisationseinheit, farbe, lehrveranstaltung_id
ORDER BY datum, beginn; ";
return $this->execReadOnlyQuery($qry, $qry_params);
}
/**
* NO STANDALONE FUNCTION - Generates a SQL query string to fetch 'stundenplan' events for a specific student within the current semester.
@@ -23,12 +23,14 @@ $includesArray = array(
'public/css/components/FormUnderline.css',
'public/css/components/abgabetool/abgabe.css',
'public/css/Cis4/Cms.css',
'public/css/Cis4/Studium.css',
'public/css/Cis4/Studium.css'
),
'customJSs' => array(
'vendor/npm-asset/primevue/accordion/accordion.min.js',
'vendor/npm-asset/primevue/accordiontab/accordiontab.min.js',
'vendor/npm-asset/primevue/checkbox/checkbox.min.js',
'vendor/npm-asset/primevue/chips/chips.min.js',
'vendor/npm-asset/primevue/multiselect/multiselect.min.js',
'vendor/npm-asset/primevue/inputnumber/inputnumber.min.js',
'vendor/npm-asset/primevue/speeddial/speeddial.min.js',
'vendor/npm-asset/primevue/textarea/textarea.min.js',
@@ -39,7 +41,7 @@ $includesArray = array(
'vendor/moment/luxonjs/luxon.min.js'
),
'customJSModules' => array(
'public/js/apps/Cis.js',
'public/js/apps/Cis/Cis.js',
),
);
@@ -47,8 +49,6 @@ $includesArray = array(
$this->load->view('templates/CISVUE-Header', $includesArray);
?>
<div id="fhccontent" class="h-100" route=<?php echo $route ?>>
<router-view
:view-data='<?php echo json_encode($viewData) ?>'
></router-view>
<router-view></router-view>
</div>
<?php $this->load->view('templates/CISVUE-Footer', $includesArray); ?>
+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/style.css.php" type="text/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="../../../include/js/jquery.ui.datepicker.translation.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';
// Load Addons to get Moodle_Path
@@ -71,7 +72,7 @@ echo '
$("#myTable").tablesorter(
{
sortList: [[0,0],[1,0]],
widgets: [\'zebra\']
widgets: [\'zebra\',\'filter\']
});
}
);
@@ -151,8 +152,9 @@ foreach($service->result as $row)
$person = new person();
$person->getPersonFromBenutzer($row->operativ_uid);
$operativ = $person->nachname.' '.$person->vorname;
$oeBez = new organisationseinheit($row->oe_kurzbz);
echo '<tr>';
echo '<td>',$row->oe_kurzbz,'</td>';
echo '<td>',$oeBez->bezeichnung,'</td>';
echo '<td><b>'.$row->bezeichnung.'</b></td>';
echo '<td>',$row->beschreibung,'</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;
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>';
}
@@ -342,6 +342,8 @@ echo '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>';
<vbox>
<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-entwicklungsteam_id" hidden="true" />
<groupbox id="mitarbeiter-entwicklungsteam-detail-groupbox" flex="1">
<caption label="Details" />
<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-datum-beginn').value=beginn;
document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-ende').value=ende;
document.getElementById('mitarbeiter-entwicklungsteam-detail-entwicklungsteam_id').value=entwicklungsteam_id;
MitarbeiterEntwicklungsteamDetailDisableFields(false);
}
@@ -1725,6 +1726,7 @@ function MitarbeiterEntwicklungsteamSpeichern()
studiengang_kz_old = document.getElementById('mitarbeiter-entwicklungsteam-detail-textbox-studiengang').value;
beginn = document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-beginn').value;
ende = document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-ende').value;
entwicklungsteam_id = document.getElementById('mitarbeiter-entwicklungsteam-detail-entwicklungsteam_id').value;
if(studiengang_kz=='')
{
+8
View File
@@ -3555,6 +3555,14 @@ function StudentZeugnisDokumentArchivieren()
case 'microcredential_2':
case 'microcredential_3':
case 'microcredential_4':
case 'microdegree_1':
case 'microdegree_2':
case 'microdegree_3':
case 'microdegree_4':
case 'microdegreeabschluss_1':
case 'microdegreeabschluss_2':
case 'microdegreeabschluss_3':
case 'microdegreeabschluss_4':
xml = 'microcredential.xml.php';
break;
+3 -1
View File
@@ -364,9 +364,10 @@ class entwicklungsteam extends basis_db
$bismeldung_jahr = $datetime->format('Y');
//laden des Datensatzes
$qry = "SELECT *
$qry = "SELECT tbl_entwicklungsteam.*, tbl_besqual.*, tbl_studiengang.studiengang_kz, tbl_studiengang.melderelevant
FROM bis.tbl_entwicklungsteam
JOIN bis.tbl_besqual USING(besqualcode)
JOIN public.tbl_studiengang USING(studiengang_kz)
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 (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->ext_id = $row->ext_id;
$obj->besqual = $row->besqualbez;
$obj->melderelevant = $this->db_parse_bool($row->melderelevant);
$this->result[] = $obj;
}
+4 -2
View File
@@ -584,8 +584,9 @@ class lehreinheitmitarbeiter extends basis_db
$qry = '
WITH semester_sws_tbl AS (
SELECT DISTINCT lehreinheit_id, studiensemester_kurzbz, lema.semesterstunden,
stg.studiengang_kz, stg.melde_studiengang_kz, stg.lgartcode
SELECT
DISTINCT lehreinheit_id, studiensemester_kurzbz, lema.semesterstunden,
stg.studiengang_kz, stg.melde_studiengang_kz, stg.lgartcode, stg.melderelevant
FROM lehre.tbl_lehreinheitmitarbeiter lema
JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id)
@@ -598,6 +599,7 @@ class lehreinheitmitarbeiter extends basis_db
AND ss.studiensemester_kurzbz IN ('.$this->implode4SQL($studiensemester_kurzbz_arr).')
-- nur lehre, die bisgemeldet wird
AND lema.bismelden
AND stg.melderelevant
-- keine lehreinheiten ohne semesterstunden
AND lema.semesterstunden != 0
)
+6
View File
@@ -0,0 +1,6 @@
{
"name": "FHC-Core",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}
+11 -1
View File
@@ -147,6 +147,8 @@ html {
--fhc-cis-menu-lvl-5-color-hover: var(--fhc-text);
--fhc-cis-grade-positive: var(--fhc-success);
--fhc-cis-grade-negative: var(--fhc-danger);
--fhc-offcanvas-zindex: 1045;
}
#themeSwitch i{
@@ -375,7 +377,7 @@ html {
/* searchbar */
#nav-search {
background-color: var(--fhc-primary);
z-index: 1;
z-index: calc(var(--fhc-offcanvas-zindex) + 1) !important;
}
#nav-search.me-3 {
margin: 0 !important;
@@ -413,10 +415,18 @@ html {
color: var(--fhc-link) !important;
}
#nav-main {
z-index: var(--fhc-offcanvas-zindex);
}
#nav-main-sticky {
max-height: calc(100vh - var(--fhc-cis-header-height));
}
#nav-user-menu {
z-index: calc(var(--fhc-offcanvas-zindex) + 1);
}
#nav-user-menu img {
object-fit: cover;
height: calc( 3 * var(--fhc-cis-header-py));
+1 -1
View File
@@ -19,7 +19,7 @@ export default {
getViewData() {
return {
method: 'get',
url: '/api/frontend/v1/Cis4FhcApi/getViewData'
url: '/api/frontend/v1/Cis4FhcApi/dashboardViewData'
};
}
};
+26
View File
@@ -0,0 +1,26 @@
/**
* 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 {
getAllStudienSemester(studiensemester, studiengang, semester, studienplan) {
return {
method: 'get',
url: 'api/frontend/v1/Studium/getStudienAllSemester/',
params: {studiensemester, studiengang, semester, studienplan}
};
},
}
+48 -5
View File
@@ -16,6 +16,12 @@
*/
export default {
getMyLvPlanViewData() {
return {
method: 'get',
url: `/api/frontend/v1/LvPlan/myLvPlanViewData`,
};
},
getRoomInfo(ort_kurzbz, start_date, end_date) {
return {
method: 'post',
@@ -30,11 +36,11 @@ export default {
params: { start_date, end_date, lv_id }
};
},
eventsPersonal(start_date, end_date) {
eventsPersonal(start_date, end_date, uid = null) {
return {
method: 'post',
url: '/api/frontend/v1/lvPlan/eventsPersonal',
params: { start_date, end_date }
params: { start_date, end_date, uid }
};
},
eventsLv(lv_id, start_date, end_date) {
@@ -57,11 +63,11 @@ export default {
params: { start_date, end_date }
};
},
getLvPlanReservierungen(start_date, end_date) {
getLvPlanReservierungen(start_date, end_date, uid = null) {
return {
method: 'post',
url: '/api/frontend/v1/LvPlan/getReservierungen',
params: { start_date, end_date }
params: { start_date, end_date, uid }
};
},
getLehreinheitStudiensemester(lehreinheit_id) {
@@ -92,5 +98,42 @@ export default {
method: 'get',
url: '/api/frontend/v1/LvPlan/getLv/' + lehrveranstaltung_id
};
}
},
eventsStgOrg(start_date, end_date, stg_kz, sem, verband, gruppe) {
return {
method: 'post',
url: '/api/frontend/v1/lvPlan/eventsStgOrg',
params: { start_date, end_date, stg_kz, sem, verband, gruppe }
};
},
getStudiengaenge(){
return {
method: 'get',
url: '/api/frontend/v1/lvPlan/getStudiengaenge'
}
},
getLehrverband(stg_kz, sem){
return {
method: 'get',
url: `/api/frontend/v1/lvPlan/getLehrverband/${stg_kz}/${sem}`
}
},
getGruppe(stg_kz, sem, verband){
return {
method: 'get',
url: `/api/frontend/v1/lvPlan/getLehrverband/${stg_kz}/${sem}/${verband}`
}
},
checkPermissionOtherLvPlan(){
return {
method: 'get',
url: '/api/frontend/v1/lvPlan/permissionOtherLvPlan',
}
},
getCompactibleEventTypes(){
return {
method: 'get',
url: '/api/frontend/v1/lvPlan/compactibleEventTypes',
}
},
};
+25
View File
@@ -0,0 +1,25 @@
/**
* 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 {
getOtherLvPlanViewData(uid) {
return {
method: 'get',
url: `/api/frontend/v1/OtherLvPlan/otherLvPlanViewData/${uid}`,
};
},
};
+1 -1
View File
@@ -17,7 +17,7 @@
export default {
profilViewData(uid) {
getProfilViewData(uid = null) {
let url = "/api/frontend/v1/Profil/profilViewData";
if(uid){
url += `/${uid}`;
+25
View File
@@ -0,0 +1,25 @@
/**
* 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 {
getStgOrgLvPlanViewData(uid) {
return {
method: 'get',
url: `/api/frontend/v1/StgOrgLvPlan/stgOrgLvPlanViewData`,
};
},
};
+11 -4
View File
@@ -17,6 +17,7 @@
export default {
getBookmarks() {
return {
method: 'get',
url: '/api/frontend/v1/Bookmark/getBookmarks'
@@ -28,18 +29,24 @@ export default {
url: `/api/frontend/v1/Bookmark/delete/${bookmark_id}`
};
},
update({ bookmark_id, url, title, tag=null }) {
update({ bookmark_id, url, title, tag }) {
return {
method: 'post',
url: `/api/frontend/v1/Bookmark/update/${bookmark_id}`,
params: { url, title }
params: { url, title, tag }
};
},
insert({ url, title, tag }) {
insert({ url, title, tag, sort }) {
return {
method: 'post',
url: `/api/frontend/v1/Bookmark/insert`,
params: { url, title, tag }
params: { url, title, tag, sort }
};
},
changeOrder(bookmark_id1, bookmark_id2) {
return {
method: 'post',
url: `/api/frontend/v1/Bookmark/changeOrder/${bookmark_id1}/${bookmark_id2}`,
};
}
};
@@ -1,27 +1,30 @@
import FhcDashboard from '../components/Dashboard/Dashboard.js';
import PluginsPhrasen from '../plugins/Phrasen.js';
import Theme from '../plugins/Theme.js';
import contrast from '../directives/contrast.js';
import {setScrollbarWidth} from "../helpers/CssVarCalcHelpers.js";
import LvPlan from "../components/Cis/LvPlan/Lehrveranstaltung.js";
import MyLvPlan from "../components/Cis/LvPlan/Personal.js";
import MylvStudent from "../components/Cis/Mylv/Student.js";
import Profil from "../components/Cis/Profil/Profil.js";
import Raumsuche from "../components/Cis/Raumsuche/Raumsuche.js";
import CmsNews from "../components/Cis/Cms/News.js";
import CmsContent from "../components/Cis/Cms/Content.js";
import Info from "../components/Cis/Mylv/Semester/Studiengang/Lv/Info.js";
import RoomInformation, {DEFAULT_MODE_RAUMINFO} from "../components/Cis/Mylv/RoomInformation.js";
import AbgabetoolStudent from "../components/Cis/Abgabetool/AbgabetoolStudent.js";
import AbgabetoolMitarbeiter from "../components/Cis/Abgabetool/AbgabetoolMitarbeiter.js";
import AbgabetoolAssistenz from "../components/Cis/Abgabetool/AbgabetoolAssistenz.js";
import DeadlineOverview from "../components/Cis/Abgabetool/DeadlineOverview.js";
import Studium from "../components/Cis/Studium/Studium.js";
import FhcDashboard from '../../components/Dashboard/Dashboard.js';
import PluginsPhrasen from '../../plugins/Phrasen.js';
import Theme from '../../plugins/Theme.js';
import contrast from '../../directives/contrast.js';
import {setScrollbarWidth} from "../../helpers/CssVarCalcHelpers.js";
import LvPlan from "../../components/Cis/LvPlan/Lehrveranstaltung.js";
import MyLvPlan from "../../components/Cis/LvPlan/MyLvPlan.js";
import MylvStudent from "../../components/Cis/Mylv/Student.js";
import Profil from "../../components/Cis/Profil/Profil.js";
import Raumsuche from "../../components/Cis/Raumsuche/Raumsuche.js";
import CmsNews from "../../components/Cis/Cms/News.js";
import CmsContent from "../../components/Cis/Cms/Content.js";
import Info from "../../components/Cis/Mylv/Semester/Studiengang/Lv/Info.js";
import RoomInformation, {DEFAULT_MODE_RAUMINFO_DESKTOP, DEFAULT_MODE_RAUMINFO_MOBILE} from "../../components/Cis/Mylv/RoomInformation.js";
import AbgabetoolStudent from "../../components/Cis/Abgabetool/AbgabetoolStudent.js";
import AbgabetoolMitarbeiter from "../../components/Cis/Abgabetool/AbgabetoolMitarbeiter.js";
import AbgabetoolAssistenz from "../../components/Cis/Abgabetool/AbgabetoolAssistenz.js";
import DeadlineOverview from "../../components/Cis/Abgabetool/DeadlineOverview.js";
import Studium from "../../components/Cis/Studium/Studium.js";
import StgOrgLvPlan from "../../components/Cis/LvPlan/StgOrg.js";
import OtherLvPlan from "../../components/Cis/LvPlan/OtherLvPlan.js";
import ApiRouteInfo from '../api/factory/routeinfo.js';
import {capitalize} from "../helpers/StringHelpers.js";
import ApiRouteInfo from '../../api/factory/routeinfo.js';
import {capitalize} from "../../helpers/StringHelpers.js";
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
const isMobile = window.matchMedia("(max-width: 767px)").matches;
const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(`/${ciPath}`),
@@ -84,7 +87,7 @@ const router = VueRouter.createRouter({
name: "RoomInformation",
params: { // in this case always populate other params since they are not optional
ort_kurzbz: to.params.ort_kurzbz,
mode: DEFAULT_MODE_RAUMINFO,
mode: isMobile ? DEFAULT_MODE_RAUMINFO_MOBILE : DEFAULT_MODE_RAUMINFO_DESKTOP,
focus_date: new Date().toISOString().split("T")[0]
},
};
@@ -101,7 +104,7 @@ const router = VueRouter.createRouter({
const mode = route.params.mode &&
validModes.includes(route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase())
? route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase()
: DEFAULT_MODE_RAUMINFO;
: (isMobile ? DEFAULT_MODE_RAUMINFO_MOBILE : DEFAULT_MODE_RAUMINFO_DESKTOP);
// default to today date if not provided
const d = new Date(route.params.focus_date)
@@ -196,6 +199,26 @@ const router = VueRouter.createRouter({
};
}
},
{
path: `/Cis/StgOrgLvPlan/:mode?/:focus_date?/:stgkz?/:sem?/:verband?/:gruppe?`,
name: 'StgOrgLvPlan',
component: StgOrgLvPlan,
props(route) {
return {
propsViewData: route.params
};
}
},
{
path: `/Cis/OtherLvPlan/:otherUid/:mode?/:focus_date?`,
name: "OtherLvPlan",
component: OtherLvPlan,
props(route) {
return {
propsViewData: route.params
};
}
},
{
path: `/Cis4`,
name: 'Cis4',
@@ -228,7 +251,8 @@ const router = VueRouter.createRouter({
const app = Vue.createApp({
name: 'CisApp',
data: () => ({
appSideMenuEntries: {}
appSideMenuEntries: {},
renderers: null,
}),
components: {},
computed: {
@@ -236,12 +260,12 @@ const app = Vue.createApp({
const smallScreen = window.matchMedia("(max-width: 767px)").matches;
const touchCapable = ("ontouchstart" in window) || navigator.maxTouchPoints > 0;
return smallScreen;// && touchCapable;
}
},
},
provide() {
return { // provide injectable & watchable language property
language: Vue.computed(() => this.$p.user_language),
isMobile: this.isMobile
isMobile: this.isMobile,
}
},
methods: {
@@ -310,4 +334,4 @@ app.mount('#fhccontent');
router.afterEach((to, from, failure) => {
app.config.globalProperties.$api.call(ApiRouteInfo.info('cis4', to.fullPath));
});
});
+45 -2
View File
@@ -1,8 +1,9 @@
import FhcSearchbar from "../../components/searchbar/searchbar.js";
import CisMenu from "../../components/Cis/Menu.js";
import PluginsPhrasen from '../../plugins/Phrasen.js';
import ApiSearchbar from '../../api/factory/searchbar.js';
import Theme from "../../plugins/Theme.js";
import ApiSearchbar from '../../api/factory/searchbar.js';
import ApiLvPlan from "../../api/factory/lvPlan.js";
const app = Vue.createApp({
name: 'CisMenuApp',
@@ -136,11 +137,53 @@ const app = Vue.createApp({
}
};
},
computed: {
isMobile() {
const smallScreen = window.matchMedia("(max-width: 767px)").matches;
const touchCapable = ("ontouchstart" in window) || navigator.maxTouchPoints > 0;
return smallScreen;// && touchCapable;
},
},
provide() {
return {
isMobile: this.isMobile
}
},
methods: {
searchfunction: function(searchsettings) {
return this.$api.call(ApiSearchbar.searchCis(searchsettings));
}
}
},
async mounted() {
const openOtherLvPlanAction = {
label: Vue.computed(() => this.$p.t("lehre/stundenplan")),
icon: "fas fa-calendar-days",
type: "link",
action: function (data) {
const uid = JSON.parse(data.data).uid;
const link =
FHC_JS_DATA_STORAGE_OBJECT.app_root +
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
"/Cis/OtherLvPlan/" +
uid;
return link;
},
};
let checkPermissionOtherLvPlanResult = await this.$api.call(
ApiLvPlan.checkPermissionOtherLvPlan(),
);
if (
checkPermissionOtherLvPlanResult.meta.status === "success" &&
checkPermissionOtherLvPlanResult.data
) {
this.searchbaroptions.actions.employee.childactions.push(
openOtherLvPlanAction,
);
this.searchbaroptions.actions.student.childactions.push(
openOtherLvPlanAction,
);
}
},
});
FhcApps.makeExtendable(app);
+6
View File
@@ -18,6 +18,12 @@ const app = Vue.createApp({
};
}
});
app.use(primevue.config.default, {
zIndex: {
overlay: 9000,
tooltip: 8000
}
})
app.use(PluginsPhrasen);
app.directive('tooltip', primevue.tooltip);
app.mount('#main');
+120 -37
View File
@@ -1,5 +1,5 @@
import LineEvent from './Line/Event.js';
import LineBackground from './Line/Background.js';
import LineEvent from "./Line/Event.js";
import LineBackground from "./Line/Background.js";
/**
* TODO(chris):
@@ -10,54 +10,117 @@ export default {
name: "GridLine",
components: {
LineEvent,
LineBackground
},
inject: {
axisRow: "axisRow"
LineBackground,
},
inject: ["axisRow", "shouldCompactEvents", "compactibleEventTypes"],
props: {
date: {
type: luxon.DateTime,
required: true
required: true,
},
start: {
type: luxon.DateTime,
required: true
required: true,
},
end: {
type: luxon.DateTime,
required: true
required: true,
},
events: {
type: Array,
default: []
default: [],
},
backgrounds: {
type: Array,
default: []
}
default: [],
},
},
computed: {
eventsWithRowInfo() {
const events = [];
this.events.forEach(event => {
const rows = [1, -1];
formattedEvents() {
let formattedEvents = this.events.map((event) => {
event.rows = [1, -1];
if (event.startsHere) {
rows[0] = 't_' + event.start.diff(this.date).toMillis();
event.rows[0] =
"t_" + event.start.diff(this.date).toMillis();
}
if (event.endsHere) {
rows[1] = 't_' + event.end.diff(this.date).toMillis();
event.rows[1] = "t_" + event.end.diff(this.date).toMillis();
}
events.push({
...event,
rows
});
return event;
});
return events;
}
if (this.shouldCompactEvents && this.compactibleEventTypes?.length) {
formattedEvents =
this.compactEvents(formattedEvents, this.compactibleEventTypes);
}
return formattedEvents;
},
},
template: /* html */`
methods: {
compactEvents(events, compactibleEventTypes) {
let formattedEvents = events
.filter(
(event) =>
!compactibleEventTypes.includes(event.type),
)
.map((event) => {
event.display = "default";
return event;
});
let eventsToBeCompacted = events.filter((event) =>
compactibleEventTypes.includes(event.type),
);
let compactedEvents = [];
eventsToBeCompacted.forEach((event) => {
let existingCompactedEvent = compactedEvents.find(
(compactedEvent) =>
event.rows[0] === compactedEvent.rows[0] &&
event.rows[1] === compactedEvent.rows[1],
);
if (!existingCompactedEvent) {
compactedEvents.push({
events: [
{
farbe: event.orig.farbe,
},
],
rows: event.rows,
});
} else {
existingCompactedEvent.events.push({
farbe: event.orig.farbe,
});
}
});
compactedEvents.forEach((compactedEvent) => {
if (compactedEvent.events.length < 4) {
formattedEvents.push({
display: "compacted",
...compactedEvent,
});
} else {
formattedEvents.push({
display: "compacted",
events: compactedEvent.events.slice(0, 3),
rows: compactedEvent.rows,
});
formattedEvents.push({
display: "compactedExtra",
events: compactedEvent.events.slice(3),
rows: compactedEvent.rows,
});
}
});
return formattedEvents;
},
},
template: /* html */ `
<div
class="fhc-calendar-base-grid-line"
style="position:relative;display:grid;grid-auto-flow:dense"
@@ -69,17 +132,37 @@ export default {
:end="end"
:background="bg"
></line-background>
<line-event
v-for="(event, i) in eventsWithRowInfo"
:key="i"
:style="'grid-' + axisRow + ': ' + event.rows.join('/')"
:event="event"
>
<template v-slot="slot">
<slot name="event" v-bind="slot" />
</template>
</line-event>
<template v-for="(event, i) in formattedEvents" :key="i">
<line-event
v-if="!event.display || event.display === 'default'"
:style="'grid-' + axisRow + ': ' + event.rows.join('/')"
:event="event"
>
<template v-slot="slot">
<slot name="event" v-bind="slot" />
</template>
</line-event>
<div
v-else-if="event.display === 'compacted'"
:style="'grid-' + axisRow + ': ' + event.rows.join('/')"
class="d-flex flex-row justify-content-center gap-1 align-items-center"
>
<i
v-for="(subEvent, subEventIndex) in event.events"
:key="subEventIndex"
class="fa-solid fa-circle fa-2xs"
:style="subEvent.farbe ? {color: '#' + subEvent.farbe} : {}"
></i>
</div>
<div
v-else-if="event.display === 'compactedExtra'"
:style="'grid-' + axisRow + ': ' + event.rows.join('/')"
class="w-100 d-flex flex-row justify-content-center"
>
{{"+" + event.events.length}}
</div>
</template>
<slot name="dropzone" />
</div>
`
}
`,
};
+62 -20
View File
@@ -8,12 +8,14 @@ import { useRenderers } from '../../composables/Renderers.js';
import ModeDay from './Mode/Day.js';
import ModeWeek from './Mode/Week.js';
import ModeMonth from './Mode/Month.js';
import ModeList from './Mode/List.js';
export default {
name: "CalendarLvPlan",
components: {
FhcCalendar
},
inject: ["isMobile"],
props: {
date: {
type: [Date, String, Number, luxon.DateTime],
@@ -28,6 +30,16 @@ export default {
required: true
}
},
provide() {
return {
shouldCompactEvents: Vue.computed(
() => this.$props.mode === "Month" && this.isMobile,
),
compactibleEventTypes: Vue.computed(
() => this.compactibleEventTypes,
),
};
},
emits: [
"update:date",
"update:mode",
@@ -36,11 +48,6 @@ export default {
data() {
return {
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
modes: {
day: Vue.markRaw(ModeDay),
week: Vue.markRaw(ModeWeek),
month: Vue.markRaw(ModeMonth)
},
modeOptions: {
day: {
emptyMessage: Vue.computed(() => this.$p.t('lehre/noLvFound')),
@@ -48,9 +55,13 @@ export default {
},
week: {
collapseEmptyDays: false
}
},
list: {
length: 7,
},
},
teachingunits: null
teachingunits: null,
compactibleEventTypes: [],
};
},
computed: {
@@ -72,7 +83,20 @@ export default {
label: now.startOf('minute').toISOTime({ suppressSeconds: true, includeOffset: false })
}
];
}
},
modes() {
let modes = {
day: Vue.markRaw(ModeDay),
month: Vue.markRaw(ModeMonth),
};
if (this.isMobile) {
modes.list = Vue.markRaw(ModeList);
} else {
modes.week = Vue.markRaw(ModeWeek);
}
return modes;
},
},
methods: {
eventStyle(event) {
@@ -83,12 +107,29 @@ export default {
updateRange(rangeInterval) {
this.rangeInterval = rangeInterval;
this.$emit('update:range', rangeInterval);
}
},
resetEventLoader() {
this.reset();
},
async getStunden() {
let stundenResponse = await this.$api.call(ApiLvPlan.getStunden());
this.teachingunits = stundenResponse.data.map((el) => ({
id: el.stunde,
start: el.beginn,
end: el.ende,
}));
},
async getCompactibleEventTypes() {
let compactibleEventTypesResponse = await this.$api.call(
ApiLvPlan.getCompactibleEventTypes(),
);
this.compactibleEventTypes = compactibleEventTypesResponse.data;
},
},
setup(props, context) {
const rangeInterval = Vue.ref(null);
const { events, lv } = useEventLoader(rangeInterval, props.getPromiseFunc);
const { events, lv, reset } = useEventLoader(rangeInterval, props.getPromiseFunc);
Vue.watch(lv, newValue => {
context.emit('update:lv', newValue);
@@ -100,19 +141,13 @@ export default {
rangeInterval,
events,
lv,
reset,
renderers
};
},
created() {
this.$api
.call(ApiLvPlan.getStunden())
.then(res => {
return this.teachingunits = res.data.map(el => ({
id: el.stunde,
start: el.beginn,
end: el.ende
}));
});
async created() {
await this.getStunden();
await this.getCompactibleEventTypes();
},
template: /* html */`
<fhc-calendar
@@ -134,6 +169,13 @@ export default {
>
<template v-slot="{ event, mode }">
<div
v-if="!event"
class="h-100 d-flex justify-content-center align-items-center"
>
{{ $p.t('lehre/noLvFound') }}
</div>
<div
v-else
:class="'event-type-' + event.type + ' ' + mode + 'PageContainer'"
:type="mode == 'day' ? 'button' : undefined"
:style="eventStyle(event)"
+1 -1
View File
@@ -93,7 +93,7 @@ export default {
mounted() {
this.$emit('update:range', this.range);
},
template: `
template: /*html*/ `
<div
class="fhc-calendar-mode-list flex-grow-1 position-relative"
@cal-click-default.capture="handleClickDefaults"
@@ -42,14 +42,6 @@ export const AbgabetoolAssistenz = {
stg_kz_prop: {
default: null
},
viewData: {
type: Object,
required: true,
default: () => ({name: '', uid: ''}),
validator(value) {
return value && value.uid // && value.name -> extensive viewData use only for cis4 onwards
}
}
},
data() {
return {
@@ -31,16 +31,6 @@ export const AbgabetoolMitarbeiter = {
old_abgabe_beurteilung_link: Vue.computed(() => this.old_abgabe_beurteilung_link)
}
},
props: {
viewData: {
type: Object,
required: true,
default: () => ({name: '', uid: ''}),
validator(value) {
return value && value.uid // && value.name -> extensive viewData use only for cis4 onwards
}
}
},
data() {
return {
tableData: null,
@@ -3,6 +3,7 @@ import ApiAbgabe from '../../../api/factory/abgabe.js'
import BsModal from "../../Bootstrap/Modal.js";
import FhcOverlay from "../../Overlay/FhcOverlay.js";
import { getDateStyleClass} from "./getDateStyleClass.js";
import ApiAuthinfo from "../../../api/factory/authinfo.js";
export const AbgabetoolStudent = {
name: "AbgabetoolStudent",
@@ -24,14 +25,6 @@ export const AbgabetoolStudent = {
student_uid_prop: {
default: null
},
viewData: {
type: Object,
required: true,
default: () => ({uid: ''}),
validator(value) {
return value && value.uid
}
}
},
data() {
return {
@@ -44,9 +37,18 @@ export const AbgabetoolStudent = {
detail: null,
projektarbeiten: null,
selectedProjektarbeit: null,
moodle_link: null
moodle_link: null,
uid: null,
};
},
computed: {
isViewMode() {
return this.student_uid !== this.uid
},
student_uid() {
return this.student_uid_prop || this.uid || null
}
},
methods: {
checkQualityGatesStrict(termine) {
let qgate1Passed = false
@@ -258,18 +260,11 @@ export const AbgabetoolStudent = {
},
handleDownloadBeurteilung2(projektarbeit) {
window.open(projektarbeit.beurteilung2)
}
},
watch: {
},
computed: {
isViewMode() {
return this.student_uid !== this.viewData.uid
},
student_uid() {
return this.student_uid_prop || this.viewData?.uid || null
}
async fetchAuthUID() {
const authIdResponse = await this.$api.call(ApiAuthinfo.getAuthUID());
this.uid = authIdResponse.data.uid;
},
},
async created() {
this.phrasenPromise = this.$p.loadCategory(['abgabetool', 'global'])
@@ -302,6 +297,8 @@ export const AbgabetoolStudent = {
}).catch(e => {
this.loading = false
})
await this.fetchAuthUID();
},
mounted() {
this.setupMounted()
@@ -10,14 +10,6 @@ export const DeadlineOverview = {
person_uid_prop: {
default: null
},
viewData: {
type: Object,
required: true,
default: () => ({name: '', uid: ''}),
validator(value) {
return value && value.name && value.uid
}
}
},
data() {
return {
+114 -92
View File
@@ -2,115 +2,137 @@ import Pagination from "../../Pagination/Pagination.js";
import StudiengangInformation from "./StudiengangInformation/StudiengangInformation.js";
import BsConfirm from "../../Bootstrap/Confirm.js";
import ApiCms from '../../../api/factory/cms.js';
import ApiCms from "../../../api/factory/cms.js";
export default {
name: "NewsComponent",
components: {
Pagination,
StudiengangInformation,
},
data() {
return {
content: null,
maxPageCount: 0,
page_size: 10,
page:1,
};
},
watch:{
'$p.user_language.value':function(sprache){
this.fetchNews();
}
},
computed:{
sprache: function(){
return this.$p.user_language.value;
components: {
Pagination,
StudiengangInformation,
},
},
methods: {
fetchNews() {
return this.$api
.call(ApiCms.getNews(this.page, this.page_size, this.sprache))
.then(res => res.data)
.then(result => {
this.content = result;
inject: ["isMobile"],
data() {
return {
content: null,
maxPageCount: 0,
page_size: 10,
page: 1,
};
},
watch: {
"$p.user_language.value": function (sprache) {
this.fetchNews();
},
},
computed: {
sprache: function () {
return this.$p.user_language.value;
},
},
methods: {
async fetchNews() {
let newsResponse = await this.$api.call(
ApiCms.getNews(this.page, this.page_size, this.sprache),
);
this.content = newsResponse.data;
document.querySelectorAll("#cms [data-confirm]").forEach((el) => {
el.addEventListener("click", (evt) => {
evt.preventDefault();
BsConfirm.popup(el.dataset.confirm)
.then(() => {
Axios.get(el.href)
.then((res) => {
// TODO(chris): check for success then show message and/or reload
location = location;
})
.catch((err) => console.error("ERROR:", err));
document.querySelectorAll("#cms [data-confirm]").forEach((el) => {
el.addEventListener("click", (evt) => {
evt.preventDefault();
BsConfirm.popup(el.dataset.confirm)
.then(() => {
Axios.get(el.href)
.then((res) => {
// TODO(chris): check for success then show message and/or reload
location = location;
})
.catch(() => {
});
});
});
document.querySelectorAll("#cms [data-href]").forEach((el) => {
el.href = el.dataset.href.replace(
/^ROOT\//,
FHC_JS_DATA_STORAGE_OBJECT.app_root
);
});
Vue.nextTick(()=>{
document.querySelectorAll(".card-header").forEach((el) => {
el.classList.add("fhc-primary");
});
document.querySelectorAll(".row").forEach((el) => {
el.classList.add("w-100");
el.classList.add("align-items-center");
});
document.querySelectorAll(".row h2").forEach((el) => {
el.classList.add("mb-0");
});
.catch((err) => console.error("ERROR:", err));
})
.catch(() => {});
});
});
document.querySelectorAll("#cms [data-href]").forEach((el) => {
el.href = el.dataset.href.replace(
/^ROOT\//,
FHC_JS_DATA_STORAGE_OBJECT.app_root,
);
});
})
await this.$nextTick();
this.formatExternalHtml();
},
async loadNewPageContent(data) {
let newsResponse = await this.$api.call(
ApiCms.getNews(data.page, data.rows),
);
this.content = newsResponse.data;
await this.$nextTick();
this.formatExternalHtml();
},
formatExternalHtml() {
document
.querySelectorAll(".news-list-item .card-header")
.forEach((el) => {
el.classList.add("fhc-primary");
});
document.querySelectorAll(".news-list-item .row").forEach((el) => {
el.classList.add("w-100");
el.classList.add("align-items-center");
});
document
.querySelectorAll(".news-list-item .row h2")
.forEach((el) => {
el.classList.add("mb-0");
});
},
loadNewPageContent(data) {
this.$api
.call(ApiCms.getNews(data.page, data.rows))
.then(res => res.data)
.then(result => {
this.content = result;
});
}
},
created() {
this.fetchNews();
afterPageUpdated(event) {
this.page = event.page;
this.page_size = event.rows;
this.$refs.newsPageHeading.scrollIntoView({block: 'end'});
this.loadNewPageContent(event);
},
},
created() {
this.fetchNews();
this.$api
.call(ApiCms.getNewsRowCount())
.then(res => res.data)
.then(result => {
.then((res) => res.data)
.then((result) => {
this.maxPageCount = result;
});
},
template: /*html*/ `
<h2 class="fhc-primary-color">News</h2>
<hr/>
<pagination v-show="content?true:false" :page_size="page_size" @page="page=$event.page; loadNewPageContent($event)" :maxPageCount="maxPageCount">
</pagination>
<div class="container-fluid mt-4">
<div class="row">
<div class="col" v-html="content">
</div>
<div class="col-auto">
<div style="width:15rem">
<studiengang-information></studiengang-information>
},
template: /*html*/ `
<div :class="{'pb-3': isMobile}" class="overflow-x-hidden">
<h2 ref="newsPageHeading" class="fhc-primary-color">News</h2>
<hr/>
<pagination
v-show="content?true:false"
:page="page"
:page_size="page_size"
@pageUpdated="afterPageUpdated($event)"
:maxPageCount="maxPageCount"
></pagination>
<div class="container-fluid mt-4">
<div class="row">
<div class="col" v-html="content">
</div>
<div class="col-auto">
<div style="width:15rem">
<studiengang-information></studiengang-information>
</div>
</div>
</div>
</div>
<pagination
v-show="content?true:false"
:page="page"
:page_size="page_size"
@pageUpdated="afterPageUpdated($event)"
:maxPageCount="maxPageCount"
></pagination>
</div>
<pagination v-show="content?true:false" :page_size="page_size" @page="loadNewPageContent" :maxPageCount="maxPageCount">
</pagination>
`,
};
@@ -3,7 +3,8 @@ import FhcCalendar from "../../Calendar/LvPlan.js";
import ApiLvPlan from '../../../api/factory/lvPlan.js';
import ApiAuthinfo from '../../../api/factory/authinfo.js';
export const DEFAULT_MODE_LVPLAN = 'Week'
export const DEFAULT_MODE_LVPLAN_MOBILE = 'List';
export const DEFAULT_MODE_LVPLAN_DESKTOP = 'Week';
export default {
name: 'LvPlanLehrveranstaltung',
@@ -19,6 +20,7 @@ export default {
lv: null
};
},
inject: ["isMobile"],
computed:{
currentDay() {
if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date)))
@@ -26,8 +28,13 @@ export default {
return this.propsViewData?.focus_date;
},
currentMode() {
if (!this.propsViewData?.mode || !['day', 'week', 'month'].includes(this.propsViewData?.mode.toLowerCase()))
return DEFAULT_MODE_LVPLAN;
let validModes = ['day', 'month'];
validModes.push(this.isMobile ? 'list' : 'week');
const defaultMode = this.isMobile ? DEFAULT_MODE_LVPLAN_MOBILE : DEFAULT_MODE_LVPLAN_DESKTOP;
if (!this.propsViewData?.mode || !validModes.includes(this.propsViewData?.mode.toLowerCase()))
return defaultMode;
return this.propsViewData?.mode;
},
currentLv() {
+10 -6
View File
@@ -3,7 +3,8 @@ import FhcCalendar from "../../Calendar/LvPlan.js";
import ApiLvPlan from '../../../api/factory/lvPlan.js';
import ApiAuthinfo from '../../../api/factory/authinfo.js';
export const DEFAULT_MODE_LVPLAN = 'Week'
export const DEFAULT_MODE_LVPLAN_MOBILE = 'List';
export const DEFAULT_MODE_LVPLAN_DESKTOP = 'Week';
export default {
name: 'LvPlan',
@@ -11,7 +12,6 @@ export default {
FhcCalendar
},
props: {
viewData: Object, // NOTE(chris): this is inherited from router-view
propsViewData: Object
},
data() {
@@ -20,21 +20,24 @@ export default {
studiensemester_start: null,
studiensemester_ende: null,
uid: null,
lv: null
lv: null,
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
};
},
inject: ["isMobile"],
computed:{
currentDay() {
return this.propsViewData?.focus_date || luxon.DateTime.now().setZone(FHC_JS_DATA_STORAGE_OBJECT.timezone).toISODate();
return this.propsViewData?.focus_date || luxon.DateTime.now().setZone(this.timezone).toISODate();
},
currentMode() {
return this.propsViewData?.mode || DEFAULT_MODE_LVPLAN;
const defaultMode = this.isMobile ? DEFAULT_MODE_LVPLAN_MOBILE : DEFAULT_MODE_LVPLAN_DESKTOP;
return this.propsViewData?.mode || defaultMode;
},
downloadLinks() {
if (!this.studiensemester_start || !this.studiensemester_ende || !this.uid)
return false;
const opts = { zone: FHC_JS_DATA_STORAGE_OBJECT.timezone };
const opts = { zone: this.timezone };
const start = luxon.DateTime
.fromISO(this.studiensemester_start, opts)
.toUnixInteger();
@@ -114,6 +117,7 @@ export default {
<fhc-calendar
ref="calendar"
v-model:lv="lv"
:timezone="timezone"
:get-promise-func="getPromiseFunc"
:date="currentDay"
:mode="currentMode"
@@ -3,7 +3,8 @@ import FhcCalendar from "../../Calendar/LvPlan.js";
import ApiLvPlan from '../../../api/factory/lvPlan.js';
import ApiAuthinfo from '../../../api/factory/authinfo.js';
export const DEFAULT_MODE_LVPLAN = 'Week'
export const DEFAULT_MODE_LVPLAN_DESKTOP = "Week";
export const DEFAULT_MODE_LVPLAN_MOBILE = "List";
export default {
name: 'LvPlanPersonal',
@@ -11,7 +12,6 @@ export default {
FhcCalendar
},
props: {
viewData: Object, // NOTE(chris): this is inherited from router-view
propsViewData: Object
},
data() {
@@ -21,18 +21,30 @@ export default {
studiensemester_ende: null,
uid: null,
isMitarbeiter: false,
isStudent: false
isStudent: false,
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
};
},
inject: ["isMobile"],
computed:{
currentDay() {
if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date)))
return luxon.DateTime.now().setZone(FHC_JS_DATA_STORAGE_OBJECT.timezone).toISODate();
return luxon.DateTime.now().setZone(this.timezone).toISODate();
return this.propsViewData?.focus_date;
},
currentMode() {
if (!this.propsViewData?.mode || !['day', 'week', 'month'].includes(this.propsViewData?.mode.toLowerCase()))
return DEFAULT_MODE_LVPLAN;
let validModes = ["day", "month"];
validModes.push(this.isMobile ? "list" : "week");
const defaultMode = this.isMobile
? DEFAULT_MODE_LVPLAN_MOBILE
: DEFAULT_MODE_LVPLAN_DESKTOP;
if (
!this.propsViewData?.mode ||
!validModes.includes(this.propsViewData?.mode.toLowerCase())
)
return defaultMode;
return this.propsViewData?.mode;
},
downloadLinks() {
@@ -47,7 +59,7 @@ export default {
return;
}
const opts = { zone: FHC_JS_DATA_STORAGE_OBJECT.timezone };
const opts = { zone: this.timezone };
const start = luxon.DateTime
.fromISO(this.studiensemester_start, opts)
.toUnixInteger();
@@ -102,16 +114,18 @@ export default {
this.$api.call(ApiLvPlan.eventsPersonal(start.toISODate(), end.toISODate())),
this.$api.call(ApiLvPlan.getLvPlanReservierungen(start.toISODate(), end.toISODate()))
];
}
},
async fetchAuthInfo() {
const authInfoResponse = await this.$api.call(ApiAuthinfo.getAuthInfo());
const authInfo = authInfoResponse.data;
this.uid = authInfo.uid;
this.isMitarbeiter = authInfo.isMitarbeiter;
this.isStudent = authInfo.isStudent;
},
},
created() {
this.$api
.call(ApiAuthinfo.getAuthInfo())
.then(res => {
this.uid = res.data.uid;
this.isMitarbeiter = res.data.isMitarbeiter;
this.isStudent = res.data.isStudent;
});
async created() {
await this.fetchAuthInfo();
},
template: /*html*/`
<div class="cis-lvplan-personal d-flex flex-column h-100">
@@ -123,7 +137,9 @@ export default {
</h2>
<hr>
<fhc-calendar
v-if="timezone"
ref="calendar"
:timezone="timezone"
:get-promise-func="getPromiseFunc"
:date="currentDay"
:mode="currentMode"
@@ -0,0 +1,269 @@
import FormForm from "../../Form/Form.js";
import FormInput from "../../Form/Input.js";
import FhcCalendar from "../../Calendar/LvPlan.js";
import ApiLvPlan from "../.././../api/factory/lvPlan.js";
import ApiOtherLvPlan from "../.././../api/factory/otherLvPlan.js";
import ApiAuthinfo from "../../../api/factory/authinfo.js";
export const DEFAULT_MODE_LVPLAN_DESKTOP = "Week";
export const DEFAULT_MODE_LVPLAN_MOBILE = "List";
export default {
name: "OtherLvPlan",
components: {
FormForm,
FormInput,
FhcCalendar,
},
props: {
propsViewData: Object,
},
data() {
return {
localProps: {},
studiensemester_kurzbz: null,
studiensemester_start: null,
studiensemester_ende: null,
isOtherPersonMitarbeiter: false,
isOtherPersonStudent: false,
currentStgBezeichnung: null,
listVerband: [],
listGroup: [],
rangeIntervalFirst: null,
otherPersonData: {
fullName: "",
photo: "",
},
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
};
},
inject: ["isMobile"],
computed: {
currentDay() {
if (
!this.propsViewData?.focus_date ||
isNaN(new Date(this.propsViewData?.focus_date))
)
return luxon.DateTime.now().setZone(this.timezone).toISODate();
return this.propsViewData?.focus_date;
},
currentMode() {
let validModes = ["day", "month"];
validModes.push(this.isMobile ? "list" : "week");
const defaultMode = this.isMobile
? DEFAULT_MODE_LVPLAN_MOBILE
: DEFAULT_MODE_LVPLAN_DESKTOP;
if (
!this.propsViewData?.mode ||
!validModes.includes(this.propsViewData?.mode.toLowerCase())
)
return defaultMode;
return this.propsViewData?.mode;
},
downloadLinks() {
if (
!this.studiensemester_start ||
!this.studiensemester_ende ||
!this.propsViewData.otherUid
)
return false;
const type = this.isOtherPersonStudent
? "student"
: this.isOtherPersonMitarbeiter
? "lektor"
: null;
if (!type) return;
const opts = { zone: this.timezone };
const start = luxon.DateTime.fromISO(
this.studiensemester_start,
opts,
).toUnixInteger();
const ende = luxon.DateTime.fromISO(
this.studiensemester_ende,
opts,
).toUnixInteger();
const download_link =
FHC_JS_DATA_STORAGE_OBJECT.app_root +
"cis/private/lvplan/stpl_kalender.php" +
"?type=" +
type +
"&pers_uid=" +
this.propsViewData.otherUid +
"&begin=" +
start +
"&ende=" +
ende;
return [
{
title: "excel",
icon: "fa-solid fa-file-excel",
link: download_link + "&format=excel",
},
{
title: "csv",
icon: "fa-solid fa-file-csv",
link: download_link + "&format=csv",
},
{
title: "ical1",
icon: "fa-regular fa-calendar",
link: download_link + "&format=ical&version=1&target=ical",
},
{
title: "ical2",
icon: "fa-regular fa-calendar",
link: download_link + "&format=ical&version=2&target=ical",
},
];
},
get_image_base64_src: function () {
if (!this.otherPersonData.photo?.length) {
return "";
}
return "data:image/jpeg;base64," + this.otherPersonData.photo;
},
},
watch: {
"propsViewData.otherUid": {
handler() {
this.$router.go();
},
},
},
methods: {
handleChangeDate(day, newMode) {
return this.handleChangeMode(newMode, day);
},
handleChangeMode(newMode, day) {
const mode = newMode[0].toUpperCase() + newMode.slice(1);
const focus_date = day.toISODate();
this.$router.push({
name: "OtherLvPlan",
params: {
mode,
focus_date,
},
});
},
updateRange(rangeInterval) {
this.$api
.call(
ApiLvPlan.studiensemesterDateInterval(
rangeInterval.end.startOf("week").toISODate(),
),
)
.then((res) => {
this.studiensemester_kurzbz =
res.data.studiensemester_kurzbz;
this.studiensemester_start = res.data.start;
this.studiensemester_ende = res.data.ende;
});
},
getPromiseFunc(start, end) {
return [
this.$api.call(
ApiLvPlan.eventsPersonal(
start.toISODate(),
end.toISODate(),
this.propsViewData.otherUid,
),
),
this.$api.call(
ApiLvPlan.getLvPlanReservierungen(
start.toISODate(),
end.toISODate(),
this.propsViewData.otherUid,
),
),
];
},
async fetchViewData() {
const viewDataResponse = await this.$api.call(
ApiOtherLvPlan.getOtherLvPlanViewData(
this.propsViewData.otherUid,
),
);
const viewData = viewDataResponse.data;
this.isOtherPersonMitarbeiter =
!!viewData?.user_data?.is_mitarbeiter;
this.isOtherPersonStudent = !!viewData?.user_data?.is_student;
this.otherPersonData.fullName =
viewData?.user_data?.vorname +
" " +
viewData?.user_data?.nachname;
this.otherPersonData.photo = viewData?.user_data?.foto;
},
async redirectToMyLvPlanIfAuthUid() {
const authInfoResponse = await this.$api.call(
ApiAuthinfo.getAuthInfo(),
);
const authId = authInfoResponse.data.uid;
if (authId === this.propsViewData.otherUid) {
this.$router.push({ name: "MyLvPlan" });
}
},
},
async created() {
await this.redirectToMyLvPlanIfAuthUid();
await this.fetchViewData();
},
template: `
<div class="d-flex flex-column h-100">
<h2>
<div class="d-flex flex-row justify-content-between align-items-center">
<span>
{{ $p.t('lehre/stundenplan') + (studiensemester_kurzbz ? " " + studiensemester_kurzbz : "") }}
</span>
<div @click="this.$router.push({name: 'ProfilView', params: {uid: propsViewData.otherUid}})" type="button" class="d-flex flex-row align-items-center gap-3">
<span v-if="otherPersonData.fullName?.length">
{{ otherPersonData.fullName }}
</span>
<img v-if="otherPersonData.photo?.length" alt="profile picture" class=" img-thumbnail " style=" max-height:60px; " :src="get_image_base64_src"/>
</div>
</div>
</h2>
<hr>
<fhc-calendar
v-if="timezone"
ref="calendar"
:timezone="timezone"
:get-promise-func="getPromiseFunc"
:date="currentDay"
:mode="currentMode"
@update:date="handleChangeDate"
@update:mode="handleChangeMode"
@update:range="updateRange"
class="responsive-calendar"
>
<div
v-if="downloadLinks"
class="d-flex gap-1 justify-items-start"
>
<div v-for="{ title, icon, link } in downloadLinks">
<a
:href="link"
:aria-label="title"
class="py-1 btn btn-outline-secondary"
>
<div class="d-flex flex-column">
<i aria-hidden="true" :class="icon"></i>
<span style="font-size:.5rem">{{ title }}</span>
</div>
</a>
</div>
</div>
</fhc-calendar>
</div>
`,
};
+413
View File
@@ -0,0 +1,413 @@
import FormForm from "../../Form/Form.js";
import FormInput from "../../Form/Input.js";
import FhcCalendar from "../../Calendar/LvPlan.js";
import ApiLvPlan from '../.././../api/factory/lvPlan.js';
import ApiStgOrgLvPlan from '../.././../api/factory/stgOrgLvPlan.js';
import ApiAuthinfo from '../../../api/factory/authinfo.js';
export const DEFAULT_MODE_LVPLAN_DESKTOP = "Week";
export const DEFAULT_MODE_LVPLAN_MOBILE = "List";
export default {
name: "LvPlanStgOrg",
components: {
FormForm,
FormInput,
FhcCalendar,
},
props: {
propsViewData: Object
},
data() {
return {
localProps: {},
studiensemester_kurzbz: null,
studiensemester_start: null,
studiensemester_ende: null,
uid: null,
isMitarbeiter: false,
isStudent: false,
currentStgBezeichnung: null,
formData: {
stgkz: null,
sem: null,
verband: null,
gruppe: null,
},
listStg: [],
listSem: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
listVerband: [],
listGroup: [],
rangeIntervalFirst: null,
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
};
},
inject: ["isMobile"],
computed: {
maxSemester() {
const currentStg = this.listStg.find(
(item) => item.studiengang_kz === this.formData.stgkz,
);
return currentStg?.max_semester;
},
currentDay() {
if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date)))
return luxon.DateTime.now().setZone(this.timezone).toISODate();
return this.propsViewData?.focus_date;
},
currentMode() {
let validModes = ["day", "month"];
validModes.push(this.isMobile ? "list" : "week");
const defaultMode = this.isMobile
? DEFAULT_MODE_LVPLAN_MOBILE
: DEFAULT_MODE_LVPLAN_DESKTOP;
if (
!this.propsViewData?.mode ||
!validModes.includes(this.propsViewData?.mode.toLowerCase())
)
return defaultMode;
return this.propsViewData?.mode;
},
downloadLinks() {
if (
!this.studiensemester_start ||
!this.studiensemester_ende ||
!this.uid
)
return false;
let type = false;
type = this.isStudent ? "student" : type;
type = this.isMitarbeiter ? "lektor" : type;
if (false === type) {
return;
}
const opts = { zone: this.timezone };
const start = luxon.DateTime
.fromISO(this.studiensemester_start, opts)
.toUnixInteger();
const ende = luxon.DateTime
.fromISO(this.studiensemester_ende, opts)
.toUnixInteger();
const download_link =
FHC_JS_DATA_STORAGE_OBJECT.app_root +
"cis/private/lvplan/stpl_kalender.php" +
"?type=" +
type +
"&pers_uid=" +
this.uid +
"&begin=" +
start +
"&ende=" +
ende;
return [
{
title: "excel",
icon: "fa-solid fa-file-excel",
link: download_link + "&format=excel",
},
{
title: "csv",
icon: "fa-solid fa-file-csv",
link: download_link + "&format=csv",
},
{
title: "ical1",
icon: "fa-regular fa-calendar",
link: download_link + "&format=ical&version=1&target=ical",
},
{
title: "ical2",
icon: "fa-regular fa-calendar",
link: download_link + "&format=ical&version=2&target=ical",
},
];
},
},
methods: {
loadLvPlan() {
if (!this.formData.stgkz) {
this.$fhcAlert.alertError(this.$p.t("LvPlan", "chooseStg"));
return;
}
if (
!this.formData.sem &&
(this.formData.verband || this.formData.gruppe)
) {
this.$fhcAlert.alertError(
this.$p.t("LvPlan", "error_SemMissing"),
);
return;
}
if (!this.formData.verband && this.formData.gruppe) {
this.$fhcAlert.alertError(
this.$p.t("LvPlan", "error_VerbandMissing"),
);
return;
}
const params = {
mode: this.currentMode,
focus_date: this.currentDay,
stgkz: this.formData.stgkz,
sem: this.formData.sem,
verband: this.formData.verband,
gruppe: this.formData.gruppe,
};
//ensure logic: no value after a null value in route
if (params.sem == null) {
params.verband = null;
params.gruppe = null;
}
if (params.verband == null) {
params.gruppe = null;
}
//delete all null values to avoid null in router
Object.keys(params).forEach(
(key) => params[key] == null && delete params[key],
);
this.$router.push({
name: "StgOrgLvPlan",
params,
});
this.$refs['calendar']?.resetEventLoader();
},
loadListSem(){
if(!this.listSem)
this.listSem = [...Array(this.maxSemester).keys()].map(i => i + 1);
},
loadListVerband() {
this.$api
.call(ApiLvPlan.getLehrverband(this.formData.stgkz, this.formData.sem, this.formData.verband))
.then(result => {
const data = result.data;
const mappedData = data.map((item) => item.verband);
this.listVerband = [
...new Set(
mappedData.filter(
(v) =>
v !== null &&
v !== undefined &&
String(v).trim() !== "",
),
),
].sort();
})
.catch(this.$fhcAlert.handleSystemError);
},
loadListGroup() {
this.$api
.call(ApiLvPlan.getGruppe(this.formData.stgkz, this.formData.sem, this.formData.verband))
.then(result => {
const data = result.data;
const mappedData = data.map((item) => item.gruppe);
this.listGroup = [
...new Set(
mappedData.filter(
(v) =>
v !== null &&
v !== undefined &&
String(v).trim() !== "",
),
),
].sort();
})
.catch(this.$fhcAlert.handleSystemError);
},
handleChangeDate(day, newMode) {
return this.handleChangeMode(newMode, day);
},
handleChangeMode(newMode, day) {
const mode = newMode[0].toUpperCase() + newMode.slice(1);
const focus_date = day.toISODate();
this.$router.push({
name: "StgOrgLvPlan",
params: {
mode,
focus_date,
stgkz: this.formData.stgkz,
sem: this.formData.sem,
verband: this.formData.verband,
gruppe: this.formData.gruppe,
},
});
},
updateRange(rangeInterval) {
this.$api
.call(
ApiLvPlan.studiensemesterDateInterval(
rangeInterval.end.startOf("week").toISODate(),
),
)
.then((res) => {
this.studiensemester_kurzbz =
res.data.studiensemester_kurzbz;
this.studiensemester_start = res.data.start;
this.studiensemester_ende = res.data.ende;
});
},
getPromiseFunc(start, end) {
return [
this.$api.call(
ApiLvPlan.eventsStgOrg(
start,
end,
this.formData.stgkz,
this.formData.sem,
this.formData.verband,
this.formData.gruppe,
),
),
];
},
async fetchAuthInfo() {
const authInfoResponse = await this.$api.call(ApiAuthinfo.getAuthInfo());
const authInfo = authInfoResponse.data;
this.uid = authInfo.uid;
this.isMitarbeiter = authInfo.isMitarbeiter;
this.isStudent = authInfo.isStudent;
},
async fetchViewData() {
const viewDataResponse = await this.$api.call(ApiStgOrgLvPlan.getStgOrgLvPlanViewData());
const viewData = viewDataResponse.data;
this.listStg = viewData.studiengaenge;
},
},
async created(){
await this.fetchAuthInfo();
await this.fetchViewData();
if (this.propsViewData) {
this.formData.stgkz = this.propsViewData.stgkz ? this.propsViewData.stgkz: null;
this.formData.sem = this.propsViewData.sem ? this.propsViewData.sem: null;
this.formData.verband = this.propsViewData.verband ? this.propsViewData.verband: null;
this.formData.gruppe = this.propsViewData.gruppe ? this.propsViewData.gruppe: null;
//ensure loading dropdown arrays for version propsView
if(!this.listVerband.length && this.formData.sem)
{
this.loadListVerband();
}
if(!this.listGroup.length && this.formData.verband)
{
this.loadListGroup();
}
}
},
template: `
<div class="cis-lvplan-stg-org d-flex flex-column h-100">
<div class="mt-3">
<form-form class="row row-cols-1 row-cols-md-2 row-cols-lg-4 row-cols-xl-5 g-3 mb-3">
<form-input
type="select"
v-model="formData.stgkz"
@change="loadListSem(formData.stgkz)"
>
<option :value="null" selected>{{ $p.t('LvPlan/chooseStg') }}</option>
<option
v-for="stg in listStg"
:key="stg.studiengang_kz"
:value="stg.studiengang_kz"
>
{{ stg.kurzbzlang }} ({{ stg.bezeichnung }})
</option>
</form-input>
<form-input
type="select"
v-model="formData.sem"
@change="loadListVerband()"
@click="loadListSem(formData.stgkz)"
>
<option :value="null" selected>Semester</option>
<option
v-for="sem in listSem"
:key="sem"
:value="sem"
>
{{ sem }}
</option>
</form-input>
<form-input
type="select"
v-model="formData.verband"
@change="loadListGroup()"
>
<option :value="null" selected>{{ $p.t('lehre/verband') }} </option>
<option
v-for="verband in listVerband"
:key="verband"
:value="verband"
>
{{ verband }}
</option>
</form-input>
<form-input
type="select"
v-model="formData.gruppe"
>
<option :value="null" selected>{{ $p.t('gruppenmanagement/gruppe') }}</option>
<option
v-for="group in listGroup"
:key="group"
:value="group"
>
{{ group }}
</option>
</form-input>
<button type="button" class="btn btn-secondary" @click="loadLvPlan">{{ $p.t('LvPlan/loadLvPlan') }}</button>
</form-form>
</div>
<template v-if="timezone">
<fhc-calendar
v-if="propsViewData && propsViewData.stgkz"
ref="calendar"
v-model:lv="formData"
:timezone="timezone"
:get-promise-func="getPromiseFunc"
:date="currentDay"
:mode="currentMode"
@update:date="handleChangeDate"
@update:mode="handleChangeMode"
@update:range="updateRange"
class="responsive-calendar"
>
<div
v-if="downloadLinks"
class="d-flex gap-1 justify-items-start"
>
<div v-for="{ title, icon, link } in downloadLinks">
<a
:href="link"
:aria-label="title"
class="py-1 btn btn-outline-secondary"
>
<div class="d-flex flex-column">
<i aria-hidden="true" :class="icon"></i>
<span style="font-size:.5rem">{{ title }}</span>
</div>
</a>
</div>
</div>
</fhc-calendar>
</template>
</div>
`,
};
@@ -0,0 +1,187 @@
import FhcCalendar from "../../Calendar/LvPlan.js";
import ApiLvPlan from '../.././../api/factory/lvPlan.js';
import ApiAuthinfo from '../../../api/factory/authinfo.js';
export const DEFAULT_MODE_LVPLAN = 'Week';
export default {
name: 'LvPlanStgOrg',
inject: {
cisRoot: {
from: 'cisRoot'
},
},
components: {
FhcCalendar,
},
props: {
viewData: Object,
propsViewData: Object
},
data() {
return {
studiensemester_kurzbz: null,
studiensemester_start: null,
studiensemester_ende: null,
uid: null,
isMitarbeiter: false,
isStudent: false,
listStg: [],
currentStgBezeichnung: null
};
},
computed:{
currentDay() {
if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date)))
return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate();
return this.propsViewData?.focus_date;
},
currentMode() {
if (!this.propsViewData?.mode || !['day', 'week', 'month'].includes(this.propsViewData?.mode.toLowerCase()))
return DEFAULT_MODE_LVPLAN;
return this.propsViewData?.mode;
},
downloadLinks() {
if (!this.studiensemester_start || !this.studiensemester_ende || !this.uid)
return false;
let type = false;
type = this.isStudent ? 'student' : type;
type = this.isMitarbeiter ? 'lektor' : type;
if (false === type)
{
return;
}
const opts = { zone: this.viewData.timezone };
const start = luxon.DateTime
.fromISO(this.studiensemester_start, opts)
.toUnixInteger();
const ende = luxon.DateTime
.fromISO(this.studiensemester_ende, opts)
.toUnixInteger();
const download_link = FHC_JS_DATA_STORAGE_OBJECT.app_root
+ 'cis/private/lvplan/stpl_kalender.php'
+ '?type=' + type
+ '&pers_uid=' + this.uid
+ '&begin=' + start
+ '&ende=' + ende;
return [
{ title: "excel", icon: 'fa-solid fa-file-excel', link: download_link + '&format=excel' },
{ title: "csv", icon: 'fa-solid fa-file-csv', link: download_link + '&format=csv' },
{ title: "ical1", icon: 'fa-regular fa-calendar', link: download_link + '&format=ical&version=1&target=ical' },
{ title: "ical2", icon: 'fa-regular fa-calendar', link: download_link + '&format=ical&version=2&target=ical' }
];
}
},
methods: {
handleChangeDate(day, newMode) {
return this.handleChangeMode(newMode, day);
},
handleChangeMode(newMode, day) {
const mode = newMode[0].toUpperCase() + newMode.slice(1)
const focus_date = day.toISODate();
this.$router.push({
name: "StgOrgLvPlan",
params: {
mode,
focus_date,
stgkz: this.propsViewData.stgkz,
sem: this.propsViewData.sem,
verband: this.propsViewData.verband,
gruppe: this.propsViewData.gruppe,
}
});
},
updateRange(rangeInterval) {
this.$api
.call(ApiLvPlan.studiensemesterDateInterval(
rangeInterval.end.startOf('week').toISODate()
))
.then(res => {
this.studiensemester_kurzbz = res.data.studiensemester_kurzbz;
this.studiensemester_start = res.data.start;
this.studiensemester_ende = res.data.ende;
});
},
getPromiseFunc(start, end) {
return [
this.$api.call(ApiLvPlan.eventsStgOrg(start.toISODate(), end.toISODate(), this.propsViewData.stgkz, this.propsViewData.sem, this.propsViewData.verband, this.propsViewData.gruppe)),
//local for test
/* this.$api.call(ApiLvPlan.eventsStgOrg(start.toISODate(), end.toISODate(), this.stgkz, this.sem, this.verband, this.gruppe))*/
];
},
backToDropdown(){
this.$router.push({
name: "OverviewLvPlan",
});
}
},
created(){
this.$api
.call(ApiAuthinfo.getAuthInfo())
.then(res => {
this.uid = res.data.uid;
this.isMitarbeiter = res.data.isMitarbeiter;
this.isStudent = res.data.isStudent;
});
if(this.propsViewData.stgkz) {
this.$api
.call(ApiLvPlan.getStudiengaenge())
.then(result => {
const currentStg = result.data.find(
item => item.studiengang_kz == this.propsViewData.stgkz
);
this.currentStgBezeichnung = currentStg.kurzbzlang + " - " + currentStg.bezeichnung;
})
.catch(this.$fhcAlert.handleSystemError);
}
},
template: `
<div class="cis-lvplan-stg-org d-flex flex-column h-100">
<h2>{{ $p.t('LvPlan/headerLvPlanLvVerband') }}</h2>
<p v-if="propsViewData.stgkz"><span><strong>{{currentStgBezeichnung}}</strong></span>
<span v-if="propsViewData.sem" style="margin-left:10px;"> Semester: {{propsViewData.sem}} </span>
<span v-if="propsViewData.verband" style="margin-left:10px;"> Verband: {{propsViewData.verband}} </span>
<span v-if="propsViewData.gruppe" style="margin-left:10px;"> Gruppe: {{propsViewData.gruppe}} </span>
<button class="py-1 btn btn-outline-secondary" :title="this.$p.t('LvPlan', 'backToDropdown')" style="margin-left:10px;"> <i class="fa fa-arrow-left fa-xs" @click="backToDropdown"></i></button>
</p>
<p v-else>{{ $p.t('LvPlan/noStgProvided') }}</p>
<fhc-calendar
ref="calendar"
:timezone="viewData.timezone"
:get-promise-func="getPromiseFunc"
:date="currentDay"
:mode="currentMode"
@update:date="handleChangeDate"
@update:mode="handleChangeMode"
@update:range="updateRange"
class="responsive-calendar"
>
<div
v-if="downloadLinks"
class="d-flex gap-1 justify-items-start"
>
<div v-for="{ title, icon, link } in downloadLinks">
<a
:href="link"
:aria-label="title"
class="py-1 btn btn-outline-secondary"
>
<div class="d-flex flex-column">
<i aria-hidden="true" :class="icon"></i>
<span style="font-size:.5rem">{{ title }}</span>
</div>
</a>
</div>
</div>
</fhc-calendar>
</div>
`,
};
+47 -20
View File
@@ -30,6 +30,7 @@ export default {
menuOpen:true,
};
},
inject: ["isMobile"],
provide(){
return{
setActiveEntry: this.setActiveEntry,
@@ -58,7 +59,7 @@ export default {
},
site_url(){
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
}
},
},
methods: {
fetchMenu() {
@@ -112,10 +113,26 @@ export default {
});
},
template: /*html*/`
<button id="nav-main-btn" class="navbar-toggler rounded-0" type="button" data-bs-toggle="offcanvas" data-bs-target="#nav-main" aria-controls="nav-main" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<fhc-searchbar ref="searchbar" id="nav-search" class="fhc-searchbar w-100 py-1 py-lg-2" :searchoptions="searchbaroptions" :searchfunction="searchfunction"></fhc-searchbar>
<div
id="header-options-collapsible"
class="collapse multi-collapse collapse-horizontal show"
>
<div class="d-flex flex-row align-items-center gap-2 h-100" style="width: 79px">
<button id="nav-main-btn" class="navbar-toggler rounded-0 px-2 border-0" type="button" data-bs-toggle="offcanvas" data-bs-target="#nav-main" aria-controls="nav-main" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<span v-if="isMobile" class="d-flex flex-row align-items-center">
<theme-switch></theme-switch>
</span>
</div>
</div>
<fhc-searchbar
:searchoptions="searchbaroptions"
:searchfunction="searchfunction"
ref="searchbar"
id="nav-search"
class="fhc-searchbar w-100 py-1 py-lg-2"
></fhc-searchbar>
<div id="nav-logo" class="d-none d-lg-block">
<div class="d-flex h-100 justify-content-between">
<a :href="rootUrl">
@@ -124,22 +141,32 @@ export default {
<theme-switch></theme-switch>
</div>
</div>
<div id="nav-user">
<button id="nav-user-btn" class="btn btn-link rounded-0" type="button" data-bs-toggle="collapse" data-bs-target="#nav-user-menu" aria-expanded="false" aria-controls="nav-user-menu">
<img :src="avatarUrl" :alt="$p.t('profilUpdate/profilBild')" class="bg-dark avatar rounded-circle border border-dark"/>
</button>
<ul ref="navUserDropdown"
@[\`shown.bs.collapse\`]="handleShowNavUser"
@[\`hide.bs.collapse\`]="handleHideNavUser"
id="nav-user-menu" class="top-100 end-0 collapse list-unstyled" aria-labelledby="nav-user-btn">
<li><a class="fhc-dark-bg btn rounded-0 d-block" :href="site_url + '/Cis/Profil'" id="menu-profil">Profil</a></li>
<li >
<cis-sprachen @languageChanged="fetchMenu"></cis-sprachen>
</li>
<li><hr class="dropdown-divider m-0 "></li>
<li ><a class="fhc-dark-bg btn rounded-0 d-block" :href="logoutUrl">Logout</a></li>
</ul>
<div
id="header-usermenu-collapsible"
class="collapse multi-collapse collapse-horizontal show"
>
<div
:style="!isMobile ? '' : 'width: 51px'"
id="nav-user"
>
<button id="nav-user-btn" class="btn btn-link rounded-0" type="button" data-bs-toggle="collapse" data-bs-target="#nav-user-menu" aria-expanded="false" aria-controls="nav-user-menu">
<img :src="avatarUrl" :alt="$p.t('profilUpdate/profilBild')" class="bg-dark avatar rounded-circle border border-dark"/>
</button>
<ul ref="navUserDropdown"
@[\`shown.bs.collapse\`]="handleShowNavUser"
@[\`hide.bs.collapse\`]="handleHideNavUser"
id="nav-user-menu" class="top-100 end-0 collapse list-unstyled" aria-labelledby="nav-user-btn">
<li><a class="fhc-dark-bg btn rounded-0 d-block" :href="site_url + '/Cis/Profil'" id="menu-profil">Profil</a></li>
<li >
<cis-sprachen @languageChanged="fetchMenu"></cis-sprachen>
</li>
<li><hr class="dropdown-divider m-0 "></li>
<li ><a class="fhc-dark-bg btn rounded-0 d-block" :href="logoutUrl">Logout</a></li>
</ul>
</div>
</div>
<nav id="nav-main" class="offcanvas offcanvas-start" tabindex="-1" aria-labelledby="nav-main-btn" data-bs-backdrop="false">
<div id="nav-main-sticky">
<div id="nav-main-toggle" class="position-static d-none d-lg-block ">
+5 -38
View File
@@ -68,41 +68,8 @@ export default {
return false;
}
},
link() {
if (this.entry.template_kurzbz == 'redirect') {
if (!this.entry.content)
return '';
let xmlDoc = (new DOMParser()).parseFromString(this.entry.content,"text/xml");
let url = xmlDoc.getElementsByTagName('url')[0];
if (!url)
return '';
// TODO(chris): replace get params
url = url.childNodes[0].nodeValue + "";
if (url.includes("../cms/news.php")) {
let news_regex = new RegExp("^\.\./cms/news\.php");
url = url.replace(news_regex, FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/CisVue/Cms/news');
}
else if (url.includes("../cms/content.php?")) {
let content_regex = new RegExp("^\.\./cms/content.php\\?content_id=([0-9]+)");
let content_regex_result = content_regex.exec(url);
// content_regex_result[1] will be the first matched group
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/CisVue/Cms/content/' + content_regex_result[1];
}
else if(url.includes("../index.ci.php")){
let index_regex = new RegExp("^\.\./index\.ci\.php");
url = url.replace(index_regex, FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router);
}
else if (url.includes("../")) {
let relative_regex = new RegExp("^\.\./");
url = url.replace(relative_regex, FHC_JS_DATA_STORAGE_OBJECT.app_root);
}
return url;
}
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/CisVue/Cms/content/' + this.entry.content_id;
},
hasFullLink() {
return this.link.startsWith(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router)
return this.entry.url.startsWith(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router)
},
target() {
if (this.entry.template_kurzbz == 'redirect') {
@@ -145,7 +112,7 @@ export default {
this.addUrlCount(count);
},
checkActiveUrl(url){
this.getUrlMatchPoints(url,this.link);
this.getUrlMatchPoints(url,this.entry.url);
let url_hash_spaceSymbol_regex = new RegExp("%20","gi");
let url_hash_sharpSymbol_regex = new RegExp("^#");
@@ -155,7 +122,7 @@ export default {
// if the url hash contains the titel of the menu
// or if the url equals the link of a menu
// then set the menu active
if (url_hash == this.entry.titel || url.href == this.link) {
if (url_hash == this.entry.titel || url.href == this.entry.url) {
this.setActiveEntry(this.entry.content_id);
}
},
@@ -204,7 +171,7 @@ export default {
<template v-if="hasChilds">
<div class="btn-group w-100">
<a :target="target"
:href="(entry.menu_open && hasFullLink)?link:null"
:href="(entry.menu_open && hasFullLink) ? entry.url : null"
@click="toggleCollapse"
:class="{
'btn btn-default rounded-0 text-start': true,
@@ -227,7 +194,7 @@ export default {
</ul>
</template>
<a v-else
:href="link"
:href="entry.url"
:target="target"
:class="{
'btn btn-default rounded-0 w-100 text-start': true,
@@ -100,7 +100,7 @@ export default {
</template>
<template v-else>
<span v-if="event?.lehrfach_bez ">{{event?.lehrfach_bez + (event?.stg_kurzbzlang?' / ' + event?.stg_kurzbzlang:'')}}</span>
<span v-else>Lehrveranstaltungs Übersicht</span>
<span v-else>{{$p.t('lehre','lvUebersicht')}}</span>
</template>
</template>
@@ -2,7 +2,8 @@ import FhcCalendar from "../../Calendar/LvPlan.js";
import ApiLvPlan from '../../../api/factory/lvPlan.js';
export const DEFAULT_MODE_RAUMINFO = 'Week'
export const DEFAULT_MODE_RAUMINFO_MOBILE = 'List';
export const DEFAULT_MODE_RAUMINFO_DESKTOP = 'Week';
export default {
name: "RoomInformation",
@@ -13,12 +14,14 @@ export default {
viewData: Object, // NOTE(chris): this is inherited from router-view
propsViewData: Object
},
inject: ["isMobile"],
computed: {
currentDay() {
return this.propsViewData?.focus_date || luxon.DateTime.now().setZone(FHC_JS_DATA_STORAGE_OBJECT.timezone).toISODate();
},
currentMode() {
return this.propsViewData?.mode || DEFAULT_MODE_RAUMINFO;
const defaultMode = this.isMobile ? DEFAULT_MODE_RAUMINFO_MOBILE : DEFAULT_MODE_RAUMINFO_DESKTOP;
return this.propsViewData?.mode || defaultMode;
}
},
methods:{
@@ -1,9 +1,11 @@
import MylvSemesterStudiengangLv from "./Studiengang/Lv.js";
import MylvSemesterStudiengangAverageGrade from "./Studiengang/AverageGrade.js";
import Phrasen from "../../../../mixins/Phrasen.js";
export default {
components: {
MylvSemesterStudiengangLv
MylvSemesterStudiengangLv,
MylvSemesterStudiengangAverageGrade
},
mixins: [
Phrasen
@@ -29,9 +31,10 @@ export default {
methods: {
note(lv) {
return lv.benotung ? lv.znote || lv.lvnote || null : null;
}
},
},
template: `<div class="card mb-3">
<div class="card-body">
<h4 class="card-title mb-3">{{$p.user_language.value === 'English' ? sg_bezeichnung_eng : bezeichnung}} - {{kuerzel}}
<small>{{semester}}.{{$p.t('lehre/semester')}}</small>
@@ -41,6 +44,7 @@ export default {
<mylv-semester-studiengang-lv v-bind="lv" class="text-center h-100"></mylv-semester-studiengang-lv>
</div>
</div>
<mylv-semester-studiengang-average-grade :lvs="lvs" />
</div>
</div>`
};
@@ -0,0 +1,94 @@
import Phrasen from "../../../../../mixins/Phrasen.js";
export default {
mixins: [
Phrasen
],
props: {
lvs: Array,
},
data: ( ) =>{
return {
gradeAverage: null,
gradeWeightedAverage: null,
existingGrades: false
}
},
methods: {
calculateAverages(){
let sum = 0;
let count = 0;
let sumWeighted = 0;
let sumEcts = 0;
this.lvs.forEach((lv) => {
if ((lv.znote >= 1 && lv.znote <= 5) && lv.znote!= null) {
this.existingGrades = true;
sum+= lv.znote;
count++;
sumWeighted += lv.znote * Number(lv.ects);
sumEcts += Number(lv.ects);
}
});
this.gradeAverage = (sum/count).toFixed(2);
this.gradeWeightedAverage = (sumWeighted/sumEcts).toFixed(2);
}
},
watch: {
lvs: {
handler() {
this.calculateAverages();
},
deep: true,
immediate: true
}
},
mounted(){
this.calculateAverages();
},
template: /*html*/`
<div class="card mylv-semester-studiengang-grades">
<div class="card-header text-center">
<h6>{{$p.t('lehre/notenstatistik')}}</h6>
</div>
<div v-if="existingGrades">
<table class="card-body table w-auto mx-auto">
<tbody>
<tr>
<td class="text-end">
{{$p.t('lehre/headerAverage')}}
</td>
<td class="text-start">
{{ gradeAverage }}
</td>
</tr>
<tr>
<td class="text-end">
{{$p.t('lehre/headerWeightedAverage')}}
</td>
<td class="text-start">
{{ gradeWeightedAverage }}
</td>
</tr>
</tbody>
</table>
</div>
<div v-else class="card-body text-center">
<p>{{$p.t('lehre/info_noGradesYet')}}</p>
</div>
<div v-if="existingGrades" class="card-footer d-flex align-items-start text-muted small">
<i class="fa fa-circle-info me-2 mt-1"></i>
<div>
<strong>{{$p.t('ui', 'hinweis')}}</strong><br>
* {{$p.t('lehre/noticeAverage')}}
<br>
** {{$p.t('lehre/noticeWeightedAverage')}}
</div>
</div>
</div>
`
}
@@ -73,11 +73,11 @@ export default {
},
grade() {
const languageIndex = this.$p.user_language.value === 'English' ? 1 : 0
// no more showing of grade LV, if grade Zeugnis is not existing yet
if(this.benotung && this.znotebez?.length) {
return this.znotebez[languageIndex]
} else if(this.benotung && this.lvnotebez?.length) {
return this.lvnotebez[languageIndex]
} else return null
}
else return null
},
LvHasPruefungenInformation(){
return this.pruefungenData && this.pruefungenData.length > 0;
@@ -9,6 +9,7 @@ import QuickLinks from "./ProfilComponents/QuickLinks.js";
import ProfilEmails from "./ProfilComponents/ProfilEmails.js";
import RoleInformation from "./ProfilComponents/RoleInformation.js";
import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
import CalendarSync from "./ProfilComponents/CalendarSync.js";
import ApiProfilUpdate from '../../../api/factory/profilUpdate.js';
import { dateFilter } from '../../../tabulator/filters/Dates.js';
@@ -26,6 +27,7 @@ export default {
ProfilEmails,
RoleInformation,
ProfilInformation,
CalendarSync,
},
inject: ["sortProfilUpdates", "collapseFunction", "language","isEditable"],
@@ -103,7 +105,6 @@ export default {
},
],
},
betriebsmittel_table_options: {
persistenceID: "filterTableMaProfilBetriebsmittel",
persistence: {
@@ -160,6 +161,7 @@ export default {
props: {
data: Object,
editData: Object,
calendarSyncUrls: Array,
},
methods: {
@@ -296,6 +298,11 @@ export default {
}
};
},
quickLinks() {
let quickLinks = [];
//
return quickLinks;
},
},
created() {
@@ -313,7 +320,6 @@ export default {
});
//? sorts the profil Updates: pending -> accepted -> rejected
this.data.profilUpdates?.sort(this.sortProfilUpdates);
},
watch: {
'data.funktionen'(newVal) {
@@ -331,12 +337,6 @@ export default {
<edit-profil v-if="showModal" ref="editModal" :isMitarbeiter="true" @hideBsModal="hideEditProfilModal" :value="JSON.parse(JSON.stringify(filteredEditData))" :titel="$p.t('profil','profilBearbeiten')"></edit-profil>
<div class="row">
<div class="d-md-none col-12 ">
<!--TODO: uncomment when implemented
<div class="row mb-3">
<div class="col">
<quick-links :title="$p.t('profil','quickLinks')" :mobile="true"></quick-links>
</div>
</div>-->
<!-- Bearbeiten Button -->
<div v-if="isEditable" class="row mb-3 ">
<div class="col">
@@ -465,17 +465,11 @@ export default {
</div>
<!-- START OF SIDE PANEL -->
<div class="col-md-4 col-xxl-3 col-sm-12 text-break" >
<!--TODO: uncomment when implemented
<div class="row d-none d-md-block mb-3">
<div class="col">
<quick-links :title="$p.t('profil','quickLinks')"></quick-links>
</div>
</div>-->
<div v-if="quickLinks.length" class="row mb-4">
<div class="col">
<quick-links :title="$p.t('profil/quickLinks')" :links="quickLinks" />
</div>
</div>
<!-- Bearbeiten Button -->
<div class="row d-none d-md-block ">
<div class="col mb-3">
@@ -501,12 +495,17 @@ export default {
<ausweis-status :data="data.zutrittsdatum"></ausweis-status>
</div>
</div>
<div class="row">
<div class="row mb-3">
<div class="col">
<!-- MAILVERTEILER -->
<mailverteiler :data="data?.mailverteiler" :title="$p.t('profil','mailverteiler')"></mailverteiler>
</div>
</div>
<div class="row">
<div class="col">
<calendar-sync :uid="$props.data.username" :calendarSyncUrls="$props.calendarSyncUrls"></calendar-sync>
</div>
</div>
</div>
</div>
</div>
@@ -1,30 +1,30 @@
import {CoreFilterCmpt} from "../../../components/filter/Filter.js";
import { CoreFilterCmpt } from "../../../components/filter/Filter.js";
import Mailverteiler from "./ProfilComponents/Mailverteiler.js";
import QuickLinks from "./ProfilComponents/QuickLinks.js";
import RoleInformation from "./ProfilComponents/RoleInformation.js";
import ProfilEmails from "./ProfilComponents/ProfilEmails.js";
import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
import QuickLinks from "./ProfilComponents/QuickLinks.js";
import { dateFilter } from '../../../tabulator/filters/Dates.js';
import { dateFilter } from "../../../tabulator/filters/Dates.js";
export default {
components: {
CoreFilterCmpt,
Mailverteiler,
QuickLinks,
RoleInformation,
ProfilEmails,
ProfilInformation,
QuickLinks,
},
inject: ["collapseFunction", "language"],
data() {
return {
collapseIconFunktionen: true,
preloadedPhrasen:{},
preloadedPhrasen: {},
funktionen_table_options: {
persistenceID: "filterTableMaViewProfilFunktionen",
persistence: {
columns: false
columns: false,
},
minHeight: 300,
layout: "fitColumns",
@@ -35,58 +35,65 @@ export default {
//? option when wanting to hide the collapsed list
{
title:
"<i id='collapseIconFunktionen' role='button' class='fa-solid fa-angle-down '></i>",
title: "<i id='collapseIconFunktionen' role='button' class='fa-solid fa-angle-down '></i>",
field: "collapse",
headerSort: false,
headerFilter: false,
formatter: "responsiveCollapse",
maxWidth: 40,
headerClick: this.collapseFunction,
visible: true
visible: true,
},
{
title: Vue.computed(() => this.$p.t('ui/bezeichnung')),
title: Vue.computed(() => this.$p.t("ui/bezeichnung")),
field: "Bezeichnung",
headerFilter: true,
minWidth: 200,
visible: true
visible: true,
},
{
title: Vue.computed(() => this.$p.t('lehre/organisationseinheit')),
title: Vue.computed(() =>
this.$p.t("lehre/organisationseinheit"),
),
field: "Organisationseinheit",
headerFilter: true,
minWidth: 200,
visible: true
visible: true,
},
{
title: Vue.computed(() => this.$p.t('global/gueltigVon')),
title: Vue.computed(() =>
this.$p.t("global/gueltigVon"),
),
field: "Gültig_von",
headerFilterFunc: 'dates',
headerFilterFunc: "dates",
headerFilter: dateFilter,
resizable: true,
minWidth: 200,
visible: true,
formatter:"datetime",
formatterParams: this.datetimeFormatterParams()
formatter: "datetime",
formatterParams: this.datetimeFormatterParams(),
},
{
title: Vue.computed(() => this.$p.t('global/gueltigBis')),
title: Vue.computed(() =>
this.$p.t("global/gueltigBis"),
),
field: "Gültig_bis",
headerFilterFunc: 'dates',
headerFilterFunc: "dates",
headerFilter: dateFilter,
resizable: true,
minWidth: 200,
visible: true,
formatter:"datetime",
formatterParams: this.datetimeFormatterParams()
formatter: "datetime",
formatterParams: this.datetimeFormatterParams(),
},
{
title: Vue.computed(() => this.$p.t('profil/wochenstunden')),
title: Vue.computed(() =>
this.$p.t("profil/wochenstunden"),
),
field: "Wochenstunden",
headerFilter: true,
minWidth: 200,
visible: true
visible: true,
},
],
},
@@ -94,47 +101,56 @@ export default {
},
//? this is the prop passed to the dynamic component with the custom data of the view
props: ["data"],
props: ["data", "permissions"],
methods: {
funktionenTableBuilt: function () {
this.$refs.funktionenTable.tabulator.setData(this.data.funktionen);
},
datetimeFormatterParams: function() {
datetimeFormatterParams: function () {
const params = {
inputFormat:"yyyy-MM-dd",
outputFormat:"dd.MM.yyyy",
invalidPlaceholder:"(invalid date)",
timezone:FHC_JS_DATA_STORAGE_OBJECT.timezone
inputFormat: "yyyy-MM-dd",
outputFormat: "dd.MM.yyyy",
invalidPlaceholder: "(invalid date)",
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
};
return params;
}
},
},
watch: {
'data.funktionen'(newVal) {
if(this.$refs.funktionenTable) this.$refs.funktionenTable.tabulator.setData(newVal);
"data.funktionen"(newVal) {
if (this.$refs.funktionenTable)
this.$refs.funktionenTable.tabulator.setData(newVal);
},
"language.value"(newVal) {
// reevaluates computed phrasen
if (this.$refs.funktionenTable)
this.$refs.funktionenTable.tabulator.setColumns(
this.funktionen_table_options.columns,
);
},
'language.value'(newVal) { // reevaluates computed phrasen
if(this.$refs.funktionenTable) this.$refs.funktionenTable.tabulator.setColumns(this.funktionen_table_options.columns)
}
},
computed: {
getTelefonValue() {
if(this.data.standort_telefon?.kontakt) {
return this.data.standort_telefon.kontakt + " " + this.data.telefonklappe
} else if(this.data.standort_telefon) {
return this.data.standort_telefon + " " + this.data.telefonklappe
if (this.data.standort_telefon?.kontakt) {
return (
this.data.standort_telefon.kontakt +
" " +
this.data.telefonklappe
);
} else if (this.data.standort_telefon) {
return (
this.data.standort_telefon + " " + this.data.telefonklappe
);
} else {
return this.data.telefonklappe
return this.data.telefonklappe;
}
},
fotoStatus() {
return this.data?.fotoStatus ?? null;
},
personEmails() {
return this.data?.emails ? this.data.emails : [];
},
profilInformation() {
if (!this.data) {
return {};
@@ -151,42 +167,67 @@ export default {
foto: this.data.foto,
};
},
roleInformation() {
if (!this.data) {
return {};
}
return {
geburtsdatum: {
label: `${this.$p.t('profil','Geburtsdatum')}`,
value: this.data.gebdatum
label: `${this.$p.t("profil", "Geburtsdatum")}`,
value: this.data.gebdatum,
},
geburtsort: {
label: `${this.$p.t('profil','Geburtsort')}`,
value: this.data.gebort
label: `${this.$p.t("profil", "Geburtsort")}`,
value: this.data.gebort,
},
personenkennzeichen: {
label: `${this.$p.t('profil','Kurzzeichen')}`,
value: this.data.kurzbz
label: `${this.$p.t("profil", "Kurzzeichen")}`,
value: this.data.kurzbz,
},
telefon: {
label: `${this.$p.t('profil','Telefon')}`,
value: this.getTelefonValue
label: `${this.$p.t("profil", "Telefon")}`,
value: this.getTelefonValue,
},
office: {
label: `${this.$p.t('profil','Büro')}`,
value: this.data.ort_kurzbz
}
label: `${this.$p.t("profil", "Büro")}`,
value: this.data.ort_kurzbz,
},
};
},
quickLinks() {
let quickLinks = [];
if (
this.$props.permissions &&
this.$props.permissions["basis/other_lv_plan"]
) {
quickLinks.push({
icon: "fa-calendar-days",
phrase: "lehre/stundenplan",
action: () => {
this.$router.push({
name: "OtherLvPlan",
params: { otherUid: this.$props.data.username },
});
},
});
}
return quickLinks;
},
},
created(){
created() {
this.$p.loadCategory(["ui", "lehre", "global", "profil"]).then(() => {
this.preloadedPhrasen.bezeichnungPhrase = this.$p.t('ui/bezeichnung');
this.preloadedPhrasen.organisationseinheitPhrase = this.$p.t('lehre/organisationseinheit');
this.preloadedPhrasen.gueltigVonPhrase = this.$p.t('global/gueltigVon');
this.preloadedPhrasen.gueltigBisPhrase = this.$p.t('global/gueltigBis');
this.preloadedPhrasen.wochenstundenPhrase = this.$p.t('profil/wochenstunden');
this.preloadedPhrasen.bezeichnungPhrase =
this.$p.t("ui/bezeichnung");
this.preloadedPhrasen.organisationseinheitPhrase = this.$p.t(
"lehre/organisationseinheit",
);
this.preloadedPhrasen.gueltigVonPhrase =
this.$p.t("global/gueltigVon");
this.preloadedPhrasen.gueltigBisPhrase =
this.$p.t("global/gueltigBis");
this.preloadedPhrasen.wochenstundenPhrase = this.$p.t(
"profil/wochenstunden",
);
this.preloadedPhrasen.loaded = true;
});
},
@@ -242,7 +283,7 @@ export default {
</div>
<!-- START OF THE SECOND PROFIL INFORMATION ROW -->
<!-- ROW WITH PROFIL IMAGE AND INFORMATION END -->
</div >
</div>
<!-- SECOND ROW UNDER THE PROFIL IMAGE AND INFORMATION WITH THE TABLES -->
<div class="row">
<!-- FIRST TABLE -->
@@ -256,26 +297,22 @@ export default {
<!-- START OF SIDE PANEL -->
<div class="col-md-4 col-xxl-3 col-sm-12 text-break" >
<!-- VISIBLE UNTIL VIEWPORT MD -->
<!--TODO: uncomment when implemented
<div class="row d-none d-md-block mb-3">
<div class="col">
<quick-links :title="$p.t('profil','quickLinks')" ></quick-links>
</div>
</div>
-->
<div class="row">
<div class="col">
<!-- MAILVERTEILER -->
<mailverteiler :data="data?.mailverteiler" :title="$p.t('profil','mailverteiler')"></mailverteiler>
</div>
</div>
<!-- END OF SIDE PANEL -->
</div>
<div v-if="quickLinks.length" class="row mb-4">
<div class="col">
<quick-links :title="$p.t('profil/quickLinks')" :links="quickLinks" />
</div>
</div>
<div class="row">
<div class="col">
<!-- MAILVERTEILER -->
<mailverteiler :data="data?.mailverteiler" :title="$p.t('profil','mailverteiler')"></mailverteiler>
</div>
</div>
<!-- END OF SIDE PANEL -->
</div>
<!-- END OF CONTAINER ROW-->
</div>
<!-- END OF CONTAINER -->
</div>
<!-- END OF CONTAINER -->
</div>
`,
};
+84 -60
View File
@@ -4,8 +4,8 @@ import ViewStudentProfil from "./StudentViewProfil.js";
import ViewMitarbeiterProfil from "./MitarbeiterViewProfil.js";
import Loading from "../../Loader.js";
import ApiProfil from '../../../api/factory/profil.js';
import ApiProfilUpdate from '../../../api/factory/profilUpdate.js';
import ApiProfil from "../../../api/factory/profil.js";
import ApiProfilUpdate from "../../../api/factory/profilUpdate.js";
Vue.$collapseFormatter = function (data) {
//data - an array of objects containing the column title and value for each cell
@@ -35,7 +35,7 @@ Vue.$collapseFormatter = function (data) {
};
export const Profil = {
name: 'Profil',
name: "Profil",
components: {
StudentProfil,
MitarbeiterProfil,
@@ -46,11 +46,8 @@ export const Profil = {
props: {
uid: {
type: String,
required:false,
required: false,
},
viewData: {
type: Object,
}
},
data() {
return {
@@ -62,17 +59,19 @@ export const Profil = {
data: null,
// notfound is null by default, but contains an UID if no user exists with that UID
notFoundUID: null,
isEditable: this.viewData.editable ?? false,
isEditable: false,
authPermissions: null,
calendarSyncUrls: [],
};
},
provide() {
return {
isEditable: Vue.computed(()=>this.isEditable),
isEditable: Vue.computed(() => this.isEditable),
profilUpdateStates: Vue.computed(() =>
this.profilUpdateStates ? this.profilUpdateStates : false
this.profilUpdateStates ? this.profilUpdateStates : false,
),
profilUpdateTopic: Vue.computed(() =>
this.profilUpdateTopic ? this.profilUpdateTopic : false
this.profilUpdateTopic ? this.profilUpdateTopic : false,
),
setLoading: (newValue) => {
this.loading = newValue;
@@ -130,8 +129,12 @@ export const Profil = {
//? if they have the same status the insert date is used for ordering
if (ele1.status === ele2.status) {
result =
new Date(ele2.insertamum.split(".").reverse().join("-")) -
new Date(ele1.insertamum.split(".").reverse().join("-"));
new Date(
ele2.insertamum.split(".").reverse().join("-"),
) -
new Date(
ele1.insertamum.split(".").reverse().join("-"),
);
}
return result;
},
@@ -139,6 +142,8 @@ export const Profil = {
},
methods: {
async load() {
await this.fetchViewData();
// fetch profilUpdateStates to provide them to children components
await this.$api
.call(ApiProfilUpdate.getStatus())
@@ -157,20 +162,20 @@ export const Profil = {
.catch((error) => {
console.error(error);
});
this.$api
.call(ApiProfil.profilViewData(this.$route.params.uid??null))
.then((response) => response.data).then(data=>{
this.view = data?.profil_data.view;
this.data = data?.profil_data.data;
this.isEditable = data?.editable ?? false;
})
.catch((error) => {
console.error(error);
});
},
async fetchViewData() {
let viewDataResult = await this.$api.call(
ApiProfil.getProfilViewData(this.$route.params.uid ?? null),
);
const data = viewDataResult.data;
if (!data) return;
this.view = data.profil_data.view;
this.isEditable = data.profil_data.editable;
this.data = data.profil_data.data;
this.calendarSyncUrls = data.calendar_sync_urls ?? [];
this.authPermissions = data.permissions;
},
zustellAdressenCount() {
if (!this.data || !this.data.adressen) {
@@ -186,7 +191,7 @@ export const Profil = {
})
.map((adresse) => {
return adresse.requested_change.adresse_id;
})
}),
);
}
@@ -197,8 +202,9 @@ export const Profil = {
.every((adresse) =>
this.data.profilUpdates.some(
(update) =>
update.requested_change.adresse_id == adresse.adresse_id
)
update.requested_change.adresse_id ==
adresse.adresse_id,
),
)
) {
adressenArray = adressenArray.concat(
@@ -208,12 +214,11 @@ export const Profil = {
})
.map((adr) => {
return adr.adresse_id;
})
}),
);
}
return [...new Set(adressenArray)];
},
zustellKontakteCount() {
if (!this.data || !this.data.kontakte) {
@@ -226,14 +231,17 @@ export const Profil = {
kontakteArray = kontakteArray.concat(
this.data.profilUpdates
.filter((update) => {
return update.status === 'Pending' && update.requested_change.zustellung;
return (
update.status === "Pending" &&
update.requested_change.zustellung
);
})
.map((kontant) => {
return {
kontakt_id: kontant.requested_change.kontakt_id,
kontakttyp: kontant.requested_change.kontakttyp
};
})
kontakt_id: kontant.requested_change.kontakt_id,
kontakttyp: kontant.requested_change.kontakttyp,
};
}),
);
}
@@ -244,8 +252,10 @@ export const Profil = {
.every((kontakt) =>
this.data.profilUpdates.some(
(update) =>
update.status === 'Pending' && update.requested_change.kontakt_id == kontakt.kontakt_id
)
update.status === "Pending" &&
update.requested_change.kontakt_id ==
kontakt.kontakt_id,
),
)
) {
kontakteArray = kontakteArray.concat(
@@ -255,10 +265,10 @@ export const Profil = {
})
.map((kon) => {
return {
kontakt_id: kon.kontakt_id,
kontakttyp: kon.kontakttyp
};
})
kontakt_id: kon.kontakt_id,
kontakttyp: kon.kontakttyp,
};
}),
);
}
@@ -266,7 +276,6 @@ export const Profil = {
},
},
computed: {
filteredEditData() {
if (!this.data) {
return;
@@ -330,8 +339,12 @@ export const Profil = {
// excludes all contacts that are already used in pending profil update requests
return !this.data.profilUpdates?.some(
(update) =>
update.status === this.profilUpdateStates["Pending"] &&
update.requested_change?.kontakt_id === item.kontakt_id
update.status ===
this.profilUpdateStates[
"Pending"
] &&
update.requested_change?.kontakt_id ===
item.kontakt_id,
);
})
.map((kontakt) => {
@@ -347,12 +360,18 @@ export const Profil = {
topic: this.profilUpdateTopic?.["Private Adressen"],
data: this.data.adressen
?.filter((item) => {
return !this.data.profilUpdates?.some((update) => {
return (
update.status === this.profilUpdateStates["Pending"] &&
update.requested_change?.adresse_id == item.adresse_id
);
});
return !this.data.profilUpdates?.some(
(update) => {
return (
update.status ===
this.profilUpdateStates[
"Pending"
] &&
update.requested_change
?.adresse_id == item.adresse_id
);
},
);
})
.map((adresse) => {
return {
@@ -374,23 +393,28 @@ export const Profil = {
this.$refs.loadingModalRef.hide();
}
},
uid (newVal, oldVal) {
this.load()
}
uid(newVal, oldVal) {
this.load();
},
},
created() {
this.load()
this.load();
},
template: `
<div>
<div class="pb-4">
<div v-if="notFoundUID">
<h3>Es wurde keine Person mit der UID {{this.notFoundUID}} gefunden</h3>
</div>
<div v-else>
<loading ref="loadingModalRef" :timeout="0"></loading>
<component :is="view" :data="data" :editData="filteredEditData" ></component>
<component
:is="view"
:data="data"
:editData="filteredEditData"
:permissions="authPermissions"
:calendarSyncUrls="calendarSyncUrls"></component>
</div>
</div>`,
}
};
export default Profil
export default Profil;
@@ -0,0 +1,54 @@
export default {
name: "CalendarSync",
props: { uid: String, calendarSyncUrls: Array },
data() {
return {
syncInstructionsUrlWithoutParam:
FHC_JS_DATA_STORAGE_OBJECT.app_root +
"cms/content.php?content_id=",
};
},
methods: {
copyUrlToClipboard(url) {
navigator.clipboard.writeText(url);
this.$fhcAlert.alertSuccess(
this.$p.t("profil/calendar_sync_clipboard_copy_confirmation"),
);
},
},
template: `
<div class="card">
<div class="card-header">
{{ $p.t("profil/calendar_sync") }}
</div>
<div class="card-body">
<div class="d-flex flex-column gap-3">
<a
target="_blank"
:href="syncInstructionsUrlWithoutParam + $p.t('DMS-Link/lvplanSyncFAQ')"
class="fhc-link-color d-flex flex-row gap-2 align-items-center"
>
<span>
<i class="fa-solid fa-up-right-from-square ms-2"></i>
</span>
<span>
{{ $p.t("profil/calendar_sync_instructions") }}
</span>
</a>
<a
v-for="syncUrl in $props.calendarSyncUrls"
:key="syncUrl.identifier"
@click.prevent="copyUrlToClipboard(syncUrl.url)"
@contextmenu.prevent="copyUrlToClipboard(syncUrl.url)"
href="#"
class="fhc-link-color d-flex flex-row gap-2 align-items-center"
>
<span>
<i class="fa-regular fa-copy ms-2 text-decoration-none"></i>
</span>
{{ $p.t(syncUrl.labelPhrase) }}
</a>
</div>
</div>
</div>`,
};
@@ -1,53 +1,31 @@
export default {
//TODO: To be implemented
props: {
data: {
type: String,
},
title: {
type: String,
required: true,
},
mobile: {
type: Boolean,
default: false,
},
},
methods: {
hideCollapse: function () {
this.collapseOpen = false;
},
showCollapse: function () {
this.collapseOpen = true;
},
},
data() {
return {
collapseOpen: false,
};
},
template: /*html*/ `
name: "QuickLinks",
data() {
return {};
},
props: {
title: {
type: String,
required: true,
},
links: {
type: Array,
required: true,
},
},
template: `
<div class="card">
<template v-if="mobile">
<button class="btn btn-outline-primary" data-bs-toggle="collapse" data-bs-target="#quickLinks" :aria-expanded="collapseOpen" aria-controls="quickLinks" >
{{title}}
<i class="fa " :class="collapseOpen?'fa-chevron-up':'fa-chevron-down'"></i>
</button>
<div @[\`show.bs.collapse\`]="collapseOpen=true;" @[\`hide.bs.collapse\`]="collapseOpen=false;" class="mt-1 collapse" id="quickLinks">
<div class="list-group">
<a href="#" class="list-group-item list-group-item-action">{{$p.t('profil','zeitwuensche')}}</a>
<a href="#" class="list-group-item list-group-item-action">{{$p.t('profil','lehrveranstaltungen')}}</a>
<a href="#" class="list-group-item list-group-item-action ">{{$p.t('profil','zeitsperren')}}</a>
<div class="card-header">
{{title}}
</div>
<div class="card-body">
<div class="d-flex flex-row align-items-center gap-3 flex-wrap">
<div v-for="link in links" @click="link.action()" type="button" class="d-flex flex-row gap-2 px-3 py-2 border fhc-primary">
<div><i class="fa" :class="link.icon"></i></div>
{{ $p.t(link.phrase) }}
<div><i class="fa fa-arrow-up-right-from-square" style="color:var(--fhc-light) !important"></i></div>
</div>
</div>
</template>
<template v-else>
<div class="card-header">{{title}}</div>
<div class="card-body">
<a style="text-decoration:none" class="my-1 d-block" href="#">{{$p.t('profil','zeitwuensche')}}</a>
<a style="text-decoration:none" class="my-1 d-block" href="#">{{$p.t('profil','lehrveranstaltungen')}}</a>
<a style="text-decoration:none" class="my-1 d-block" href="#">{{$p.t('profil','zeitsperren')}}</a>
</div>
</template>
</div>
</div>`,
};
@@ -1,7 +1,6 @@
import {CoreFilterCmpt} from "../../../components/filter/Filter.js";
import Mailverteiler from "./ProfilComponents/Mailverteiler.js";
import AusweisStatus from "./ProfilComponents/FhAusweisStatus.js";
import QuickLinks from "./ProfilComponents/QuickLinks.js";
import Adresse from "./ProfilComponents/Adresse.js";
import Kontakt from "./ProfilComponents/Kontakt.js";
import ProfilEmails from "./ProfilComponents/ProfilEmails.js";
@@ -9,6 +8,8 @@ import RoleInformation from "./ProfilComponents/RoleInformation.js";
import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
import FetchProfilUpdates from "./ProfilComponents/FetchProfilUpdates.js";
import EditProfil from "./ProfilModal/EditProfil.js";
import QuickLinks from "./ProfilComponents/QuickLinks.js";
import CalendarSync from "./ProfilComponents/CalendarSync.js";
import ApiProfilUpdate from '../../../api/factory/profilUpdate.js';
import { dateFilter } from '../../../tabulator/filters/Dates.js';
@@ -18,7 +19,6 @@ export default {
CoreFilterCmpt,
Mailverteiler,
AusweisStatus,
QuickLinks,
Adresse,
Kontakt,
ProfilEmails,
@@ -26,6 +26,8 @@ export default {
ProfilInformation,
FetchProfilUpdates,
EditProfil,
QuickLinks,
CalendarSync,
},
inject: ["sortProfilUpdates", "collapseFunction", "language","isEditable"],
data() {
@@ -101,6 +103,7 @@ export default {
props: {
data: Object,
editData: Object,
calendarSyncUrls: Array,
},
provide() {
return {
@@ -240,6 +243,11 @@ export default {
}
};
},
quickLinks() {
let quickLinks = [];
//
return quickLinks;
},
},
created() {
// preload phrasen
@@ -265,15 +273,7 @@ export default {
:value="JSON.parse(JSON.stringify(filteredEditData))" :titel="$p.t('profil','profilBearbeiten')"></edit-profil>
<!-- ROW -->
<div class="row">
<!-- HIDDEN QUICK LINKS -->
<div class="d-md-none col-12 ">
<!--TODO: uncomment when implemented
<div class="row py-2">
<div class="col">
<quick-links :title="$p.t('profil','quickLinks')" :mobile="true"></quick-links>
</div>
</div>-->
<!-- Bearbeiten Button -->
<div v-if="isEditable" class="row ">
<div class="col mb-3">
@@ -403,12 +403,11 @@ export default {
</div>
<!-- START OF SIDE PANEL -->
<div class="col-md-4 col-xxl-3 col-sm-12 text-break" >
<!--TODO: uncomment when implemented
<div class="row d-none d-md-block mb-3">
<div v-if="quickLinks.length" class="row mb-4">
<div class="col">
<quick-links :title="$p.t('profil','quickLinks')"></quick-links>
<quick-links :title="$p.t('profil/quickLinks')" :links="quickLinks" />
</div>
</div>-->
</div>
<!-- Bearbeiten Button -->
<div class="row d-none d-md-block">
<div class="col mb-3">
@@ -434,13 +433,18 @@ export default {
</div>
</div>
<!-- START OF THE SECOND ROW IN THE SIDE PANEL -->
<div class="row">
<div class="row mb-3">
<div class="col">
<!-- HIER SIND DIE MAILVERTEILER -->
<mailverteiler :title="$p.t('profil','mailverteiler')" :data="data?.mailverteiler"></mailverteiler>
</div>
<!-- END OF THE SECOND ROW IN THE SIDE PANEL -->
</div>
<div class="row">
<div class="col">
<calendar-sync :uid="$props.data.username" :calendarSyncUrls="$props.calendarSyncUrls"></calendar-sync>
</div>
</div>
<!-- END OF SIDE PANEL -->
</div>
<!-- END OF CONTAINER ROW-->
@@ -1,30 +1,28 @@
import QuickLinks from "./ProfilComponents/QuickLinks.js";
import Mailverteiler from "./ProfilComponents/Mailverteiler.js";
import ProfilEmails from "./ProfilComponents/ProfilEmails.js";
import RoleInformation from "./ProfilComponents/RoleInformation.js";
import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
import QuickLinks from "./ProfilComponents/QuickLinks.js";
export default {
data() {
return {};
},
components: {
QuickLinks,
Mailverteiler,
ProfilEmails,
RoleInformation,
ProfilInformation,
QuickLinks,
},
props: ["data", "permissions"],
data() {
return {};
},
props: ["data"],
provide() {
return {
studiengang_kz: Vue.computed({ get: () => this.data.studiengang_kz }),
}
studiengang_kz: Vue.computed({
get: () => this.data.studiengang_kz,
}),
};
},
methods: {},
computed: {
fotoStatus() {
return this.data?.fotoStatus ?? null;
@@ -45,66 +43,67 @@ export default {
foto: this.data.foto,
};
},
personEmails() {
return this.data?.emails ? this.data.emails : [];
},
roleInformation() {
if (!this.data) {
return {};
}
return {
geburtsdatum: {
label: `${this.$p.t('profil','Geburtsdatum')}`,
value: this.data.gebdatum
label: `${this.$p.t("profil", "Geburtsdatum")}`,
value: this.data.gebdatum,
},
geburtsort: {
label: `${this.$p.t('profil','Geburtsort')}`,
value: this.data.gebort
label: `${this.$p.t("profil", "Geburtsort")}`,
value: this.data.gebort,
},
personenkennzeichen: {
label: `${this.$p.t('person','personenkennzeichen')}`,
value: this.data.personenkennzeichen
label: `${this.$p.t("person", "personenkennzeichen")}`,
value: this.data.personenkennzeichen,
},
studiengang: {
label: `${this.$p.t('lehre','studiengang')}`,
value: this.data.studiengang
label: `${this.$p.t("lehre", "studiengang")}`,
value: this.data.studiengang,
},
semester: {
label: `${this.$p.t('lehre','semester')}`,
value: this.data.semester
label: `${this.$p.t("lehre", "semester")}`,
value: this.data.semester,
},
verband: {
label: `${this.$p.t('lehre','lehrverband')}`,
value: this.data.verband
label: `${this.$p.t("lehre", "lehrverband")}`,
value: this.data.verband,
},
gruppe: {
label: `${this.$p.t('lehre','gruppe')}`,
value: this.data.gruppe.trim()
}
label: `${this.$p.t("lehre", "gruppe")}`,
value: this.data.gruppe.trim(),
},
};
},
quickLinks() {
let quickLinks = [];
if (this.$props.permissions && this.$props.permissions["basis/other_lv_plan"]) {
quickLinks.push({
icon: "fa-calendar-days",
phrase: "lehre/stundenplan",
action: () => {
this.$router.push({
name: "OtherLvPlan",
params: { otherUid: this.$props.data.username },
});
},
});
}
return quickLinks;
},
},
mounted() {
},
template: /*html*/ `
<div class="container-fluid text-break fhc-form" >
<!-- ROW -->
<div class="row">
<!-- HIDDEN QUICK LINKS -->
<!-- uncomment when implemented
<div class="d-md-none col-12 ">
<quick-links :title="$p.t('profil','quickLinks')" :mobile="true"></quick-links>
</div>-->
<!-- END OF HIDDEN QUCK LINKS -->
<!-- MAIN PANNEL -->
<div class="col-sm-12 col-md-8 col-xxl-9 ">
<!-- ROW WITH PROFIL IMAGE AND INFORMATION -->
@@ -112,12 +111,18 @@ export default {
<!-- ROW WITH THE PROFIL INFORMATION -->
<div class="row mb-4">
<!-- FIRST KAESTCHEN -->
<div class="col-lg-12 col-xl-6 ">
<div class="col-lg-12 col-xl-6 ">
<div class="row mb-4">
<div class="col">
<profil-information :data="profilInformation" :title="$p.t('profil','studentIn')" :fotoStatus="fotoStatus"></profil-information>
</div>
</div>
<!-- SECOND ROW OF FIRST COLUMN -->
<div class="row mb-4">
<div class="col">
</div>
</div>
<!-- START OF SECOND PROFIL INFORMATION COLUMN -->
<!-- END OF PROFIL INFORMATION ROW -->
<!-- INFORMATION CONTENT END -->
@@ -145,17 +150,12 @@ export default {
</div>
<!-- START OF SIDE PANEL -->
<div class="col-md-4 col-xxl-3 col-sm-12 text-break" >
<!-- SRART OF QUICK LINKS IN THE SIDE PANEL -->
<!-- START OF THE FIRDT ROW IN THE SIDE PANEL -->
<!-- THESE QUCK LINKS ARE ONLY VISIBLE UNTIL VIEWPORT MD -->
<!--TODO: uncomment when implemented
<div class="row d-none d-md-block mb-3">
<div class="col">
<quick-links :title="$p.t('profil','quickLinks')"></quick-links>
</div>
</div>-->
<!-- START OF THE FIRST ROW IN THE SIDE PANEL -->
<div v-if="quickLinks.length" class="row mb-4">
<div class="col">
<quick-links :title="$p.t('profil/quickLinks')" :links="quickLinks" />
</div>
</div>
<!-- START OF THE SECOND ROW IN THE SIDE PANEL -->
<div class="row">
<div class="col">
@@ -3,14 +3,12 @@ import VueDatePicker from '../../vueDatepicker.js.php';
import ApiOrt from '../../../api/factory/ort.js'
export const Raumsuche = {
name: "Raumsuche",
props: {
},
components: {
VueDatePicker,
CoreFilterCmpt,
InputNumber: primevue.inputnumber,
},
inject: ["isMobile"],
data() {
return {
phrasenPromise: null,
@@ -194,8 +192,9 @@ export const Raumsuche = {
<h1 class="h3">{{$p.t('rauminfo/roomSearch')}}</h1>
<hr>
<div class="row">
<div class="col-12 col-lg-2">
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-2">
<VueDatePicker
@contextmenu="(e) => {if (isMobile) {e.preventDefault();}}"
:dark="isDarkMode"
v-model="datum"
:clearable="false"
@@ -207,8 +206,9 @@ export const Raumsuche = {
auto-apply>
</VueDatePicker>
</div>
<div class="col-12 col-lg-1">
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-1">
<VueDatePicker
@contextmenu="(e) => {if (isMobile) {e.preventDefault();}}"
:dark="isDarkMode"
v-model="von"
:clearable="false"
@@ -220,8 +220,9 @@ export const Raumsuche = {
>
</VueDatePicker>
</div>
<div class="col-12 col-lg-1">
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-1">
<VueDatePicker
@contextmenu="(e) => {if (isMobile) {e.preventDefault();}}"
:dark="isDarkMode"
v-model="bis"
:clearable="false"
@@ -233,7 +234,7 @@ export const Raumsuche = {
</VueDatePicker>
</div>
<div class="col-12 col-lg-3">
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-3">
<select ref="raumtyp" id="raumtypSelect" v-model="selectedType" class="form-select"
:aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setRoute($event.target.value)">
<option :key="defaultType" selected :value="defaultType">{{defaultType.beschreibung}}</option>
@@ -242,7 +243,7 @@ export const Raumsuche = {
</div>
<div class="col-12 col-lg-3">
<div :class="{'pb-2': isMobile}" class="col-12 col-lg-3">
<InputNumber v-model="anzahl"
:prefix="$p.t('rauminfo/minCapacity') + ': '"
inputId="anzahlInput" :min="1" :max="1000"
@@ -31,7 +31,7 @@ export default {
this.event.lektor.slice(0, 3).map(lektor => lektor.kurzbz).join("\n")
+ "\n" + this.$p.t('lehre/weitereLektoren', [this.event.lektor.length - 3])
].join(": "));
} else {;
} else {
tooltipArray.push([
this.$p.t('lehre/lektor'),
this.event.lektor.map(lektor => lektor.kurzbz).join("\n")
@@ -142,7 +142,6 @@ export default {
</tr>
</tbody>
</table>
<lv-menu :containerStyles="['p-0']" :rowStyles="['m-0']" v-if="lvMenu.length" :menu="lvMenu" />
<lv-menu v-if="lvMenu.length && $route.name === 'MyLvPlan'" :containerStyles="['p-0']" :rowStyles="['m-0']" :menu="lvMenu" />
</div>`,
}
+67 -31
View File
@@ -1,5 +1,6 @@
import LvUebersicht from "../Mylv/LvUebersicht.js";
import ApiCisStudium from '../../../api/factory/cis/studium.js';
export default {
data(){
@@ -26,6 +27,7 @@ export default {
}
},
name: "OverviewStudiengaenge",
components: {
LvUebersicht,
},
@@ -88,6 +90,27 @@ export default {
studienordnung.selectedIndex = newSelectIndex;
this.changeSelectedStudienPlan(studienordnung.value);
},
onStudiensemesterChange(event) {
const value = event.target.value;
this.setHash(value);
this.changeSelectedStudienSemester(value);
},
onStudiengangChange(event) {
const value = event.target.value;
this.setHash(value);
this.changeSelectedStudienGang(value);
},
onSemesterChange(event) {
const value = event.target.value;
this.setHash(value);
this.changeSelectedSemester(value);
},
onStudienordnungChange(event) {
const value = event.target.value;
this.setHash(value);
this.changeSelectedStudienPlan(value);
},
storeDataToLocalStorage(key,value){
localStorage.setItem(key, value);
@@ -97,28 +120,32 @@ export default {
return value;
},
changeSelectedStudienSemester(studiensemester_kurzbz) {
this.$fhcApi.factory.studium.getAllStudienSemester(studiensemester_kurzbz, this.selectedStudiengang, this.selectedSemester, this.selectedStudienordnung)
return this.$api
.call(ApiCisStudium.getAllStudienSemester(studiensemester_kurzbz, this.selectedStudiengang, this.selectedSemester, this.selectedStudienordnung))
.then(data => data.data)
.then(res => {
this.extractPropertyValues(res);
})
},
changeSelectedStudienGang(studiengang_kz) {
this.$fhcApi.factory.studium.getAllStudienSemester(this.selectedStudiensemester, studiengang_kz, this.selectedSemester, this.selectedStudienordnung)
return this.$api
.call(ApiCisStudium.getAllStudienSemester(this.selectedStudiensemester, studiengang_kz, this.selectedSemester, this.selectedStudienordnung))
.then(data => data.data)
.then(res => {
this.extractPropertyValues(res);
})
},
changeSelectedSemester(semester) {
this.$fhcApi.factory.studium.getAllStudienSemester(this.selectedStudiensemester, this.selectedStudiengang, semester, this.selectedStudienordnung)
return this.$api
.call(ApiCisStudium.getAllStudienSemester(this.selectedStudiensemester, this.selectedStudiengang, semester, this.selectedStudienordnung))
.then(data => data.data)
.then(res => {
this.extractPropertyValues(res);
})
},
changeSelectedStudienPlan(studienplan_id) {
this.$fhcApi.factory.studium.getAllStudienSemester(this.selectedStudiensemester, this.selectedStudiengang, this.selectedSemester, studienplan_id)
return this.$api
.call(ApiCisStudium.getAllStudienSemester(this.selectedStudiensemester, this.selectedStudiengang, this.selectedSemester, studienplan_id))
.then(data => data.data)
.then(res => {
this.extractPropertyValues(res);
@@ -160,6 +187,7 @@ export default {
this.studiengaenge = studiengang.all;
this.selectedStudiengang = studiengang.preselected?.studiengang_kz;
this.semester = semester.all;
this.selectedSemester = semester?.preselected;
@@ -195,7 +223,11 @@ export default {
},
studiengangTitel(studiengang) {
if (!studiengang) return "";
return `${studiengang?.kurzbzlang} (${studiengang?.bezeichnung})`;
if(this.isGermanLanguage){
return `${studiengang?.kurzbzlang} (${studiengang?.bezeichnung})`;
}else{
return `${studiengang?.kurzbzlang} (${studiengang?.english})`;
}
},
studiensemesterTitel(studiensemester){
if (!studiensemester) return "";
@@ -213,9 +245,12 @@ export default {
},
computed:{
isGermanLanguage(){
return this.$p.user_language.value == "German"
},
selectedLehrveranstaltungTitel(){
const studiengang = this.studiengaenge.find((studiengang) => studiengang.studiengang_kz == this.selectedStudiengang);
return `${this.selectedLehrveranstaltung?.bezeichnung} ${this.selectedLehrveranstaltung?.lehrform_kurzbz} / ${studiengang.kurzbzlang}-${this.selectedSemester} ${this.selectedLehrveranstaltung?.orgform_kurzbz} (${this.selectedStudiensemester})`;
return `${this.isGermanLanguage ? this.selectedLehrveranstaltung?.bezeichnung : this.selectedLehrveranstaltung?.bezeichnung_english} ${this.selectedLehrveranstaltung?.lehrform_kurzbz} / ${studiengang.kurzbzlang}-${this.selectedSemester} ${this.selectedLehrveranstaltung?.orgform_kurzbz} (${this.selectedStudiensemester})`;
},
computedStudienOrdnung(){
if(!this.studienOrdnung) return null;
@@ -232,14 +267,14 @@ export default {
let result = [];
Object.entries(this.computedStudienOrdnung).forEach(([key,value])=>{
result.push({
bezeichnung: `Studienordnung: ${key}`,
bezeichnung: `${this.$p.t('studium', 'studienordnung') }: ${key}`,
disabled: true,
});
value.forEach((studienplan)=>{
result.push({
studienplan:studienplan,
diabled: false,
bezeichnung: `${studienplan?.bezeichnung}-${studienplan?.orgform_kurzbz} ( ${studienplan?.orgform_bezeichnung}, ${studienplan?.sprache} )`
bezeichnung: `${studienplan?.bezeichnung}-${studienplan?.orgform_kurzbz} ( ${this.isGermanLanguage ? studienplan?.orgform_bezeichnung : studienplan?.orgform_bezeichnung_english}, ${studienplan?.sprache} )`
});
})
@@ -256,22 +291,23 @@ export default {
const studienordnung = JSON.parse(this.getDataFromLocalStorage("studienordnung")) ?? undefined;
// only fetch default data if no data is stored in the local storage
this.$fhcApi.factory.studium.getAllStudienSemester(studiensemester, studiengang, semester, studienordnung)
.then(data => data.data)
.then(res => {
this.extractPropertyValues(res);
})
this.$api
.call(ApiCisStudium.getAllStudienSemester(studiensemester, studiengang, semester, studienordnung))
.then(data => data.data)
.then(res => {
this.extractPropertyValues(res);
})
},
template: `
template: /*html*/`
<div>
<h2>Studium</h2>
<h2>{{$p.t('studium','studium')}}</h2>
<hr>
<lv-uebersicht ref="lvUebersicht" :titel="selectedLehrveranstaltungTitel" :event="selectedLehrveranstaltung" :studiensemester="selectedStudiensemester" v-if="selectedLehrveranstaltung">
<template #content>
<div v-if="Array.isArray(selectedLehrveranstaltung.lektoren) && selectedLehrveranstaltung.lektoren.length>0" class="mb-4">
<h4>Lektoren:</h4>
<h4>{{$p.t('studium','lektoren')}}:</h4>
<a :href="'mailto:'+lektor?.email" class="fhc-link-color mx-2" v-for="lektor in selectedLehrveranstaltung.lektoren">{{lektor.name}}</a>
</div>
<h4>Menu:</h4>
@@ -279,13 +315,13 @@ export default {
</lv-uebersicht>
<div class="lvOptions">
<div>
<h6>Studiensemester:</h6>
<h6>{{$p.t('studium','studiensemester')}}:</h6>
<div class="input-group">
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeStudiensemester(1)" :aria-label="$p.t('global','previous')" :title="$p.t('global','previous')">
<i class="fa fa-caret-left" aria-hidden="true"></i>
</button>
<select ref="studiensemester" v-model="selectedStudiensemester" class="form-select" :aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setHash($event.target.value)">
<option v-for="semester in studienSemester" @click="changeSelectedStudienSemester(semester.studiensemester_kurzbz)" :key="semester" :value="semester.studiensemester_kurzbz">{{studiensemesterTitel(semester.studiensemester_kurzbz) }}</option>
<select ref="studiensemester" v-model="selectedStudiensemester" @change="onStudiensemesterChange" class="form-select" :aria-label="$p.t('global/studiensemester_auswaehlen')">
<option v-for="semester in studienSemester" :key="semester" :value="semester.studiensemester_kurzbz">{{studiensemesterTitel(semester.studiensemester_kurzbz) }}</option>
</select>
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeStudiensemester(-1)" :aria-label="$p.t('global','next')" :title="$p.t('global','next')">
<i class="fa fa-caret-right" aria-hidden="true"></i>
@@ -294,13 +330,13 @@ export default {
</div>
<div>
<h6>Studiengang:</h6>
<h6>{{$p.t('lehre','studiengang')}}:</h6>
<div class="input-group">
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeStudiengang(-1)" :aria-label="$p.t('global','previous')" :title="$p.t('global','previous')">
<i class="fa fa-caret-left" aria-hidden="true"></i>
</button>
<select ref="studiengaenge" v-model="selectedStudiengang" class="form-select" :aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setHash($event.target.value)">
<option v-for="studiengang in studiengaenge" @click="changeSelectedStudienGang(studiengang.studiengang_kz)" :key="studiengang.studiengang_kz" :value="studiengang.studiengang_kz" >{{studiengangTitel(studiengang)}}</option>
<select ref="studiengaenge" v-model="selectedStudiengang" class="form-select" @change="onStudiengangChange" :aria-label="$p.t('global/studiensemester_auswaehlen')">
<option v-for="studiengang in studiengaenge" :key="studiengang.studiengang_kz" :value="studiengang.studiengang_kz" >{{studiengangTitel(studiengang)}}</option>
</select>
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeStudiengang(1)" :aria-label="$p.t('global','next')" :title="$p.t('global','next')">
<i class="fa fa-caret-right" aria-hidden="true"></i>
@@ -309,13 +345,13 @@ export default {
</div>
<div>
<h6>Semester:</h6>
<h6>{{$p.t('lehre','semester')}}:</h6>
<div class="input-group">
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeSemester(-1)" :aria-label="$p.t('global','previous')" :title="$p.t('global','previous')">
<i class="fa fa-caret-left" aria-hidden="true"></i>
</button>
<select ref="semester" v-model="selectedSemester" class="form-select" :aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setHash($event.target.value)">
<option v-for="sem in semester" @click="changeSelectedSemester(sem)" :key="semester" :value="sem">{{sem}}. Semester</option>
<select ref="semester" v-model="selectedSemester" class="form-select" @change="onSemesterChange" :aria-label="$p.t('global/studiensemester_auswaehlen')">
<option v-for="sem in semester" :key="sem" :value="sem">{{sem}}. Semester</option>
</select>
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeSemester(1)" :aria-label="$p.t('global','next')" :title="$p.t('global','next')">
<i class="fa fa-caret-right" aria-hidden="true"></i>
@@ -324,13 +360,13 @@ export default {
</div>
<div>
<h6>Studienordnung:</h6>
<h6>{{$p.t('studium','studienordnung')}}:</h6>
<div class="input-group">
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeStudienordnung(-1)" :aria-label="$p.t('global','previous')" :title="$p.t('global','previous')">
<i class="fa fa-caret-left" aria-hidden="true"></i>
</button>
<select ref="studienordnung" v-model="selectedStudienordnung" class="form-select" :aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setHash($event.target.value)">
<option v-for="ordnung in computedStudienOrdnungSelectValues" :disabled="ordnung.disabled" @click="changeSelectedStudienPlan(ordnung?.studienplan?.studienplan_id)" :key="ordnung?.studienplan?.bezeichnung " :value="ordnung?.studienplan?.studienplan_id">{{ordnung.bezeichnung}}</option>
<select ref="studienordnung" v-model="selectedStudienordnung" class="form-select" @change="onStudienordnungChange" :aria-label="$p.t('global/studiensemester_auswaehlen')">
<option v-for="ordnung in computedStudienOrdnungSelectValues" :disabled="ordnung.disabled" :key="ordnung?.studienplan?.studienplan_id" :value="ordnung?.studienplan?.studienplan_id">{{ordnung.bezeichnung}}</option>
</select>
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeStudienordnung(1)" :aria-label="$p.t('global','next')" :title="$p.t('global','next')">
<i class="fa fa-caret-right" aria-hidden="true"></i>
@@ -345,13 +381,13 @@ export default {
<template v-for="lehrveranstaltung in lehrveranstaltungen" :key="lehrveranstaltung.lehrveranstaltung_id">
<div class="card" v-if="Array.isArray(lehrveranstaltung.lehrveranstaltungen) && lehrveranstaltung.lehrveranstaltungen.length >0" >
<div class="card-header">
<h5 class=" card-title">{{lehrveranstaltung.bezeichnung}}</h5>
<h5 class=" card-title">{{isGermanLanguage == 'German' ? lehrveranstaltung.bezeichnung : lehrveranstaltung.bezeichnung_english }}</h5>
<h6 class=" card-subtitle">{{lehrveranstaltung.lehrform_kurzbz}}</h6>
</div>
<div class="card-body">
<ul class="list-group list-group-flush">
<li class="d-flex list-group-item" v-for="lv in lehrveranstaltung.lehrveranstaltungen">
<a class="fhc-link-color d-block me-auto" href="#" @click="openLvUebersicht(lv)">{{lv.bezeichnung}}</a>
<a class="fhc-link-color d-block me-auto" href="#" @click="openLvUebersicht(lv)">{{isGermanLanguage == 'German' ? lv.bezeichnung : lv.bezeichnung_english}}</a>
<p>{{lv.lehrform_kurzbz}}</p>
</li>
</ul>
@@ -23,6 +23,32 @@ export default {
computed: {
pickerWidgets() {
return this.widgets.filter(widget => widget.allowed);
},
sizeLimits() {
return Object.fromEntries(this.widgets.map(({ setup, widget_id: type }) => {
const result = {}; // work on a copy
if (setup.height === undefined)
result.height = { min: 1, max: undefined };
else if (Number.isInteger(setup.height))
result.height = { min: setup.height, max: setup.height };
else
result.height = {
min: setup.height.min ?? 1,
max: setup.height.max
};
if (setup.width === undefined)
result.width = { min: 1, max: undefined };
else if (Number.isInteger(setup.width))
result.width = { min: setup.width, max: setup.width };
else
result.width = {
min: setup.width.min ?? 1,
max: setup.width.max
};
return [type, result];
}));
}
},
watch: {
@@ -35,6 +61,12 @@ export default {
widgetAdd(widget, section_name) {
this.$refs.widgetpicker.getWidget().then(widget_id => {
widget.widget = widget_id;
// NOTE(chris): min size
widget.place = Object.fromEntries(Object.entries(widget.place).map(([key, value]) => {
value.w = this.sizeLimits[widget_id].width.min;
value.h = this.sizeLimits[widget_id].height.min;
return [key, value];
}));
widget.id = 'loading_' + String((new Date()).valueOf());
delete widget.custom;
widget.preset = 1;
@@ -45,6 +77,8 @@ export default {
section.widgets.push(loading);
});
delete widget.id;
const params = {
dashboard: this.dashboard,
funktion_kurzbz: section_name,
@@ -78,7 +112,10 @@ export default {
const section = this.sections.find(section => section.name == section_name);
for (var wid in section.widgets) {
if (section.widgets[wid].id == k) {
payload[k] = ObjectUtils.mergeDeep(section.widgets[wid], payload[k]);
const copy = ObjectUtils.mergeDeep(section.widgets[wid], payload[k]);
if (payload[k].config)
copy.config = payload[k].config;
payload[k] = copy;
// NOTE(chris): remove internal props
for (var prop of ['_x', '_y', '_w', '_h', 'index', 'id', 'custom'])
if (payload[k][prop])
+56 -12
View File
@@ -2,6 +2,7 @@ import DashboardSection from "./Section.js";
import DashboardWidgetPicker from "./Widget/Picker.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';
@@ -17,26 +18,50 @@ export default {
required: true,
default: 'CIS'
},
viewData: {
type: Object,
required: true,
validator(value) {
return value && value.name
}
}
},
data() {
return {
widgets: [],
originalWidgets: {},
widgetsSetup: null,
editMode: false
editMode: false,
timezone: null,
userFirstName: null,
}
},
provide() {
return {
editMode: Vue.computed(() => this.editMode),
widgetsSetup: Vue.computed(() => this.widgetsSetup)
widgetsSetup: Vue.computed(() => this.widgetsSetup),
timezone: this.timezone
}
},
computed: {
sizeLimits() {
return Object.fromEntries(this.widgetsSetup.map(({ setup, widget_id: type }) => {
const result = {}; // work on a copy
if (setup.height === undefined)
result.height = { min: 1, max: undefined };
else if (Number.isInteger(setup.height))
result.height = { min: setup.height, max: setup.height };
else
result.height = {
min: setup.height.min ?? 1,
max: setup.height.max
};
if (setup.width === undefined)
result.width = { min: 1, max: undefined };
else if (Number.isInteger(setup.width))
result.width = { min: setup.width, max: setup.width };
else
result.width = {
min: setup.width.min ?? 1,
max: setup.width.max
};
return [type, result];
}));
}
},
methods: {
@@ -45,10 +70,18 @@ export default {
.getWidget()
.then(widget_id => {
widget.widget = widget_id;
// NOTE(chris): min size
widget.place = Object.fromEntries(Object.entries(widget.place).map(([key, value]) => {
value.w = this.sizeLimits[widget_id].width.min;
value.h = this.sizeLimits[widget_id].height.min;
return [key, value];
}));
widget.id = 'loading_' + String((new Date()).valueOf());
let loading = { ...widget };
loading.loading = true;
this.widgets.push(loading);
delete widget.id;
this.$api
.call(ApiDashboardUser.addWidget(this.dashboard, widget))
@@ -66,7 +99,10 @@ export default {
for (var k in payload) {
for (var wid in this.widgets) {
if (this.widgets[wid].id == k) {
payload[k] = ObjectUtils.mergeDeep(this.widgets[wid], payload[k]);
const copy = ObjectUtils.mergeDeep(this.widgets[wid], payload[k]);
if (payload[k].config)
copy.config = payload[k].config;
payload[k] = copy;
// NOTE(chris): remove internal props
for (var prop of ['_x', '_y', '_w', '_h', 'index', 'id', 'preset'])
if (payload[k][prop])
@@ -123,11 +159,19 @@ export default {
this.widgets = this.widgets.filter(widget => widget.id != id);
})
.catch(this.$fhcAlert.handleSystemError);
},
async fetchViewData() {
let viewDataResult = await this.$api.call(ApiDashboard.getViewData());
const viewData = viewDataResult.data;
this.timezone = viewData?.timezone;
this.userFirstName = viewData?.name;
}
},
created() {
async created() {
this.$p.loadCategory('dashboard');
await this.fetchViewData();
this.$api
.call(ApiDashboardWidget.listAllowed(this.dashboard))
.then(res => {
@@ -161,7 +205,7 @@ export default {
template: /* html */`
<div class="core-dashboard">
<h3>
{{ $p.t('global/personalGreeting', [ viewData?.name ]) }}
{{ userFirstName ? $p.t('global/personalGreeting', [ userFirstName ]) : '' }}
<button
class="btn ms-2"
aria-label="edit dashboard"
+3
View File
@@ -324,6 +324,7 @@ export default {
</div>
</Transition>
</header>
<!-- TODO Manu rename/remove-->
<div v-if="ready" class="card-body overflow-hidden p-0">
<component
:is="component"
@@ -331,6 +332,8 @@ export default {
:config="arguments"
:width="width"
:height="height"
:widget-id="item_data.id"
:item_data="item_data"
@setConfig="setConfig"
@change="changeConfigManually"
></component>
+1 -1
View File
@@ -240,7 +240,7 @@ export default {
<div
v-if="item.placeholder"
class="empty-tile-hover"
@click="$emit('widgetAdd', { widget: 1, config: {}, place: {[gridWidth]: {x:item.x,y:item.y,w:1,h:1}}, custom: 1 }, name)"
@click="$emit('widgetAdd', { config: [], place: {[gridWidth]: { x: item.x, y: item.y, w: 1, h: 1 } }, custom: 1 }, name)"
></div>
<dashboard-item
v-else
@@ -5,7 +5,9 @@ export default {
"height",
"configMode",
"sharedData",
"widgetInfo"
"widgetInfo",
"widgetId",
"item_data"
],
emits: [
"setConfig",
+383 -120
View File
@@ -3,23 +3,27 @@ import FormInput from "../Form/Input.js";
import BsModal from "../Bootstrap/Modal.js";
import AbstractWidget from './Abstract.js';
import ApiBookmark from '../../api/factory/widget/bookmark.js';
import { useUrlStore } from '../../composables/Pseudostore/DashboardWidget/UrlStore.js';
export default {
name: "WidgetsUrl",
mixins: [AbstractWidget],
inject: {
editModeIsActive: {
type: Boolean,
default: false
}
},
components:{
CoreForm,
FormInput,
BsModal
BsModal,
PvChips: primevue.chips,
PvMultiSelect: primevue.multiselect,
PvAutoComplete: primevue.autocomplete,
},
mixins: [AbstractWidget],
inject: {
adminMode: {
from: 'adminMode',
default: false
}
},
data: () => ({
ready: false,
bookmark_id: null,
title_input: "",
url_input: "",
@@ -27,29 +31,53 @@ export default {
invalidURL: false,
invalidTitel: false,
},
selectedTags: [],
selectedFilters: [],
filteredArray: []
}),
computed: {
tagName() {
return this.config.tag !== undefined && this.config.tag.length > 0
? this.config.tag
: this.$p.t("bookmark", "myBookmarks");
availableTags() {
return (this.bookmarks || [])
.map(bookmark => JSON.parse(bookmark.tag))
.flat()
.filter((v, i, a) => v && a.indexOf(v) === i);
},
emptyBookmarks() {
if (this.shared instanceof Array && this.shared.length == 0) return true;
filteredBookmarks() {
if (!this.bookmarks)
return [];
if (!this.shared) return true;
if (!this.config.tags || !this.config.tags.length)
return this.bookmarks;
return false;
return this.bookmarks.filter(bookmark => {
const tags = JSON.parse(bookmark.tag || "[]");
return tags.some(tag => this.config.tags.includes(tag));
});
},
newSort() {
if (this.bookmarks.length == 0)
return 1;
else
return Math.max(...this.bookmarks.map(b => b.sort)) + 1;
},
maxSort() {
if (this.bookmarks.length == 0)
return 0;
else
return Math.max(...this.filteredBookmarks.map(b => b.sort));
},
minSort() {
if (this.bookmarks.length == 0)
return 0;
else
return Math.min(...this.filteredBookmarks.map(b => b.sort));
}
},
methods: {
stopDrag(event){
event.preventDefault();
},
clearInputs(){
clearInputs() {
this.title_input = "";
this.url_input = "";
this.url_input = "";
this.selectedTags = [];
},
openCreateModal() {
this.$refs.createModal.show()
@@ -58,23 +86,23 @@ export default {
this.title_input = bookmark.title;
this.url_input = bookmark.url;
this.bookmark_id = bookmark.bookmark_id;
this.$refs.editModal.show()
this.selectedTags = JSON.parse(bookmark.tag);
this.$refs.editModal.show();
},
editBookmark(event){
event.preventDefault();
if(!this.bookmark_id || !this.url_input || !this.title_input) return;
this.$api
.call(ApiBookmark.update({
bookmark_id: this.bookmark_id,
title: this.title_input,
url: this.url_input,
}))
.then((res) => res.data)
.then((result) => {
editBookmark() {
if (!this.bookmark_id || !this.url_input || !this.title_input)
return;
this.actions
.update(
this.bookmark_id,
this.title_input,
this.url_input,
this.selectedTags
)
.then(() => {
this.$fhcAlert.alertInfo(this.$p.t("bookmark", "bookmarkUpdated"));
// refetch the bookmarks to see the updates
this.fetchBookmarks();
// reset the values for the title and url inputs
this.clearInputs();
this.$refs.editModal.hide();
@@ -82,10 +110,7 @@ export default {
})
.catch(this.$fhcAlert.handleSystemError);
},
insertBookmark(event) {
event.preventDefault();
insertBookmark() {
// reset is-invalid css on url input field
for (let key of Object.keys(this.validation)) {
this.validation[key] = false;
@@ -94,24 +119,24 @@ export default {
// early return if validation failed
if (!this.isValidationSuccessfull()) return;
this.$api
.call(ApiBookmark.insert({
tag: this.config.tag,
title: this.title_input,
url: this.url_input,
}))
.then((res) => res.data)
.then((result) => {
// get highest Sort
const sort = this.newSort;
this.actions
.insert(
this.title_input,
this.url_input,
this.selectedTags,
sort
)
.then(() => {
this.$fhcAlert.alertInfo(this.$p.t("bookmark", "bookmarkAdded"));
// refetch the bookmarks to see the updates
this.fetchBookmarks();
this.$refs.createModal.hide();
// reset the values for the title and url inputs
this.clearInputs();
})
.catch(this.$fhcAlert.handleSystemError);
},
isValidationSuccessfull() {
// validate the input fields
if (this.title_input.length === 0) {
@@ -125,111 +150,349 @@ export default {
return !Object.values(this.validation).some(value => value === true);
},
async fetchBookmarks() {
await this.$api
.call(ApiBookmark.getBookmarks())
.then((res) => res.data)
.then((result) => {
this.shared = result;
})
.catch(this.$fhcAlert.handleSystemError);
},
async removeLink(bookmark_id) {
let isConfirmed = await this.$fhcAlert.confirmDelete();
// early return if the confirm dialog was not confirmed
if (!isConfirmed) return;
if (!isConfirmed)
return;
this.$api
.call(ApiBookmark.delete(bookmark_id))
.then((res) => res.data)
.then((result) => {
this.$fhcAlert.alertInfo(this.$p.t("bookmark", "bookmarkDeleted"));
// refetch the bookmarks to see the updates
this.fetchBookmarks();
})
.catch(this.$fhcAlert.handleSystemError);
const errors = await this.actions.remove(bookmark_id);
if (!errors) {
this.$fhcAlert.alertInfo(this.$p.t("bookmark", "bookmarkDeleted"));
}
},
sortDown(bookmark_id) {
const current = this.filteredBookmarks.find(b => b.bookmark_id === bookmark_id);
const next = this.filteredBookmarks
.filter(b => b.sort > current.sort)
.sort((a, b) => a.sort - b.sort)[0];
if (!next) {
console.log("lowest sort item, no change");
return;
}
this.actions.swap(current.bookmark_id, next.bookmark_id);
},
sortUp(bookmark_id) {
const current = this.filteredBookmarks.find(b => b.bookmark_id === bookmark_id);
const next = this.filteredBookmarks
.filter(b => b.sort < current.sort)
.sort((a, b) => a.sort + b.sort)[0];
if (!next) {
console.log("highest sort item, no change");
return;
}
this.actions.swap(current.bookmark_id, next.bookmark_id);
},
hasTags(link) {
if (!link || !link.tag) return false;
let tags = link.tag;
if (typeof tags === 'string') {
try {
tags = JSON.parse(tags)
} catch {
return false;
}
}
if (Array.isArray(tags) && tags.length > 0) {
return tags.join(' ');
}
},
openFilterModal() {
if (this.config.tags && this.config.tags.length)
this.selectedFilters = [ ...this.config.tags ];
else
this.selectedFilters = [];
this.$refs.filterModal.show();
},
async handleAddingTagFilter() {
this.config.tags = this.selectedFilters;
this.$emit('change');
this.$fhcAlert.alertInfo(this.$p.t("bookmark", "filterUpdated"));
this.$refs.filterModal.hide();
},
search(event) {
const query = event.query ?? "";
// Filter for text
this.filteredArray = this.availableTags.filter(item =>
item.toLowerCase().includes(query.toLowerCase())
);
// input if search not successful
if (this.filteredArray.length === 0 && query) {
this.filteredArray = [query];
}
}
},
setup() {
const {
bookmarks,
getters: { tags },
actions
} = useUrlStore();
return { bookmarks, tags, actions }
},
async mounted() {
await this.fetchBookmarks();
},
created() {
//
// this.$emit('setConfig', true); -> use this to enable widget config mode if needed
if (!this.adminMode) {
await this.actions.fetch();
this.ready = true;
}
},
template: /*html*/ `
<div class="widgets-url w-100 h-100 overflow-auto" style="padding: 1rem 1rem;">
<div class="d-flex flex-column justify-content-between">
<button class="btn btn-outline-secondary btn-sm w-100 mt-2 card" @click="openCreateModal" type="button">{{$p.t('bookmark','newLink')}}</button>
<div class="widgets-url w-100 h-100 overflow-auto p-3">
<template v-if="shared">
<div v-if="!adminMode" class="d-flex mt-2">
<button
class="btn btn-outline-secondary btn-sm flex-grow-1 me-2"
@click="openCreateModal"
>
{{ $p.t('bookmark', 'newLink') }}
</button>
<button
v-if="config.tags && config.tags.length"
class="btn btn-secondary btn-sm"
:title="$p.t('bookmark/editFilter')"
@click="openFilterModal"
>
<i class="fa-solid fa-filter-circle-xmark"></i>
</button>
<button
v-else
class="btn btn-outline-secondary btn-sm"
:title="$p.t('bookmark/filterByTags')"
@click="openFilterModal"
>
<i class="fa-solid fa-filter"></i>
</button>
</div>
<template v-if="!emptyBookmarks">
<div v-for="link in shared" :key="link.id" class="d-flex mt-2">
<a target="_blank" :href="link.url">
<i class="fa fa-solid fa-arrow-up-right-from-square me-1"></i>{{ link.title }}
<div
v-if="adminMode"
class="h-100 d-flex align-items-center text-center"
>
{{ $p.t('bookmark/adminMode') }}
</div>
<template v-else-if="ready">
<template v-if="filteredBookmarks.length">
<div
v-for="link in filteredBookmarks"
:key="link.id"
class="d-flex mt-2"
>
<a target="_blank" :href="link.url" class="me-1">
<i
class="fa fa-solid fa-arrow-up-right-from-square me-1"
aria-hidden="true"
></i>
{{ link.title }}
</a>
<span
v-if="hasTags(link)"
:title="hasTags(link)"
style="color: silver;"
>
<i
class="fa fa-solid fa-tag text-gray-500"
aria-hidden="true"
></i>
</span>
<div class="ms-auto">
<!--EDIT BOOKMARK-->
<a
type="button"
href="#"
:title="$p.t('bookmark/editBookmark')"
aria-label="edit bookmark"
@click.prevent="openEditModal(link)"
>
<i class="fa fa-edit me-1" aria-hidden="true"></i>
</a>
<!--DELETE BOOKMARK-->
<a
type="button"
href="#"
:title="$p.t('bookmark/deleteBookmark')"
aria-label="delete bookmark"
@click.prevent="removeLink(link.bookmark_id)"
>
<i class="fa fa-regular fa-trash-can" aria-hidden="true"></i>
</a>
<!--SORT BOOKMARKS-->
<a
v-if="filteredBookmarks.length > 1"
type="button"
href="#"
:title="$p.t('bookmark/sortDownwards')"
aria-label="sortdown bookmark"
@click.prevent="sortDown(link.bookmark_id)"
>
<i
class="fa fa-arrow-down me-1"
:class="{ 'text-light pointer-events-none': link.sort === maxSort }"
></i>
</a>
<a
v-if="filteredBookmarks.length > 1"
type="button"
href="#"
:title="$p.t('bookmark/sortToTop')"
aria-label="sortup bookmark"
@click.prevent="sortUp(link.bookmark_id)"
>
<i
class="fa fa-arrow-up me-1"
:class="{ 'text-light pointer-events-none': link.sort === minSort }"
></i>
</a>
<div class="ms-auto">
<!--EDIT BOOKMARK-->
<a type="button" href="#" @click.prevent="openEditModal(link)" aria-label="edit bookmark" v-tooltip="{showDelay:1000,value:'edit bookmark'}">
<i class="fa fa-edit me-1" aria-hidden="true"></i>
</a>
<!--DELETE BOOKMARK-->
<a type="button" id="deleteBookmark" href="#" aria-label="delete bookmark" v-tooltip="{showDelay:1000,value:'delete bookmark'}" @click.prevent="removeLink(link.bookmark_id)">
<i class="fa fa-regular fa-trash-can" aria-hidden="true"></i>
</a>
</div>
</div>
</template>
</div>
</template>
<div v-else class="d-flex mt-2">
<span>{{$p.t('bookmark','emptyBookmarks')}}</span>
</div>
</template>
<template v-else>
<p v-for="i in 4" class="placeholder-glow">
<span class="placeholder" :class="{'col-9' : true}"></span>
</p>
</template>
</div>
</div>
<div v-else class="d-flex mt-2">
<span>{{ $p.t('bookmark', 'emptyBookmarks') }}</span>
</div>
</template>
<template v-else>
<p v-for="i in 4" class="placeholder-glow">
<span class="placeholder col-9"></span>
</p>
</template>
</div>
<!--EDIT MODAL-->
<teleport to="body">
<bs-modal @[\`hide.bs.modal\`]="bookmark_id=null; clearInputs();" ref="editModal">
<bs-modal
ref="editModal"
@hide-bs-modal="bookmark_id=null; clearInputs();"
>
<template #title>
<h2>{{$p.t('bookmark','editLink')}}</h2>
<h2>{{ $p.t('bookmark', 'editLink') }}</h2>
</template>
<template #default>
<form-input :label="$p.t('profil','Titel')" :title="$p.t('profil','Titel')" id="editTitle" v-model="title_input" name="title" class="mb-2"></form-input>
<form-input label="Url" title="Url" id="editUrl" v-model="url_input" name="url"></form-input>
<form-input
id="editTitle"
name="title"
v-model="title_input"
:label="$p.t('profil', 'Titel')"
:title="$p.t('profil', 'Titel')"
class="mb-2"
></form-input>
<form-input
id="editUrl"
name="url"
v-model="url_input"
label="Url"
title="Url"
></form-input>
<label class="mt-2">Tags</label>
<div class="mt-2">
<pv-auto-complete
v-model="selectedTags"
multiple
dropdown
:suggestions="filteredArray"
@complete="search"
/>
</div>
</template>
<template #footer>
<button @click="editBookmark" class="btn btn-primary">{{$p.t('bookmark','saveLink')}}</button>
<button class="btn btn-primary" @click.prevent="editBookmark">
{{ $p.t('bookmark', 'saveLink') }}
</button>
</template>
</bs-modal>
</teleport>
<!--CREATE MODAL-->
<teleport to="body">
<bs-modal @[\`hide.bs.modal\`]="clearInputs();" ref="createModal">
<bs-modal ref="createModal" @hide-bs-modal="clearInputs();">
<template #title>
<h2>{{$p.t('bookmark','newLink')}}</h2>
<h2>{{ $p.t('bookmark', 'newLink') }}</h2>
</template>
<template #default>
<form-input :label="$p.t('profil','Titel')" :title="$p.t('profil','Titel')" id="insertTitle" v-model="title_input" name="title" class="mb-2"></form-input>
<form-input label="Url" title="Url" id="insertUrl" v-model="url_input" name="url"></form-input>
<form-input
id="insertTitle"
name="title"
v-model="title_input"
:label="$p.t('profil', 'Titel')"
:title="$p.t('profil', 'Titel')"
class="mb-2"
></form-input>
<form-input
id="insertUrl"
name="url"
v-model="url_input"
label="Url"
title="Url"
></form-input>
<label class="mt-2">Tags</label>
<div class="mt-2">
<pv-auto-complete
v-model="selectedTags"
multiple
dropdown
:suggestions="filteredArray"
@complete="search"
/>
</div>
</template>
<template #footer>
<button @click="insertBookmark" class="btn btn-primary">{{$p.t('bookmark','saveLink')}}</button>
<button class="btn btn-primary" @click.prevent="insertBookmark">
{{ $p.t('bookmark', 'saveLink') }}
</button>
</template>
</bs-modal>
</teleport>
<!--FILTER MODAL-->
<teleport to="body">
<bs-modal ref="filterModal" @hide-bs-modal="clearInputs();">
<template #title>
<h2>{{ $p.t('bookmark', 'headerFilterBookmark') }}</h2>
</template>
<template #default>
<div class="mt-2 row">
<div class="col-10">
<pv-multi-select
id="tagFilterUrl"
v-model="selectedFilters"
:options="availableTags"
display="chip"
:placeholder="$p.t('bookmark', 'noFilter')"
:maxSelectedLabels="3"
class="p-inputtext-sm w-100 me-2"
/>
</div>
</div>
</template>
<template #footer>
<button
class="btn btn-secondary"
:title="$p.t('bookmark', 'filterByTags')"
@click="handleAddingTagFilter()"
>
{{ $p.t('ui/ok') }}
</button>
</template>
</bs-modal>
</teleport>
`,
};
/*
Link JSON structure:
{
+1 -1
View File
@@ -258,7 +258,7 @@ export default {
if (this.mode != MODE_IDLE)
this.dragCancel();
const updated = this.createNewGrid(this.indexedItems);
let updated = this.createNewGrid(this.indexedItems);
this.correctedPositionUpdates = updated;
if (updated.length)
+52 -29
View File
@@ -1,37 +1,60 @@
export default {
components: {
paginator: primevue.paginator,
},
emits: ["update:rows"],
props: {
maxPageCount: {
type: Number,
default: 0,
},
page_size: {
type: Number,
required: true,
},
},
data() {
return {};
},
methods: {
newPageEvent: function (data) {
},
},
mounted() {},
template: /*html*/ `
<!-- Desktop -->
components: {
paginator: primevue.paginator,
},
emits: ["pageUpdated"],
props: {
maxPageCount: {
type: Number,
default: 0,
},
page_size: {
type: Number,
required: true,
},
page: {
type: Number | null,
default: null,
},
},
data() {
return {
first: 0,
rowsPerPageOptions: [10, 20, 30],
};
},
watch: {
page(newValue) {
this.first = (newValue - 1) * this.$props.page_size;
},
},
methods: {
afterPageUpdated(data) {
this.$emit("pageUpdated", { ...data, page: data.page + 1 });
this.first = data.page * this.$props.page_size;
},
},
template: /*html*/ `
<!-- Desktop -->
<div class="d-none d-md-block">
<paginator v-model:rows="page_size" @page="(data)=>$emit('page',{...data, page:data.page+1})" :rows="page_size" :totalRecords="maxPageCount" :rowsPerPageOptions="[10, 20, 30]" >
</paginator>
<paginator
v-model:rows="page_size"
v-model:first="first"
@page="afterPageUpdated($event)"
:totalRecords="maxPageCount"
:rowsPerPageOptions="rowsPerPageOptions"
></paginator>
</div>
<!-- Mobile -->
<div class="d-block d-md-none">
<paginator v-model:rows="page_size" @page="(data)=>$emit('page',{...data, page:data.page+1})" :rows="page_size" :totalRecords="maxPageCount" :rowsPerPageOptions="[10, 20, 30]" template="FirstPageLink PrevPageLink CurrentPageReport NextPageLink LastPageLink RowsPerPageDropdown">
</paginator>
<paginator
v-model:rows="page_size"
v-model:first="first"
@page="afterPageUpdated($event)"
:totalRecords="maxPageCount"
:rowsPerPageOptions="rowsPerPageOptions"
template="FirstPageLink PrevPageLink CurrentPageReport NextPageLink LastPageLink RowsPerPageDropdown"
></paginator>
</div>
`,
};
@@ -84,6 +84,14 @@ export default {
'microcredential_2',
'microcredential_3',
'microcredential_4',
'microdegree_1',
'microdegree_2',
'microdegree_3',
'microdegree_4',
'microdegreeabschluss_1',
'microdegreeabschluss_2',
'microdegreeabschluss_3',
'microdegreeabschluss_4',
]
},
documentDropdownObject: {}
+351 -303
View File
@@ -21,69 +21,71 @@ export default {
dms,
cms,
mergedStudent,
mergedPerson
mergedPerson,
},
props: {
searchoptions: {
type: Object,
required: true
},
searchfunction: {
type: Function,
required: true
},
showBtnSubmit: Boolean
},
provide() {
return {
query: Vue.computed(() => this.lastQuery)
};
},
data: function() {
return {
searchtimer: null,
hidetimer: null,
searchsettings: {
searchstr: this.getSearchStr(),
types: this.getInitiallySelectedTypes(),
},
searchresult: [],
searchmode: '',
showresult: false,
searching: false,
error: null,
abortController: null,
props: {
searchoptions: {
type: Object,
required: true,
},
searchfunction: {
type: Function,
required: true,
},
showBtnSubmit: Boolean,
},
provide() {
return {
query: Vue.computed(() => this.lastQuery),
};
},
inject: ["isMobile"],
data: function () {
return {
searchtimer: null,
hidetimer: null,
searchsettings: {
searchstr: this.getSearchStr(),
types: this.getInitiallySelectedTypes(),
},
searchresult: [],
searchmode: "",
showresult: false,
searching: false,
error: null,
abortController: null,
settingsDropdown: null,
lastQuery: ''
};
},
lastQuery: "",
isSearchShownInMobileView: false,
};
},
computed: {
searchTypesPlaceholder() {
if (!this.searchsettings.types.length) {
return Object.values(this.typeLabels).join(' / ');
return Object.values(this.typeLabels).join(" / ");
}
return this.searchsettings.types.map(type => this.typeLabels[type]).join(' / ');
return this.searchsettings.types
.map((type) => this.typeLabels[type])
.join(" / ");
},
types() {
if (!this.searchoptions.types)
return [];
if (!this.searchoptions.types) return [];
if (Array.isArray(this.searchoptions.types))
return this.searchoptions.types;
return Object.keys(this.searchoptions.types);
},
typeLabels() {
if (!this.searchoptions.types)
return {};
if (!this.searchoptions.types) return {};
if (Array.isArray(this.searchoptions.types)) {
return this.searchoptions.types.reduce((res, type) => {
res[type] = type;
return res
return res;
}, {});
}
return this.searchoptions.types;
}
},
},
template: /*html*/`
template: /*html*/ `
<form
ref="searchform"
class="d-flex me-3"
@@ -92,80 +94,91 @@ export default {
@focusin="searchfocusin"
@focusout="searchfocusout"
>
<div
ref="searchbox"
class="h-100 input-group me-2 searchbar_searchbox"
:class="showresult ? 'open' : 'closed'"
<span
v-if="isMobile"
type="button"
data-bs-toggle="collapse"
data-bs-target=".multi-collapse"
aria-controls="header-searchbar-collapsible header-options-collapsible header-usermenu-collapsible"
aria-expanded="false"
class="d-flex flex-row align-items-center ps-3 pe-1"
>
<span class="input-group-text">
<i class="fa-solid fa-magnifying-glass"></i>
</span>
<input
ref="input"
@keyup="search"
@focus="showsearchresult"
v-model="searchsettings.searchstr"
class="form-control searchbar_input"
type="search"
:placeholder="$p.t('search/input_search_label', { types: searchTypesPlaceholder })"
:aria-label="$p.t('search/input_search_label', { types: searchTypesPlaceholder })"
>
<button
v-if="searchsettings.searchstr"
type="button"
class="searchbar_input_clear btn btn-outline-secondary"
@click="clearInput"
@focusin.stop
>
<i class="fas fa-close"></i>
</button>
<button
v-if="showBtnSubmit"
type="submit"
class="btn btn-primary"
:title="$p.t('search/submit')"
:aria-label="$p.t('search/submit')"
>
<i class="fas fa-search"></i>
</button>
<button
data-bs-toggle="collapse"
data-bs-target="#searchSettings"
aria-expanded="false"
aria-controls="searchSettings"
ref="settingsbutton"
class="searchbar_setting_btn btn btn-secondary"
type="button"
:title="$p.t('search/button_filter_label')"
:aria-label="$p.t('search/button_filter_label')"
>
<i class="fas fa-cog"></i>
</button>
</div>
<i v-if="isSearchShownInMobileView" class="fa-solid fa-chevron-left"></i>
<i v-else class="fa-solid fa-magnifying-glass"></i>
</span>
<div v-show="showresult"
class="searchbar_results" tabindex="-1">
<div class="searchbar_results_scroller" ref="result">
<div class="searchbar_results_wrapper" ref="results">
<div v-if="searching">
<i class="fas fa-spinner fa-spin fa-2x"></i>
</div>
<div v-else-if="this.error !== null">{{ error }}</div>
<div v-else-if="searchresult.length < 1">{{ $p.t('search/error_no_results') }}</div>
<template v-else v-for="res in searchresult">
<component
v-if="isValidRenderer(res.renderer)"
:is="res.renderer"
:mode="searchmode"
:res="res"
:actions="getActions(res)"
@actionexecuted="hideresult"
></component>
<div v-else class="searchbar-result text-danger fw-bold">{{ $p.t('search/error_unknown_type', res) }}</div>
</template>
</div>
</div>
</div>
<div
:class="{'flex-grow-1': !isMobile, 'collapse multi-collapse collapse-horizontal': isMobile}"
id="header-searchbar-collapsible"
@[\`show.bs.collapse\`]="isSearchShownInMobileView = true"
@[\`hidden.bs.collapse\`]="isSearchShownInMobileView = false"
>
<div
:class="{open: showresult, closed: showresult, 'px-3': isMobile}"
ref="searchbox"
class="h-100 input-group me-2 searchbar_searchbox"
:style="isMobile ? 'width: ' + getMaxWidthOfSearchbarInMobileView() : ''"
>
<span class="input-group-text">
<i class="fa-solid fa-magnifying-glass color-white"></i>
</span>
<input
ref="input"
@keyup="search"
@focus="showsearchresult"
v-model="searchsettings.searchstr"
class="form-control searchbar_input"
type="search"
:placeholder="$p.t('search/input_search_label', { types: searchTypesPlaceholder })"
:aria-label="$p.t('search/input_search_label', { types: searchTypesPlaceholder })"
>
<button
v-if="showBtnSubmit"
type="submit"
class="btn btn-primary"
:title="$p.t('search/submit')"
:aria-label="$p.t('search/submit')"
>
<i class="fas fa-search"></i>
</button>
<button
data-bs-toggle="collapse"
data-bs-target="#searchSettings"
aria-expanded="false"
aria-controls="searchSettings"
ref="settingsbutton"
class="searchbar_setting_btn btn btn-secondary"
type="button"
:title="$p.t('search/button_filter_label')"
:aria-label="$p.t('search/button_filter_label')"
>
<i class="fas fa-filter"></i>
</button>
</div>
<div v-show="showresult"
class="searchbar_results" tabindex="-1">
<div class="searchbar_results_scroller" ref="result">
<div class="searchbar_results_wrapper" ref="results">
<div v-if="searching">
<i class="fas fa-spinner fa-spin fa-2x"></i>
</div>
<div v-else-if="this.error !== null">{{ error }}</div>
<div v-else-if="searchresult.length < 1">{{ $p.t('search/error_no_results') }}</div>
<template v-else v-for="res in searchresult">
<component
v-if="isValidRenderer(res.renderer)"
:is="res.renderer"
:mode="searchmode"
:res="res"
:actions="getActions(res)"
@actionexecuted="hideresult"
></component>
<div v-else class="searchbar-result text-danger fw-bold">{{ $p.t('search/error_unknown_type', res) }}</div>
</template>
</div>
</div>
</div>
</div>
<div
id="searchSettings"
@@ -207,34 +220,42 @@ export default {
</div>
</form>
`,
watch:{
'searchsettings.searchstr': function (newSearchValue) {
if(this.searchoptions.origin){
sessionStorage.setItem(`${this.searchoptions.origin}_searchstr`,newSearchValue);
watch: {
"searchsettings.searchstr": function (newSearchValue) {
if (this.searchoptions.origin) {
sessionStorage.setItem(
`${this.searchoptions.origin}_searchstr`,
newSearchValue,
);
}
},
'searchsettings.types'(newValue) {
"searchsettings.types"(newValue) {
if (Array.isArray(newValue) && newValue.length === 0) {
this.searchsettings.types = [...this.types];
}
// stores the search types in the localstorage, only if the newValue is also an array
if (Array.isArray(newValue) && this.searchoptions.origin) {
localStorage.setItem(`${this.searchoptions.origin}_searchtypes`, JSON.stringify(newValue));
localStorage.setItem(
`${this.searchoptions.origin}_searchtypes`,
JSON.stringify(newValue),
);
}
this.search();
}
},
mounted(){
},
},
mounted() {
this.settingsDropdown = new bootstrap.Collapse(this.$refs.settings, {
toggle: false
toggle: false,
});
if (!this.searchoptions.origin){
console.warn("No origin defined in the searchoptions for the searchbar, please define the origin property in the searchbaroptions to allow reliable storage of searchstr and searchtypes accross applications.");
if (!this.searchoptions.origin) {
console.warn(
"No origin defined in the searchoptions for the searchbar, please define the origin property in the searchbaroptions to allow reliable storage of searchstr and searchtypes accross applications.",
);
}
},
updated() {
if(this.showresult) {
if (this.showresult) {
Vue.nextTick(() => {
this.calcSearchResultHeight();
});
@@ -249,32 +270,34 @@ export default {
getInitiallySelectedTypes() {
let result = false;
if (this.searchoptions.origin) {
let localStorageValue = localStorage.getItem(`${this.searchoptions.origin}_searchtypes`);
let localStorageValue = localStorage.getItem(
`${this.searchoptions.origin}_searchtypes`,
);
if (localStorageValue) {
result = JSON.parse(localStorageValue);
}
}
if (result)
return result;
if (!this.searchoptions.types)
return [];
if (result) return result;
if (!this.searchoptions.types) return [];
if (Array.isArray(this.searchoptions.types))
return [...this.searchoptions.types];
return Object.keys(this.searchoptions.types);
},
getSearchStr: function(){
if (!this.searchoptions.origin)
return '';
return sessionStorage.getItem(`${this.searchoptions.origin}_searchstr`) ?? '';
getSearchStr: function () {
if (!this.searchoptions.origin) return "";
return (
sessionStorage.getItem(
`${this.searchoptions.origin}_searchstr`,
) ?? ""
);
},
checkSettingsVisibility: function(event) {
checkSettingsVisibility: function (event) {
// hides the settings collapsible if the user clicks somewhere else
if (!this.$refs.settings.contains(event.target))
{
if (!this.$refs.settings.contains(event.target)) {
this.settingsDropdown.hide();
}
},
handleShowSettings: function() {
handleShowSettings: function () {
// adds the event listener checkSettingsVisibility only when the collapsible is shown
document.addEventListener("click", this.checkSettingsVisibility);
},
@@ -282,183 +305,208 @@ export default {
// removes the event listener checkSettingsVisibility when the collapsible is hidden
document.removeEventListener("click", this.checkSettingsVisibility);
},
calcSearchResultHeight: function() {
calcSearchResultHeight: function () {
const rect = this.$refs.results.getBoundingClientRect();
if( rect.height > 0 && rect.height < (window.innerHeight * 0.8) ) {
this.$refs.result.style.height = Math.ceil(rect.height + 16) + 'px';
if (rect.height > 0 && rect.height < window.innerHeight * 0.8) {
this.$refs.result.style.height =
Math.ceil(rect.height + 16) + "px";
} else {
this.$refs.result.style.height = Math.floor(window.innerHeight * 0.8) + 'px';
this.$refs.result.style.height =
Math.floor(window.innerHeight * 0.8) + "px";
}
},
calcSearchResultExtent: function() {
if(!this.showresult) {
calcSearchResultExtent: function () {
if (!this.showresult) {
return;
}
if(this.searchoptions?.calcheightonly === undefined
|| this.searchoptions.calcheightonly === false) {
if (
this.searchoptions?.calcheightonly === undefined ||
this.searchoptions.calcheightonly === false
) {
var rect = this.$refs.searchbox.getBoundingClientRect();
this.$refs.result.style.top = Math.floor(rect.bottom + 3) + 'px';
this.$refs.result.style.right = Math.floor(rect.right) + 'px';
this.$refs.result.style.width = Math.floor(rect.width) + 'px';
this.$refs.result.style.top =
Math.floor(rect.bottom + 3) + "px";
this.$refs.result.style.right = Math.floor(rect.right) + "px";
this.$refs.result.style.width = Math.floor(rect.width) + "px";
}
this.calcSearchResultHeight();
},
search: function() {
if(this.searchoptions?.nolivesearch === true) return;
this.calcSearchResultHeight();
},
search: function () {
if (this.searchoptions?.nolivesearch === true) return;
this.abort();
if( this.searchsettings.searchstr.length >= 2 ) {
this.calcSearchResultExtent();
this.searchtimer = setTimeout(
this.callsearchapi,
500
);
} else {
this.showresult = false;
}
},
abort() {
if (this.searchtimer !== null) {
clearTimeout(this.searchtimer);
}
if (this.abortController) {
this.abortController.abort();
this.abortController = null;
}
this.searchresult = [];
},
callsearchapi: function() {
this.error = null;
this.searchresult.splice(0, this.searchresult.length);
this.searching = true;
this.showsearchresult();
if(this.searchsettings.types.length === 0) {
this.error = this.$p.t('search/error_missing_type');
this.searching = false;
return;
}
if (this.abortController)
this.abortController.abort();
this.abortController = new AbortController();
this.searchfunction(this.searchsettings, { timeout: 50000, signal: this.abortController.signal })
.then(response=>{
if (!response.data) {
this.error = this.$p.t('search/error_general');
} else {
let res = response.data.map(el => el.data ? {...el, ...JSON.parse(el.data)} : el);
this.lastQuery = response.meta.searchstring;
if (this.searchoptions.mergeResults) {
let counter = 0;
let mergeTypes = [];
let mergedType = 'merged-';
let mergeKey = '';
switch (this.searchoptions.mergeResults) {
case 'student':
mergeTypes = ['student', 'prestudent'];
mergedType += this.searchoptions.mergeResults;
mergeKey = 'uid';
break;
case 'person':
mergeTypes = ['person', 'employee', 'student', 'prestudent'];
mergedType += this.searchoptions.mergeResults;
mergeKey = 'person_id';
break;
}
if (mergeTypes.length) {
res = Object.values(res.reduce((a, c) => {
if (!mergeTypes.includes(c.renderer)) {
a['nomerge' + counter++] = c;
} else if (c[mergeKey] === null) {
a['nomerge' + counter++] = c;
} else if (a[c[mergeKey]] === undefined) {
a[c[mergeKey]] = {
rank: c.rank,
renderer: mergedType,
type: mergedType,
list: [c]
};
} else {
a[c[mergeKey]].list.push(c);
if (c.rank > a[c[mergeKey]].rank)
a[c[mergeKey]].rank = c.rank;
}
return a;
}, {})).sort((a, b) => b.rank - a.rank);
}
}
this.searchresult = res;
this.searchmode = response.meta.mode;
}
this.searching = false;
this.retry = 0;
})
.catch(error=> {
if (error.code == "ERR_CANCELED") {
return this.retry = 0;
}
if (error.code == "ECONNABORTED" && this.retry) {
this.retry--;
return this.callsearchapi();
}
this.error = this.$p.t('search/error_general', error);
this.searching = false;
this.retry = 0;
});
},
refreshsearch: function() {
this.search();
this.togglesettings();
},
hideresult: function() {
this.showresult = false;
window.removeEventListener('resize', this.calcSearchResultExtent);
},
showsearchresult: function() {
if(this.searchoptions?.nolivesearch === true) return;
if( this.searchsettings.searchstr.length >= 2 ) {
this.showresult = true;
window.addEventListener('resize', this.calcSearchResultExtent);
this.abort();
if (this.searchsettings.searchstr.length >= 2) {
this.calcSearchResultExtent();
}
},
searchfocusin: function(e) {
e.preventDefault();
e.stopPropagation();
if( this.hidetimer !== null ) {
clearTimeout(this.hidetimer);
}
if (this.searchsettings.searchstr.length >= 2
&& this.searchresult.length === 0) {
this.searchtimer = setTimeout(this.callsearchapi, 500);
} else {
this.showresult = false;
}
},
abort() {
if (this.searchtimer !== null) {
clearTimeout(this.searchtimer);
}
if (this.abortController) {
this.abortController.abort();
this.abortController = null;
}
this.searchresult = [];
},
callsearchapi: function () {
this.error = null;
this.searchresult.splice(0, this.searchresult.length);
this.searching = true;
this.showsearchresult();
if (this.searchsettings.types.length === 0) {
this.error = this.$p.t("search/error_missing_type");
this.searching = false;
return;
}
if (this.abortController) this.abortController.abort();
this.abortController = new AbortController();
this.searchfunction(this.searchsettings, {
timeout: 50000,
signal: this.abortController.signal,
})
.then((response) => {
if (!response.data) {
this.error = this.$p.t("search/error_general");
} else {
let res = response.data.map((el) =>
el.data ? { ...el, ...JSON.parse(el.data) } : el,
);
this.lastQuery = response.meta.searchstring;
if (this.searchoptions.mergeResults) {
let counter = 0;
let mergeTypes = [];
let mergedType = "merged-";
let mergeKey = "";
switch (this.searchoptions.mergeResults) {
case "student":
mergeTypes = ["student", "prestudent"];
mergedType +=
this.searchoptions.mergeResults;
mergeKey = "uid";
break;
case "person":
mergeTypes = [
"person",
"employee",
"student",
"prestudent",
];
mergedType +=
this.searchoptions.mergeResults;
mergeKey = "person_id";
break;
}
if (mergeTypes.length) {
res = Object.values(
res.reduce((a, c) => {
if (!mergeTypes.includes(c.renderer)) {
a["nomerge" + counter++] = c;
} else if (c[mergeKey] === null) {
a["nomerge" + counter++] = c;
} else if (
a[c[mergeKey]] === undefined
) {
a[c[mergeKey]] = {
rank: c.rank,
renderer: mergedType,
type: mergedType,
list: [c],
};
} else {
a[c[mergeKey]].list.push(c);
if (c.rank > a[c[mergeKey]].rank)
a[c[mergeKey]].rank = c.rank;
}
return a;
}, {}),
).sort((a, b) => b.rank - a.rank);
}
}
this.searchresult = res;
this.searchmode = response.meta.mode;
}
this.searching = false;
this.retry = 0;
})
.catch((error) => {
if (error.code == "ERR_CANCELED") {
return (this.retry = 0);
}
if (error.code == "ECONNABORTED" && this.retry) {
this.retry--;
return this.callsearchapi();
}
this.error = this.$p.t("search/error_general", error);
this.searching = false;
this.retry = 0;
});
},
refreshsearch: function () {
this.search();
this.togglesettings();
},
hideresult: function () {
this.showresult = false;
window.removeEventListener("resize", this.calcSearchResultExtent);
},
showsearchresult: function () {
if (this.searchoptions?.nolivesearch === true) return;
if (this.searchsettings.searchstr.length >= 2) {
this.showresult = true;
window.addEventListener("resize", this.calcSearchResultExtent);
this.calcSearchResultExtent();
}
},
searchfocusin: function (e) {
e.preventDefault();
e.stopPropagation();
if (this.hidetimer !== null) {
clearTimeout(this.hidetimer);
}
if (
this.searchsettings.searchstr.length >= 2 &&
this.searchresult.length === 0
) {
this.search();
}
},
searchfocusout: function(e) {
e.preventDefault();
e.stopPropagation();
this.hidetimer = setTimeout(
this.hideresult,
100
);
},
dash2camelCase(string) {
return string.replace(/-([a-z])/g, g => g[1].toUpperCase());
},
isValidRenderer(renderer) {
const camelCaseRenderer = this.dash2camelCase(renderer);
return Object.keys(this.$.components).includes(camelCaseRenderer);
},
},
searchfocusout: function (e) {
e.preventDefault();
e.stopPropagation();
this.hidetimer = setTimeout(this.hideresult, 100);
},
dash2camelCase(string) {
return string.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
},
isValidRenderer(renderer) {
const camelCaseRenderer = this.dash2camelCase(renderer);
return Object.keys(this.$.components).includes(camelCaseRenderer);
},
getActions(res) {
let actions = this.searchoptions.actions[this.dash2camelCase(res.renderer)];
let actions =
this.searchoptions.actions[this.dash2camelCase(res.renderer)];
if (actions) {
return actions;
}
return this.searchoptions.actions[res.type];
}
}
},
getMaxWidthOfSearchbarInMobileView() {
// body width - hardcoded chevron width; necessary for accurate collapse transition transition
return (
document.querySelector("body").getBoundingClientRect().width -
27 +
"px"
);
},
},
};
+14 -3
View File
@@ -111,7 +111,7 @@ export function useEventLoader(rangeInterval, getPromiseFunc) {
return mergePromiseArr(getPromiseFunc(start, end), result);
};
Vue.watchEffect(() => {
const reload = () => {
const range = Vue.toValue(rangeInterval);
if (!(range instanceof luxon.Interval))
return;
@@ -132,7 +132,18 @@ export function useEventLoader(rangeInterval, getPromiseFunc) {
}
})
});
})
};
return { events: allEvents, lv }
Vue.watchEffect(reload);
const reset = () => {
loading_id = 0;
events.value = [];
loadingEvents.value = [];
eventsLoaded.splice(0, eventsLoaded.length);
reload();
}
return { events: allEvents, lv, reset }
}
+2
View File
@@ -58,6 +58,8 @@ class GridLogic {
item.frame.forEach(f => intermGrid.grid[f] = -1);
intermGrid.data.forEach(currItem => {
if (!currItem)
return;
if (currItem.pinned) {
if (!currItem.frame)
currItem.frame = intermGrid.getItemFrame(currItem);
@@ -0,0 +1,78 @@
import ApiBookmark from '../../../api/factory/widget/bookmark.js';
const bookmarksRef = Vue.ref([]);
const tags = Vue.computed(() => {
return bookmarksRef.value
.map(bookmark => JSON.parse(bookmark.tag))
.flat()
.filter((v, i, a) => v && a.indexOf(v) === i);
});
const state = Vue.readonly(bookmarksRef);
export function useUrlStore() {
const $api = Vue.inject('$api');
const $fhcAlert = Vue.inject('$fhcAlert');
async function fetch() {
try {
bookmarksRef.value = (await $api.call(ApiBookmark.getBookmarks())).data;
} catch(error) {
$fhcAlert.handleSystemError(error);
return error;
}
}
async function insert(title, url, tag, sort) {
try {
await $api.call(ApiBookmark.insert({ title, url, tag, sort }));
await fetch();
} catch(error) {
$fhcAlert.handleSystemError(error);
return error;
}
}
async function update(bookmark_id, title, url, tag) {
try {
await $api.call(ApiBookmark.update({ bookmark_id, title, url, tag }));
await fetch();
} catch(error) {
$fhcAlert.handleSystemError(error);
return error;
}
}
async function swap(bookmark_id_1, bookmark_id_2) {
try {
await $api.call(ApiBookmark.changeOrder(bookmark_id_1, bookmark_id_2));
await fetch();
} catch(error) {
$fhcAlert.handleSystemError(error);
return error;
}
}
async function remove(bookmark_id) {
try {
await $api.call(ApiBookmark.delete(bookmark_id));
await fetch();
} catch(error) {
$fhcAlert.handleSystemError(error);
return error;
}
}
return {
bookmarks: state,
getters: {
tags
},
actions: {
fetch,
insert,
update,
swap,
remove
}
};
}
+3 -3
View File
@@ -450,18 +450,18 @@ td.MarkLine
td.HeaderTesttool /*fuer die Button-Optik beim Testtool*/
{
color: #FFFFFF;
background-color: #00639C;
background-color: #71787D;
white-space:nowrap;
line-height: 25px;
box-shadow: inset 0 0 2px #FFFFFF;
padding: 10px;
padding: 0 10px;
width: 170px;
}
td.HeaderTesttoolSTG /*fuer die Button-Optik der Quereinstiegs-Studiengänge beim Testtool*/
{
color: white;
border: 2px solid #73a9d6;
padding: 10px;
padding: 0 10px;
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
+3 -1
View File
@@ -94,6 +94,8 @@ 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/71566_studienordnungsdokument_neuer_organisationseinheitstyp_programm.php');
require_once('dbupdate_3.4/70376_lohnguide.php');
require_once('dbupdate_3.4/76150_perm_other_lv_plan.php');
require_once('dbupdate_3.4/68957_dashboard_bookmark_neue_Spalte_sort.php');
require_once('dbupdate_3.4/68530_Dashboard_Cleanup.php');
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
@@ -468,7 +470,7 @@ $tabellen=array(
"wawi.tbl_rechnungsbetrag" => array("rechnungsbetrag_id","rechnung_id","mwst","betrag","bezeichnung","ext_id"),
"wawi.tbl_aufteilung" => array("aufteilung_id","bestellung_id","oe_kurzbz","anteil","insertamum","insertvon","updateamum","updatevon"),
"wawi.tbl_aufteilung_default" => array("aufteilung_id","kostenstelle_id","oe_kurzbz","anteil","insertamum","insertvon","updateamum","updatevon"),
"dashboard.tbl_bookmark" => array("bookmark_id","uid","url","title","tag","insertamum","insertvon","updateamum","updatevon"),
"dashboard.tbl_bookmark" => array("bookmark_id","uid","url","title","tag","insertamum","insertvon","updateamum","updatevon","sort"),
);
@@ -0,0 +1,57 @@
<?php
if (! defined('DB_NAME')) exit('No direct script access allowed');
//Add column sort to dashboard.tbl_bookmark
if(!@$db->db_query("SELECT sort FROM dashboard.tbl_bookmark LIMIT 1")) {
$qry = "ALTER TABLE dashboard.tbl_bookmark ADD COLUMN sort integer DEFAULT NULL;
COMMENT ON COLUMN dashboard.tbl_bookmark.sort IS 'Sort Index for Bookmark.';
";
if (!$db->db_query($qry))
echo '<strong>dashboard.tbl_bookmark ' . $db->db_last_error() . '</strong><br>';
else
echo '<br>Spalte sort zu Tabelle dashboard.tbl_bookmark hinzugefügt';
//add preliminary Sort for all bookmarks if NULL
if(@$db->db_query("SELECT sort FROM dashboard.tbl_bookmark LIMIT 1")) {
$qry = "WITH ranked AS (
SELECT
t1.bookmark_id,
(
SELECT COUNT(*)
FROM dashboard.tbl_bookmark t2
WHERE t2.uid = t1.uid
AND t2.bookmark_id <= t1.bookmark_id
) AS rn
FROM dashboard.tbl_bookmark t1
)
UPDATE dashboard.tbl_bookmark t
SET sort = ranked.rn
FROM ranked
WHERE t.bookmark_id = ranked.bookmark_id
AND t.sort IS NULL;";
if (!$db->db_query($qry))
echo '<strong>dashboard.tbl_bookmark ' . $db->db_last_error() . '</strong><br>';
else
echo '<br>Tabelle dashboard.tbl_bookmark: Spalte sort mit vorläufiger Sortierung befüllt';
}
}
//set column tag to type JSONB
if($result = @$db->db_query("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='dashboard'
AND TABLE_NAME='tbl_bookmark' AND COLUMN_NAME = 'tag'
AND DATA_TYPE='character varying' AND character_maximum_length='255';"))
{
$qry = "
ALTER TABLE dashboard.tbl_bookmark
ALTER COLUMN tag TYPE jsonb
USING tag::jsonb;
";
if (!$db->db_query($qry))
echo '<strong>dashboard.tbl_bookmark ' . $db->db_last_error() . '</strong><br>';
else
echo '<br>Tabelle dashboard.tbl_bookmark: Spalte tag auf Typ JSONB geändert';
}
+116 -166
View File
@@ -22,11 +22,11 @@ GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE hr.tbl_lohnguide_jobfamilie TO vilesc
INSERT INTO hr.tbl_lohnguide_jobfamilie(jobfamilie_kurzbz, bezeichnung,aktiv, sort, insertvon, insertamum) VALUES
('FÜHRUNG','Führung',true,1,'system',NOW()),
('ALLGEMEIN','Allgemein',true,2,'system',NOW()),
('TECHNIK','Technik',true,3,'system',NOW()),
('IT','IT',true,4,'system',NOW()),
('PRODUKTION','Produktion',true,5,'system',NOW()),
('HANDW_IH_LOG','Handwerk, Instandhaltung + Logistik',true,6,'system',NOW())
('AKADEMIA','Akademia',true,2,'system',NOW()),
('VERWALTUNG','Verwaltung',true,3,'system',NOW()),
('TECHNIK','Technik',true,4,'system',NOW()),
('IT_SOFTWARE','IT & Software',true,5,'system',NOW()),
('TECHN_DIENSTE','Technische Dienste',true,6,'system',NOW())
ON CONFLICT (jobfamilie_kurzbz) DO NOTHING;
";
@@ -59,38 +59,33 @@ CREATE TABLE IF NOT EXISTS hr.tbl_lohnguide_modellfunktion (
GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE hr.tbl_lohnguide_modellfunktion TO vilesci;
INSERT INTO hr.tbl_lohnguide_modellfunktion(modellfunktion_kurzbz, bezeichnung, jobfamilie_kurzbz, aktiv, sort, insertvon, insertamum) VALUES
('FÜHRUNG_I','Führung I','FÜHRUNG',true,1,'system',NOW()),
('FÜHRUNG_II','Führung I','FÜHRUNG',true,2,'system',NOW()),
('FÜHRUNG_III','Führung I','FÜHRUNG',true,3,'system',NOW()),
('GF','Geschäftsführung','FÜHRUNG',true,4,'system',NOW()),
/* Allgemein */
('FK_ALLGM','Fachkraft Allgemein','ALLGEMEIN',true,5,'system',NOW()),
('SFK_ALLGM','Spezial-Fachkraft Allgemein','ALLGEMEIN',true,6,'system',NOW()),
('SP_ALLGM','Spezialist:in Allgemein','ALLGEMEIN',true,7,'system',NOW()),
('EXP_ALLGM','Expert:in Allgemein','ALLGEMEIN',true,8,'system',NOW()),
('TOP_EXP_ALLGM','Top-Expert:in Allgemein','ALLGEMEIN',true,9,'system',NOW()),
('ABTEILUNGSLEITUNG','Abteilungsleitung','FÜHRUNG',true,1,'system',NOW()),
('GF','Geschäftsführung','FÜHRUNG',true,2,'system',NOW()),
('KOMPETENZFELDLEITER','Kompetenzfeldleiter*in','FÜHRUNG',true,3,'system',NOW()),
('DEPARTMENTSLEITER','Departmentsleiter*in','FÜHRUNG',true,4,'system',NOW()),
('FAKULTÄTSLEITER','Fakultätsleiter*in','FÜHRUNG',true,5,'system',NOW()),
/* Akademia */
('STUDENTISCHE_MA','Studentische MA','AKADEMIA',true,6,'system',NOW()),
('JUNIOR_LEC_RES','Junior Lecturer/Researcher','AKADEMIA',true,7,'system',NOW()),
('LEC_RES','Lecturer/Researcher','AKADEMIA',true,8,'system',NOW()),
('SEN_LEC_RES','Senior Lecturer/Researcher','AKADEMIA',true,9,'system',NOW()),
('STUDIENGANGSLEITUNG','Studiengangsleitung','AKADEMIA',true,10,'system',NOW()),
/* Verwaltung */
('FK_VERWALTUNG','Fachkraft Verwaltung','VERWALTUNG',true,11,'system',NOW()),
('SFK_VERWALTUNG','Spezial-Fachkraft Verwaltung','VERWALTUNG',true,12,'system',NOW()),
('SP_VERWALTUNG','Spezialist:in Verwaltung','VERWALTUNG',true,13,'system',NOW()),
('EXP_VERWALTUNG','Expert:in Verwaltung','VERWALTUNG',true,14,'system',NOW()),
/* Technik */
('FK_TECH','Fachkraft Technik','TECHNIK',true,10,'system',NOW()),
('SFK_TECH','Spezial-Fachkraft Technik','TECHNIK',true,11,'system',NOW()),
('SP_TECH','Spezialist:in Technik','TECHNIK',true,12,'system',NOW()),
('EXP_TECH','Expert:in Technik','TECHNIK',true,13,'system',NOW()),
('TOP_EXP_TECH','Top-Expert:in Technik','TECHNIK',true,14,'system',NOW()),
/* IT */
('FK_IT','Fachkraft IT','IT',true,15,'system',NOW()),
('SFK_IT','Spezial-Fachkraft IT','IT',true,16,'system',NOW()),
('SP_IT','Spezialist:in IT','IT',true,17,'system',NOW()),
('EXP_IT','Expert:in IT','IT',true,18,'system',NOW()),
('TOP_EXP_IT','Top-Expert:in IT','IT',true,19,'system',NOW()),
/* Produktion */
('HK_PROD','Hilfskraft Produktion','PRODUKTION',true,20,'system',NOW()),
('FK_PROD','Fachkraft Produktion','PRODUKTION',true,21,'system',NOW()),
('SFK_PROD','Spezial-Fachkraft Produktion','PRODUKTION',true,22,'system',NOW()),
('SP_PROD','Spezialist:in Produktion','PRODUKTION',true,23,'system',NOW()),
/* Handwerk, Instandhaltung, Logistik */
('HK_HIL','Hilfskraft Handwerk, Instandhaltung + Logistik','HANDW_IH_LOG',true,24,'system',NOW()),
('FK_HIL','Fachkraft Handwerk, Instandhaltung + Logistik','HANDW_IH_LOG',true,25,'system',NOW()),
('SFK_HIL','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik','HANDW_IH_LOG',true,26,'system',NOW()),
('SP_HIL','Spezialist:in Handwerk, Instandhaltung + Logistik','HANDW_IH_LOG',true,27,'system',NOW())
('FK_TECHNIK','Fachkraft Technik','TECHNIK',true,15,'system',NOW()),
/* IT & Software */
('FK_IT','Fachkraft IT & Software','IT_SOFTWARE',true,16,'system',NOW()),
('SFK_IT','Spezial-Fachkraft IT & Software','IT_SOFTWARE',true,17,'system',NOW()),
('SP_IT','Spezialist:in IT & Software','IT_SOFTWARE',true,18,'system',NOW()),
('EXP_IT','Expert:in IT & Software','IT_SOFTWARE',true,19,'system',NOW()),
/* Technische Dienste */
('HK_TECHN_DIENSTE','Hilfskraft Technische Dienste','TECHN_DIENSTE',true,20,'system',NOW()),
('FK_TECHN_DIENSTE','Fachkraft Technische Dienste','TECHN_DIENSTE',true,21,'system',NOW()),
('SFK_TECHN_DIENSTE','Spezial-Fachkraft Technische Dienste','TECHN_DIENSTE',true,22,'system',NOW())
ON CONFLICT (modellfunktion_kurzbz) DO NOTHING;
@@ -111,6 +106,7 @@ if ($result = $db->db_query("SELECT * FROM information_schema.tables WHERE table
CREATE TABLE IF NOT EXISTS hr.tbl_lohnguide_modellstelle (
modellstelle_kurzbz character varying(32) NOT NULL,
bezeichnung varchar(128) NOT NULL,
code character varying(32) NOT NULL,
grade int NOT NULL,
modellfunktion_kurzbz character varying(32) NOT NULL,
aktiv boolean DEFAULT FALSE,
@@ -125,132 +121,94 @@ CREATE TABLE IF NOT EXISTS hr.tbl_lohnguide_modellstelle (
GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE hr.tbl_lohnguide_modellstelle TO vilesci;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('111','Führung III 1/5',9,'FÜHRUNG_III',true,13,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('112','Führung III 2/5',10,'FÜHRUNG_III',true,14,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('113','Führung III 3/5',11,'FÜHRUNG_III',true,15,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('114','Führung III 4/5',12,'FÜHRUNG_III',true,16,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('115','Führung III 5/5',13,'FÜHRUNG_III',true,17,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('121','Führung II 1/4',14,'FÜHRUNG_II',true,7,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('122a','Führung II 2a/4',15,'FÜHRUNG_II',true,8,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('122b','Führung II 2b/4',15,'FÜHRUNG_II',true,9,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('123a','Führung II 3a/4',16,'FÜHRUNG_II',true,10,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('123b','Führung II 3b/4',16,'FÜHRUNG_II',true,11,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('124','Führung II 4/4',17,'FÜHRUNG_II',true,12,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('131','Führung I 1/4',18,'FÜHRUNG_I',true,1,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('132a','Führung I 2a/4',19,'FÜHRUNG_I',true,2,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('132b','Führung I 2b/4',19,'FÜHRUNG_I',true,3,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('133a','Führung I 3a/4',20,'FÜHRUNG_I',true,4,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('133b','Führung I 3b/4',20,'FÜHRUNG_I',true,5,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('134','Führung I 4/4',21,'FÜHRUNG_I',true,6,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- GF
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('141','Geschäftsführung 1/5',22,'GF',true,18,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('142a','Geschäftsführung 2a/5',23,'GF',true,19,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('142b','Geschäftsführung 2b/5',23,'GF',true,20,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('143a','Geschäftsführung 3a/5',24,'GF',true,21,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('143b','Geschäftsführung 3b/5',24,'GF',true,22,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('144a','Geschäftsführung 4a/5',25,'GF',true,23,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('144b','Geschäftsführung 4b/5',25,'GF',true,24,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('145','Geschäftsführung 5/5',26,'GF',true,25,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- FÜHRUNG
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz,bezeichnung, code, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES
('ABTL_1_4', 'Abteilungsleitung 1/4', '111', 16, 'ABTEILUNGSLEITUNG', true, 1, 'system', NOW()),
('ABTL_2A_4', 'Abteilungsleitung 2a/4', '112a', 17, 'ABTEILUNGSLEITUNG', true, 2, 'system', NOW()),
('ABTL_2B_4', 'Abteilungsleitung 2b/4', '112b', 17, 'ABTEILUNGSLEITUNG', true, 3, 'system', NOW()),
('ABTL_3A_4', 'Abteilungsleitung 3a/4', '113a', 18, 'ABTEILUNGSLEITUNG', true, 4, 'system', NOW()),
('ABTL_3B_4', 'Abteilungsleitung 3b/4', '113b', 18, 'ABTEILUNGSLEITUNG', true, 5, 'system', NOW()),
('ABTL_4_4', 'Abteilungsleitung 4/4', '114', 19, 'ABTEILUNGSLEITUNG', true, 6, 'system', NOW()),
('GF_1_2', 'Geschäftsführung 1/2', '121', 22, 'GF', true, 7, 'system', NOW()),
('GF_2_2', 'Geschäftsführung 2/2', '122', 23, 'GF', true, 8, 'system', NOW()),
('KOMFL_1_1', 'Kompetenzfeldleiter*in 1/1', '131', 15, 'KOMPETENZFELDLEITER', true, 9, 'system', NOW()),
('DEPL_1_1', 'Departmentleiter*in 1/1', '141', 18, 'DEPARTMENTSLEITER', true, 10, 'system', NOW()),
('FAKL_1_1', 'Fakultätsleiter*in 1/1', '151', 20, 'FAKULTÄTSLEITER', true, 11, 'system', NOW())
ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- Allgemein
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('211','Fachkraft Allgemein 1/3',4,'FK_ALLGM',true,26,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('212a','Fachkraft Allgemein 2a/3',5,'FK_ALLGM',true,27,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('212b','Fachkraft Allgemein 2b/3',5,'FK_ALLGM',true,28,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('213','Fachkraft Allgemein 3/3',6,'FK_ALLGM',true,29,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('221','Spezial-Fachkraft Allgemein 1/4', 7,'SFK_ALLGM',true,30,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('222a','Spezial-Fachkraft Allgemein 2a/4',8,'SFK_ALLGM',true,31,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('222b','Spezial-Fachkraft Allgemein 2b/4',8,'SFK_ALLGM',true,32,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('223a','Spezial-Fachkraft Allgemein 3a/4',9,'SFK_ALLGM',true,33,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('223b','Spezial-Fachkraft Allgemein 3b/4',9,'SFK_ALLGM',true,34,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('224','Spezial-Fachkraft Allgemein 4/4',10,'SFK_ALLGM',true,35,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('231','Spezialist:in Allgemein 1/4',11,'SP_ALLGM',true,36,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('232a','Spezialist:in Allgemein 2a/4',12,'SP_ALLGM',true,37,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('232b','Spezialist:in Allgemein 2b/4',12,'SP_ALLGM',true,38,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('233a','Spezialist:in Allgemein 3a/4',13,'SP_ALLGM',true,39,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('233b','Spezialist:in Allgemein 3b/4',13,'SP_ALLGM',true,40,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('234','Spezialist:in Allgemein 4/4',14,'SP_ALLGM',true,41,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('241','Expert:in Allgemein 1/4',15,'EXP_ALLGM',true,42,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('242a','Expert:in Allgemein 2a/4',16,'EXP_ALLGM',true,43,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('242b','Expert:in Allgemein 2b/4',16,'EXP_ALLGM',true,44,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('243a','Expert:in Allgemein 3a/4',17,'EXP_ALLGM',true,45,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('243b','Expert:in Allgemein 3b/4',17,'EXP_ALLGM',true,46,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('244','Expert:in Allgemein 4/4',18,'EXP_ALLGM',true,47,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('251','Top-Expert:in Allgemein 1/1',19,'TOP_EXP_ALLGM',true,48,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- AKADEMIA
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, code, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES
('STUDENTISCHE_MA_1_1', 'Studentische MA 1/1', '211', 5, 'STUDENTISCHE_MA', true, 12, 'system', NOW()),
('JUNIOR_LEC_RES_1_2', 'Junior Lecturer/Researcher 1/2', '221', 8, 'JUNIOR_LEC_RES', true, 13, 'system', NOW()),
('JUNIOR_LEC_RES_2_2', 'Junior Lecturer/Researcher 2/2', '222', 9, 'JUNIOR_LEC_RES', true, 14, 'system', NOW()),
('LEC_RES_1_2', 'Lecturer/Researcher 1/2', '231', 11, 'LEC_RES', true, 15, 'system', NOW()),
('LEC_RES_2_2', 'Lecturer/Researcher 2/2', '232', 12, 'LEC_RES', true, 16, 'system', NOW()),
('SEN_LEC_RES_1_2', 'Senior Lecturer/Researcher 1/2', '241', 13, 'SEN_LEC_RES', true, 17, 'system', NOW()),
('SEN_LEC_RES_2_2', 'Senior Lecturer/Researcher 2/2', '242', 14, 'SEN_LEC_RES', true, 18, 'system', NOW()),
('STGL_1_2', 'Studiengangsleitung 1/2', '251', 15, 'STUDIENGANGSLEITUNG', true, 19, 'system', NOW()),
('STGL_2_2', 'Studiengangsleitung 2/2', '252', 16, 'STUDIENGANGSLEITUNG', true, 20, 'system', NOW())
ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- Technik
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('311','Fachkraft Technik 1/3',4,'FK_TECH',true,49,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('312a','Fachkraft Technik 2a/3',5,'FK_TECH',true,50,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('312b','Fachkraft Technik 2b/3',5,'FK_TECH',true,51,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('313','Fachkraft Technik 3/3',6,'FK_TECH',true,52,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('321','Spezial-Fachkraft Technik 1/4',7,'SFK_TECH',true,53,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('322a','Spezial-Fachkraft Technik 2a/4',8,'SFK_TECH',true,54,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('322b','Spezial-Fachkraft Technik 2b/4',8,'SFK_TECH',true,55,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('323a','Spezial-Fachkraft Technik 3a/4',9,'SFK_TECH',true,56,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('323b','Spezial-Fachkraft Technik 3b/4',9,'SFK_TECH',true,57,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('324','Spezial-Fachkraft Technik 4/4',10,'SFK_TECH',true,58,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('331','Spezialist:in Technik 1/4',11,'SP_TECH',true,59,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('332a','Spezialist:in Technik 2a/4',12,'SP_TECH',true,60,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('332b','Spezialist:in Technik 2b/4',12,'SP_TECH',true,61,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('333a','Spezialist:in Technik 3a/4',13,'SP_TECH',true,62,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('333b','Spezialist:in Technik 3b/4',13,'SP_TECH',true,63,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('334','Spezialist:in Technik 4/4',14,'SP_TECH',true,64,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('341','Expert:in Technik 1/4',15,'EXP_TECH',true,65,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('342a','Expert:in Technik 2a/4',16,'EXP_TECH',true,66,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('342b','Expert:in Technik 2b/4',16,'EXP_TECH',true,67,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('343a','Expert:in Technik 3a/4',17,'EXP_TECH',true,68,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('343b','Expert:in Technik 3b/4',17,'EXP_TECH',true,69,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('344','Expert:in Technik 4/4',18,'EXP_TECH',true,70,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('351','Top-Expert:in Technik 1/1',19,'TOP_EXP_TECH',true,71,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- VERWALTUNG
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, code, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES
('FK_VERWALTUNG_1_3', 'Fachkraft Verwaltung 1/3', '311', 4, 'FK_VERWALTUNG', true, 21, 'system', NOW()),
('FK_VERWALTUNG_2A_3', 'Fachkraft Verwaltung 2a/3', '312a', 5, 'FK_VERWALTUNG', true, 22, 'system', NOW()),
('FK_VERWALTUNG_2B_3', 'Fachkraft Verwaltung 2b/3', '312b', 5, 'FK_VERWALTUNG', true, 23, 'system', NOW()),
('FK_VERWALTUNG_3_3', 'Fachkraft Verwaltung 3/3', '313', 6, 'FK_VERWALTUNG', true, 24, 'system', NOW()),
('SFK_VERWALTUNG_1_4', 'Spezial-Fachkraft Verwaltung 1/4', '321', 7, 'SFK_VERWALTUNG', true, 25, 'system', NOW()),
('SFK_VERWALTUNG_2A_4', 'Spezial-Fachkraft Verwaltung 2a/4', '322a', 8, 'SFK_VERWALTUNG', true, 26, 'system', NOW()),
('SFK_VERWALTUNG_2B_4', 'Spezial-Fachkraft Verwaltung 2b/4', '322b', 8, 'SFK_VERWALTUNG', true, 27, 'system', NOW()),
('SFK_VERWALTUNG_3A_4', 'Spezial-Fachkraft Verwaltung 3a/4', '323a', 9, 'SFK_VERWALTUNG', true, 28, 'system', NOW()),
('SFK_VERWALTUNG_3B_4', 'Spezial-Fachkraft Verwaltung 3b/4', '323b', 9, 'SFK_VERWALTUNG', true, 29, 'system', NOW()),
('SFK_VERWALTUNG_4_4', 'Spezial-Fachkraft Verwaltung 4/4', '324', 10, 'SFK_VERWALTUNG', true, 30, 'system', NOW()),
('SP_VERWATLTUNG_1_4', 'Spezialist:in Verwaltung 1/4', '331', 11, 'SP_VERWALTUNG', true, 31, 'system', NOW()),
('SP_VERWATLTUNG_2A_4', 'Spezialist:in Verwaltung 2a/4', '332a', 12, 'SP_VERWALTUNG', true, 32, 'system', NOW()),
('SP_VERWATLTUNG_2B_4', 'Spezialist:in Verwaltung 2b/4', '332b', 12, 'SP_VERWALTUNG', true, 33, 'system', NOW()),
('SP_VERWATLTUNG_3A_4', 'Spezialist:in Verwaltung 3a/4', '333a', 13, 'SP_VERWALTUNG', true, 34, 'system', NOW()),
('SP_VERWATLTUNG_3B_4', 'Spezialist:in Verwaltung 3b/4', '333b', 13, 'SP_VERWALTUNG', true, 35, 'system', NOW()),
('SP_VERWATLTUNG_4_4', 'Spezialist:in Verwaltung 4/4', '334', 14, 'SP_VERWALTUNG', true, 36, 'system', NOW()),
('EXP_VERWALTUNG_1_1', 'Expert:in Verwaltung 1/1', '341', 15, 'EXP_VERWALTUNG', true, 37, 'system', NOW())
ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- IT
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('411','Fachkraft IT 1/2',5,'FK_IT',true,72,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('412','Fachkraft IT 2/2',6,'FK_IT',true,73,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('421','Spezial-Fachkraft IT 1/4',7,'SFK_IT',true,74,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('422','Spezial-Fachkraft IT 2/4',8,'SFK_IT',true,75,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('423','Spezial-Fachkraft IT 3/4',9,'SFK_IT',true,76,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('424','Spezial-Fachkraft IT 4/4',10,'SFK_IT',true,77,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('431','Spezialist:in IT 1/4',11,'SP_IT',true,78,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('432a','Spezialist:in IT 2a/4',12,'SP_IT',true,79,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('432b','Spezialist:in IT 2b/4',12,'SP_IT',true,80,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('433a','Spezialist:in IT 3a/4',13,'SP_IT',true,81,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('433b','Spezialist:in IT 3b/4',13,'SP_IT',true,82,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('434','Spezialist:in IT 4/4',14,'SP_IT',true,83,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('441','Expert:in IT 1/4',15,'EXP_IT',true,84,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('442','Expert:in IT 2/4',16,'EXP_IT',true,85,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('443','Expert:in IT 3/4',17,'EXP_IT',true,86,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('444','Expert:in IT 4/4',18,'EXP_IT',true,87,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('451','Top-Expert:in IT 1/1',19,'TOP_EXP_IT',true,88,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- TECHNIK
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, code, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES
('FK_TECHNIK_1_3', 'Fachkraft Technik 1/3', '311', 4, 'FK_TECHNIK', true, 38, 'system', NOW()),
('FK_TECHNIK_2a_3', 'Fachkraft Technik 2a/3', '312a', 5, 'FK_TECHNIK', true, 39, 'system', NOW()),
('FK_TECHNIK_2b_3','Fachkraft Technik 2b/3', '312b', 5, 'FK_TECHNIK', true, 40, 'system', NOW()),
('FK_TECHNIK_3_3', 'Fachkraft Technik 3/3', '313', 6, 'FK_TECHNIK', true, 41, 'system', NOW())
ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- Produktion
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('511','Hilfskraft Produktion 1/4',1,'HK_PROD',true,89,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('512','Hilfskraft Produktion 2/4',2,'HK_PROD',true,90,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('513','Hilfskraft Produktion 3/4',3,'HK_PROD',true,91,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('514','Hilfskraft Produktion 4/4',4,'HK_PROD',true,92,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('521','Fachkraft Produktion 1/2',5,'FK_PROD',true,93,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('522','Fachkraft Produktion 2/2',6,'FK_PROD',true,94,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('531','Spezial-Fachkraft Produktion 1/4',7,'SFK_PROD',true,95,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('532','Spezial-Fachkraft Produktion 2/4',8,'SFK_PROD',true,96,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('533','Spezial-Fachkraft Produktion 3/4',9,'SFK_PROD',true,97,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('534','Spezial-Fachkraft Produktion 4/4',10,'SFK_PROD',true,98,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('541','Spezialist:in Produktion 1/4',11,'SP_PROD',true,99,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('542a','Spezialist:in Produktion 2a/4',12,'SP_PROD',true,100,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('542b','Spezialist:in Produktion 2b/4',12,'SP_PROD',true,101,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('543a','Spezialist:in Produktion 3a/4',13,'SP_PROD',true,102,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('543b','Spezialist:in Produktion 3b/4',13,'SP_PROD',true,103,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('544','Spezialist:in Produktion 4/4',14,'SP_PROD',true,104,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- IT & Software
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, code, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES
('FK_IT_1_2', 'Fachkraft IT & Software 1/2', '411', 5, 'FK_IT', true, 42, 'system', NOW()),
('FK_IT_2_2', 'Fachkraft IT & Software 2/2', '412', 6, 'FK_IT', true, 43, 'system', NOW()),
('SFK_IT_1_4', 'Spezial-Fachkraft IT & Software 1/4', '421', 7, 'SFK_IT', true, 44, 'system', NOW()),
('SFK_IT_2_4', 'Spezial-Fachkraft IT & Software 2/4', '422', 8, 'SFK_IT', true, 45, 'system', NOW()),
('SFK_IT_3_4', 'Spezial-Fachkraft IT & Software 3/4', '423', 9, 'SFK_IT', true, 46, 'system', NOW()),
('SFK_IT_4_4', 'Spezial-Fachkraft IT & Software 4/4', '424', 10, 'SFK_IT', true, 47, 'system', NOW()),
('SP_IT_1_4', 'Spezialist:in IT & Software 1/4', '431', 11, 'SP_IT', true, 48, 'system', NOW()),
('SP_IT_2A_4', 'Spezialist:in IT & Software 2a/4', '432a', 12, 'SP_IT', true, 49, 'system', NOW()),
('SP_IT_2B_4', 'Spezialist:in IT & Software 2b/4', '432b', 12, 'SP_IT', true, 50, 'system', NOW()),
('SP_IT_3A_4', 'Spezialist:in IT & Software 3a/4', '433a', 13, 'SP_IT', true, 51, 'system', NOW()),
('SP_IT_3B_4', 'Spezialist:in IT & Software 3b/4', '433b', 13, 'SP_IT', true, 52, 'system', NOW()),
('SP_IT_4_4', 'Spezialist:in IT & Software 4/4', '434', 14, 'SP_IT', true, 53, 'system', NOW()),
('EXP_IT_1_1', 'Expert:in IT & Software 1/1', '441', 15, 'EXP_IT', true, 54, 'system', NOW())
ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- Handwerk, Logistik, ..
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('611','Hilfskraft Handwerk, Instandhaltung + Logistik 1/4',1,'HK_HIL',true,105,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('612','Hilfskraft Handwerk, Instandhaltung + Logistik 2/4',2,'HK_HIL',true,106,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('613','Hilfskraft Handwerk, Instandhaltung + Logistik 3/4',3,'HK_HIL',true,107,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('614','Hilfskraft Handwerk, Instandhaltung + Logistik 4/4',4,'HK_HIL',true,108,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('621','Fachkraft Handwerk, Instandhaltung + Logistik 1/2',5,'FK_HIL',true,109,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('622','Fachkraft Handwerk, Instandhaltung + Logistik 2/2',6,'FK_HIL',true,110,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('631','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik 1/4',7,'SFK_HIL',true,111,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('632','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik 2/4',8,'SFK_HIL',true,112,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('633','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik 3/4',9,'SFK_HIL',true,113,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('634','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik 4/4',10,'SFK_HIL',true,114,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- TECHNISCHE DIENSTE
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, code, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES
('HK_TECHN_DIENSTE_1_4', 'Hilfskraft Technische Dienste 1/4', '511', 1, 'HK_TECHN_DIENSTE', true, 55, 'system', NOW()),
('HK_TECHN_DIENSTE_2_4', 'Hilfskraft Technische Dienste 2/4', '512', 2, 'HK_TECHN_DIENSTE', true, 56, 'system', NOW()),
('HK_TECHN_DIENSTE_3_4', 'Hilfskraft Technische Dienste 3/4', '513', 3, 'HK_TECHN_DIENSTE', true, 57, 'system', NOW()),
('HK_TECHN_DIENSTE_4_4', 'Hilfskraft Technische Dienste 4/4', '514', 4, 'HK_TECHN_DIENSTE', true, 58, 'system', NOW()),
('FK_TECHN_DIENSTE_1_2', 'Fachkraft Technische Dienste 1/2', '521', 5, 'FK_TECHN_DIENSTE', true, 59, 'system', NOW()),
('FK_TECHN_DIENSTE_2_2', 'Fachkraft Technische Dienste 2/2', '522', 6, 'FK_TECHN_DIENSTE', true, 60, 'system', NOW()),
('SFK_TECHN_DIENSTE_1_4', 'Spezial-Fachkraft Technische Dienste 1/4', '531', 7, 'SFK_TECHN_DIENSTE', true, 61, 'system', NOW()),
('SFK_TECHN_DIENSTE_2_4', 'Spezial-Fachkraft Technische Dienste 2/4', '532', 8, 'SFK_TECHN_DIENSTE', true, 62, 'system', NOW()),
('SFK_TECHN_DIENSTE_3_4', 'Spezial-Fachkraft Technische Dienste 3/4', '533', 9, 'SFK_TECHN_DIENSTE', true, 63, 'system', NOW()),
('SFK_TECHN_DIENSTE_4_4', 'Spezial-Fachkraft Technische Dienste 4/4', '534', 10, 'SFK_TECHN_DIENSTE', true, 64, 'system', NOW())
ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
";
@@ -280,16 +238,7 @@ CREATE TABLE IF NOT EXISTS hr.tbl_lohnguide_fachrichtung (
GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE hr.tbl_lohnguide_fachrichtung TO vilesci;
INSERT INTO hr.tbl_lohnguide_fachrichtung(fachrichtung_kurzbz,bezeichnung,aktiv,insertvon,insertamum) VALUES
('FA00','Keine Berücksichtigung',true,'system',NOW()),
('FA01','Administration allgemein',true,'system',NOW()),
('FA02','Dienste Infrastruktur',true,'system',NOW()),
('FA03','Finanzwesen & Controlling',true,'system',NOW()),
('FA04','IT',true,'system',NOW()),
('FA05','Logistik',true,'system',NOW()),
('FA06','Marketing & Digitales Marketing',true,'system',NOW()),
('FA07','Produktion',true,'system',NOW()),
('FA08','Technik',true,'system',NOW()),
('FA09','Verkauf',true,'system',NOW())
('FA00','Keine Berücksichtigung',true,'system',NOW())
ON CONFLICT (fachrichtung_kurzbz) DO NOTHING;
";
@@ -298,6 +247,7 @@ ON CONFLICT (fachrichtung_kurzbz) DO NOTHING;
echo '<strong>Lohnguide Fachrichtung: ' . $db->db_last_error() . '</strong><br>';
else
echo 'hr.tbl_lohnguide_fachrichtung wurde neu erstellt<br>';
}
}
@@ -0,0 +1,41 @@
<?php
/* Copyright (C) 2017 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* Authors: Harald Bamberger <harald.bamberger@technikum-wien.at>,
*
* Beschreibung:
* Permission basis/other_lv_plan
*/
if (! defined('DB_NAME')) exit('No direct script access allowed');
// Add permission: basis/gehaelter
if($result = @$db->db_query("SELECT 1 FROM system.tbl_berechtigung WHERE berechtigung_kurzbz = 'basis/other_lv_plan';"))
{
if($db->db_num_rows($result) == 0)
{
$qry = "INSERT INTO system.tbl_berechtigung(berechtigung_kurzbz, beschreibung) VALUES('basis/other_lv_plan', 'Permission holder can view other users timetables (LV plans)');";
if(!$db->db_query($qry))
{
echo '<strong>system.tbl_berechtigung '.$db->db_last_error().'</strong><br>';
}
else
{
echo 'system.tbl_berechtigung: Added permission "basis/other_lv_plan"<br>';
}
}
}
+749
View File
@@ -29582,6 +29582,126 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'calendar_sync',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Persönlichen LV-Plan abonnieren',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Subscribe to personal schedule',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'calendar_sync_instructions',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Anleitung LV-Plan Synchronisation',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Instructions for synchronizing your schedule',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'calendar_sync_cal_dav',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'CalDAV URL (Android, Thunderbird, CalDAV-Synchronizer)',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'CalDAV URL (Android, Thunderbird, CalDAV-Synchronizer)',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'calendar_sync_cal_dav_principal',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'CalDAV Principal URL (MacOS, iOS)',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'CalDAV Principal URL (MacOS, iOS)',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'calendar_sync_i_cal',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'iCAL URL (Google)',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'iCAL URL (Google)',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'calendar_sync_clipboard_copy_confirmation',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'URL in die Zwischenablage kopiert.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'URL copied to clipboard.',
'description' => '',
'insertvon' => 'system'
)
)
),
//Profil Phrasen ende
// LvPlan Phrasen start
array(
@@ -31409,6 +31529,88 @@ array(
),
//ProfilUpdate Phrasen ende
//Studium Phrasen start
array(
'app' => 'core',
'category' => 'studium',
'phrase' => 'studium',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Studium',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Study',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'studium',
'phrase' => 'studienordnung',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Studienordnung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Study regulation',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'studium',
'phrase' => 'lektoren',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Lektoren',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Lecturers',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'studium',
'phrase' => 'studiensemester',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Studiensemester',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Study semester',
'description' => '',
'insertvon' => 'system'
)
)
),
// Studium Phrasen Ende
array(
'app' => 'core',
'category' => 'global',
@@ -35540,6 +35742,227 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'bookmark',
'phrase' => 'editFilter',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Filter bearbeiten',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'edit Filter',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'bookmark',
'phrase' => 'filterByTags',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Nach Tags Filtern',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'filter by Tags',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'bookmark',
'phrase' => 'sortDownwards',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Nach unten verschieben',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'sort downwards',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'bookmark',
'phrase' => 'sortToTop',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Nach oben verschieben',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'sort to top',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'bookmark',
'phrase' => 'deleteBookmark',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Lesezeichen löschen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'delete Bookmark',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'bookmark',
'phrase' => 'editBookmark',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Lesezeichen bearbeiten',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'edit Bookmark',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'bookmark',
'phrase' => 'filterUpdated',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Filteraktion erfolgreich durchgeführt',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'filter updated successfully',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'bookmark',
'phrase' => 'errorInputNecessary',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Eingabe eines Zeichens erforderlich',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'One character must be entered',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'bookmark',
'phrase' => 'noFilter',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Kein Filter für Lesezeichen ausgewählt',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'No bookmark filter entered',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'bookmark',
'phrase' => 'headerFilterBookmark',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Lesezeichen filtern',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Filter Bookmarks',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'bookmark',
'phrase' => 'adminMode',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Inhalt kann nicht im Administrator Modus angezeigt werden.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Content can not be displayed in administrator mode.',
'description' => '',
'insertvon' => 'system'
)
)
),
// BOOKMARK PHRASEN END ----------------------------------------------------------------------
array(
'app' => 'core',
'category' => 'global',
@@ -38121,6 +38544,26 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'lvUebersicht',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Lehrveranstaltungs Übersicht',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Course Overview',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
@@ -58497,6 +58940,312 @@ I have been informed that I am under no obligation to consent to the transmissio
)
),
// ### Phrases Dashboard Admin END
// ### LvPlanStgOrg START ###
array(
'app' => 'core',
'category' => 'LvPlan',
'phrase' => 'noStgProvided',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'kein Studiengang hinterlegt',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'no degree-program provided',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'LvPlan',
'phrase' => 'headerLvPlanLvVerband',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Lv-Plan Lehrverband',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Course-Plan Teaching Association',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'LvPlan',
'phrase' => 'chooseStg',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Studiengang auswählen...',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Select a degree-program...',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'LvPlan',
'phrase' => 'loadLvPlan',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Lehrverband laden',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Load Course-Plan',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'LvPlan',
'phrase' => 'backToDropdown',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Zurück zum Auswahl-Dropdown',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Back to Dropdown',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'LvPlan',
'phrase' => 'error_SemMissing',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Semester wählen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Select a semester',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'LvPlan',
'phrase' => 'error_VerbandMissing',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Verband wählen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Select a verband',
'description' => '',
'insertvon' => 'system'
)
)
),
// ### LvPlanStgOrg END ###
// DMS-Link Phrasen Start
array(
'app' => 'core',
'category' => 'DMS-Link',
'phrase' => 'lvplanSyncFAQ',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => '7188',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => '7188',
'description' => '',
'insertvon' => 'system'
)
)
),
// DMS-Link Phrasen End
// ### Start Notenstatistik
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'hinweis',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Hinweis',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Notice',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'noticeAverage',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Bitte beachten Sie, dass außercurriculare Lehrveranstaltungen (zB Freifächer) den errechneten Notendurchschnitt verfälschen können.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Please note that extracurricular courses (e.g., electives) can distort the calculated grade point average.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'noticeWeightedAverage',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Der gewichtete Notendurchschnitt errechnet sich aus der Note der Lehrveranstaltung, multipliziert mit deren ECTS, dividiert durch die Summe aller ECTS.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'The weighted average grade is calculated by multiplying the grade of the course by its ECTS credits and dividing by the sum of all ECTS credits.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'headerAverage',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Notendurchschnitt* der Zeugnisnote',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Average grade* of report card grade',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'headerWeightedAverage',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Gewichteter** Notendurchschnitt* der Zeugnisnote',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Weighted** average grade* of report card grade',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'notenstatistik',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Leistungsübersicht',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Performance overview',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'info_noGradesYet',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Es wurden noch keine Beurteilungen eingetragen.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'No grades have been entered yet.',
'description' => '',
'insertvon' => 'system'
)
)
),
// ### End Notenstatistik
);
+8 -7
View File
@@ -742,7 +742,7 @@ function _getFunktionscontainer_Funktionscode123456($bisfunktion_arr)
$has_oe_lehrgang = !($studiengang->studiengang_kz > 0 && $studiengang->studiengang_kz < 10000);
// 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;
}
@@ -825,6 +825,7 @@ function _addFunktionscontainer_Funktionscode7($uid, $funktion_arr, $stichtag)
$entwicklungsteam_arr = array_filter($entwicklungsteam_arr, function ($obj) {
return
!in_array($obj->studiengang_kz, BIS_EXCLUDE_STG) &&
$obj->melderelevant &&
$obj->studiengang_kz > 0 &&
$obj->studiengang_kz < 10000;
});
@@ -889,7 +890,7 @@ function _getLehrecontainer($sws_proStg_arr)
$kennzeichen_name = $is_lehrgang ? 'LehrgangNr' : 'StgKz';
// 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();
@@ -904,8 +905,8 @@ function _getLehrecontainer($sws_proStg_arr)
}
else // Lehrecontainer mit STG schon vorhanden
{
$lehre_obj_arr = array_filter($lehre_arr, function (&$obj) use ($sws_proStg) {
return $obj->StgKz == $sws_proStg->studiengang_kz;
$lehre_obj_arr = array_filter($lehre_arr, function (&$obj) use ($sws_proStg, $kennzeichen_name) {
return isset($obj->{$kennzeichen_name}) && $obj->{$kennzeichen_name} == $sws_proStg->melde_studiengang_kz;
});
// SWS ergaenzen
@@ -1359,15 +1360,15 @@ function verwendung_exists($bisverwendung, $verwendung_arr)
/**
* 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
* @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)
{
$kennzeichenName = $row->LehrgangNr ?? $row->StgKz;
$kennzeichenName = isset($row->LehrgangNr) ? 'LehrgangNr' : 'StgKz';
if(isset($row->{$kennzeichenName}) && $row->{$kennzeichenName} == $melde_studiengang_kz)
return true;
}

Some files were not shown because too many files have changed in this diff Show More