Compare commits

...

314 Commits

Author SHA1 Message Date
Harald Bamberger 63ff8dd4c4 Merge branch 'cis40_2026-05_ma_rc' into demo-cis40 2026-05-29 13:40:02 +02:00
Harald Bamberger 6019489ef1 Merge branch 'cis40_2026-02_rc' into cis40_2026-05_ma_rc 2026-05-29 13:38:58 +02:00
adisposkofh fc79f92796 added 'add reservation'/'reservation not allowed' tooltips in calendar 2026-05-27 15:35:45 +02:00
adisposkofh 8e569a9ccd displaying forbidden room reservation slots 2026-05-27 10:43:28 +02:00
adisposkofh d7b2964e4e obsolete use of viewData object from cisRouterView 2026-05-26 17:33:38 +02:00
Harald Bamberger 366edb7e90 Merge branch 'studvw_2026_05_rc_tags' into demo-cis40 2026-05-26 11:01:57 +02:00
Harald Bamberger ac6aa28680 Merge branch 'studvw_2026_05_rc' into studvw_2026_05_rc_tags 2026-05-26 11:01:39 +02:00
ma0068 f6f58642f5 update second occurence of id STORAGE_KEY 2026-05-26 10:57:26 +02:00
Harald Bamberger 9430135592 Merge branch 'studvw_2026_05_rc_tags' into demo-cis40 2026-05-26 09:40:11 +02:00
Harald Bamberger 57eec98604 Merge branch 'studvw_2026_05_rc' into studvw_2026_05_rc_tags 2026-05-26 09:38:06 +02:00
Harald Bamberger 1561aa55b7 correct model path 2026-05-26 09:37:49 +02:00
Harald Bamberger 35f4d23308 Merge branch 'studvw_2026_05_rc' into studvw_2026_05_rc_tags 2026-05-26 09:28:40 +02:00
Harald Bamberger 199b62d13f Merge branch 'studvw_2026_05_rc' into demo-cis40 2026-05-26 09:07:04 +02:00
Harald Bamberger bb366e4117 font color black on tag limette 2026-05-26 09:06:44 +02:00
Harald Bamberger b0fc5a3618 Merge branch 'cis40_2026-05_ma_rc' into demo-cis40 2026-05-22 10:37:30 +02:00
Harald Bamberger b0f90cafb6 Merge branch 'cis40_2026-02_rc' into cis40_2026-05_ma_rc 2026-05-22 10:36:54 +02:00
Harald Bamberger 6e599908cd Merge branch 'studvw_2026_05_rc_tags' into demo-cis40 2026-05-19 17:30:14 +02:00
Harald Bamberger c099eec7a7 Merge branch 'studvw_2026_05_rc' into studvw_2026_05_rc_tags 2026-05-19 17:29:52 +02:00
Harald Bamberger 0962e62e69 Merge branch 'feature-76202/HorizontalSplit_from_master' into studvw_2026_05_rc 2026-05-19 17:28:08 +02:00
Harald Bamberger dcd6cfd74b increase width of horizontal split 2026-05-19 17:26:46 +02:00
Harald Bamberger b1be5a4ba4 Merge branch 'studvw_2026_05_rc_tags' into demo-cis40 2026-05-19 17:12:55 +02:00
Harald Bamberger 5caab182b2 Merge branch 'studvw_2026_05_rc' into studvw_2026_05_rc_tags 2026-05-19 17:12:37 +02:00
Harald Bamberger baaf941f25 Merge branch 'feature-75958/StudVW_DatenExport_wie_im_Fas' into studvw_2026_05_rc 2026-05-19 17:12:11 +02:00
Harald Bamberger 2e5e7afb4d slot end tag 2026-05-19 17:11:51 +02:00
Harald Bamberger 71f68da90b Merge branch 'studvw_2026_05_rc_tags' into demo-cis40 2026-05-19 16:52:36 +02:00
Harald Bamberger 2c4626fd1a Merge branch 'studvw_2026_05_rc' into studvw_2026_05_rc_tags 2026-05-19 16:52:14 +02:00
Harald Bamberger 178638c006 Merge branch 'feature-75958/StudVW_DatenExport_wie_im_Fas' into studvw_2026_05_rc 2026-05-19 16:51:57 +02:00
Harald Bamberger 981c97173e add padding to export icon link 2026-05-19 16:51:24 +02:00
Harald Bamberger c11fd03362 Merge branch 'studvw_2026_05_rc_tags' into demo-cis40 2026-05-19 16:42:43 +02:00
Harald Bamberger 49d5c46133 Merge branch 'feature-75959/StudVw_Automatische_Tags' into studvw_2026_05_rc_tags 2026-05-19 16:42:25 +02:00
Harald Bamberger 9fb00f6cbd remove orphaned end tag, add right padding 2026-05-19 16:40:18 +02:00
Harald Bamberger ec1422a136 Merge branch 'cis40_2026-05_ma_rc' into demo-cis40 2026-05-19 16:18:06 +02:00
Harald Bamberger d56d1bc2bd Merge branch 'feature-69187/CIS_MA_Pruefungsprotokolle' into cis40_2026-05_ma_rc 2026-05-19 16:17:35 +02:00
Harald Bamberger 45eca862ac use table alias 2026-05-19 16:17:14 +02:00
Harald Bamberger 89e98056e6 Merge branch 'cis40_2026-05_ma_rc' into demo-cis40 2026-05-19 16:12:31 +02:00
Harald Bamberger 9f738f4871 Merge branch 'feature-69187/CIS_MA_Pruefungsprotokolle' into cis40_2026-05_ma_rc 2026-05-19 16:11:37 +02:00
Harald Bamberger 3533a3fd4b add missing comma 2026-05-19 16:11:08 +02:00
Harald Bamberger 5cf14aa886 Merge branch 'studvw_2026_05_rc_tags' into demo-cis40 2026-05-19 10:28:32 +02:00
Harald Bamberger 7d3096c2e8 readd required assignments for tags removed during merge 2026-05-19 10:28:08 +02:00
Harald Bamberger ef2f9630a9 Merge branch 'feature-75959/StudVw_Automatische_Tags' into studvw_2026_05_rc_tags 2026-05-19 10:22:28 +02:00
Harald Bamberger 4d3c73c78f add join condition for notizzuordung via prestudent_id only 2026-05-19 10:21:56 +02:00
Harald Bamberger 9ee6a0a01a Merge branch 'studvw_2026_05_rc_tags' into demo-cis40 2026-05-19 09:51:42 +02:00
Harald Bamberger 1e226f4515 Merge branch 'master' into studvw_2026_05_rc_tags 2026-05-18 17:27:36 +02:00
Harald Bamberger ca8fe5bd19 Merge branch 'feature-75959/StudVw_Automatische_Tags' into studvw_2026_05_rc_tags 2026-05-18 17:27:06 +02:00
Harald Bamberger 6d26ec2ab7 Merge branch 'cis40_2026-05_ma_rc' into demo-cis40 2026-05-18 16:51:33 +02:00
Harald Bamberger 672acfe8fe Merge branch 'cis40_2026-02_rc' into cis40_2026-05_ma_rc 2026-05-18 16:49:53 +02:00
adisposkofh 6a4db90897 Merge branch 'cis40_2026-05_ma_rc' of github.com:FH-Complete/FHC-Core into cis40_2026-05_ma_rc 2026-05-18 11:22:05 +02:00
ma0068 d4e170037b add order Nachname, Vorname 2026-05-18 10:11:17 +02:00
adisposkofh 82587a70be restored missing provided properties 2026-05-15 18:40:42 +02:00
ma0068 7aba7aefb9 add order mitarbeiter_uid 2026-05-15 11:05:12 +02:00
Harald Bamberger 0a18fcf732 Merge branch 'cis40_2026-05_ma_rc' into demo-cis40 2026-05-13 21:11:36 +02:00
Harald Bamberger d0ef9ca96c Merge branch 'cis40_2026-02_rc' into cis40_2026-05_ma_rc 2026-05-13 21:11:16 +02:00
ma0068 e5ae400686 Tab Abschlusspruefung: use searchPersons instead of Mitarbeiter and refactor Label for dropdown 2026-05-13 17:38:39 +02:00
adisposkofh a5d5d42ba3 fixed issue missing event attributes in stundenplan library 2026-05-12 13:19:57 +02:00
Harald Bamberger e08b731cb5 Merge branch 'cis40_2026-05_ma_rc' into demo-cis40 2026-05-12 11:05:28 +02:00
adisposkofh 15441a46f7 initializing stg org lv plan with provided url params 2026-05-11 17:35:09 +02:00
adisposkofh a9a56bb1e9 fixed issue with parsing isEditable from fetched profile view data 2026-05-11 17:03:33 +02:00
adisposkofh e840be84eb positioning of profile cards 'quick links' and 'calendar sync' 2026-05-11 16:47:41 +02:00
adisposkofh 0b40455e3c only displaying avg grade for students 2026-05-11 15:49:20 +02:00
ma0068 1b822fc8f8 reset config stv to readonly for automated tags 2026-05-11 15:15:06 +02:00
ma0068 368eded1fd remove frontend filtering list for studiensemester, update logs TagJob 2026-05-11 15:12:53 +02:00
adisposkofh d9ea5a95af fixed issue with undefined reservierung attributes 2026-05-11 15:04:32 +02:00
Harald Bamberger d1a4cfd5df Merge branch 'cis40_2026-05_ma_rc' into demo-cis40 2026-05-11 13:55:07 +02:00
Harald Bamberger 23502d4fab Merge branch 'cis40_2026-02_rc' into cis40_2026-05_ma_rc 2026-05-11 13:54:38 +02:00
ma0068 dbcacdddd6 add tagFilter for semester to students endpoint, add decoding jsonResult in Tags 2026-05-11 13:18:56 +02:00
Harald Bamberger 8b2ef4e832 Merge branch 'master' into feature-75959/StudVw_Automatische_Tags 2026-05-11 10:54:26 +02:00
ma0068 6fedb4b108 delete unused code 2026-05-11 10:07:28 +02:00
ma0068 905c69c384 create log entries for Summary, Details and Errors 2026-05-11 09:20:59 +02:00
ma0068 6fa0a7c102 use injected studiensemester instead of api call for studiensemester 2026-05-08 11:53:47 +02:00
ma0068 7320dc448e correct bezeichnung interval.ende 2026-05-08 10:57:19 +02:00
ma0068 ede9323224 refactor manual triggering for double degree lib 2026-05-08 08:33:53 +02:00
ma0068 3dcf72d679 add semester filter for tags
- in detailheader: use currentSem for manual triggering and refactor formatter
- in list: preload list of ids with start and end for tabulator formatter to enable filtering
2026-05-07 17:36:34 +02:00
Harald Bamberger 37b70861d1 update date of use db data 2026-05-07 15:34:53 +02:00
Harald Bamberger 150d54eeda Merge branch 'cis40_2026-05_ma_rc' into demo-cis40 2026-05-07 14:02:19 +02:00
Harald Bamberger 3690babf62 Merge branch 'cis40_2026-02_rc' into cis40_2026-05_ma_rc 2026-05-07 14:01:50 +02:00
Harald Bamberger 68b26a1091 Merge branch 'cis40_2026-05_ma_rc' into demo-cis40 2026-05-06 18:36:48 +02:00
Harald Bamberger 9ca7ff73f4 Merge branch 'feature-60873/GesamtnoteneingabeCis4' into cis40_2026-05_ma_rc 2026-05-06 18:36:27 +02:00
Harald Bamberger c56064d189 js import add missing file extension 2026-05-06 18:36:05 +02:00
Harald Bamberger 5c371a20d2 Merge branch 'cis40_2026-05_ma_rc' into demo-cis40 2026-05-06 18:32:35 +02:00
Harald Bamberger 424495c636 Merge branch 'feature-70747/Zeitsperren_auf_VueJs_Portieren' into cis40_2026-05_ma_rc 2026-05-06 18:31:46 +02:00
Harald Bamberger 9c7e98f1cb js import add missing file extension 2026-05-06 18:31:26 +02:00
Harald Bamberger d42157c5f8 Merge branch 'cis40_2026-05_ma_rc' into demo-cis40 2026-05-06 18:19:53 +02:00
Harald Bamberger 502851856b Merge branch 'studvw_2026_05_rc' into demo-cis40 2026-05-06 17:57:36 +02:00
Harald Bamberger 23edcf3aa7 Merge branch 'cis40_2026-02_rc' into cis40_2026-05_ma_rc 2026-05-06 17:35:48 +02:00
Harald Bamberger 8ce67a4726 Merge branch 'master' into studvw_2026_05_rc 2026-05-06 17:35:05 +02:00
Harald Bamberger bcfdf1e05c Merge branch 'feature-75838/UXImproveProjektarbeitSTVSprint247' into studvw_2026_05_rc 2026-05-06 17:33:33 +02:00
Harald Bamberger cb744799bd Merge branch 'feature-71776/StudVW_Abschlusspruefung_DropdownsPrueferAndMultiActionNewPruefung' into studvw_2026_05_rc 2026-05-06 17:27:26 +02:00
Harald Bamberger 90ebee25fe Merge branch 'feature-71775/StudVW_ChangeSem_Behaviour' into studvw_2026_05_rc 2026-05-06 16:28:44 +02:00
Harald Bamberger 3e8ed231a6 Merge branch 'feature-71392/StudVW_Einstiegssemester_bei_Interessenten_anzeigen' into studvw_2026_05_rc 2026-05-06 16:03:37 +02:00
Harald Bamberger 4c67b9d267 Merge branch 'feature-76202/HorizontalSplit_from_master' into studvw_2026_05_rc 2026-05-06 15:56:14 +02:00
Johann Hoffmann c16cf342cb horizontalsplit component analog to verticalsplit; also built it into Studentenverwaltung.js menu; added defaultRatio prop to both components which defaults to 50/50 seperation 2026-05-06 15:54:49 +02:00
Harald Bamberger d843f05922 Merge branch 'feature-76201/StudVWTabsBeibehalten' into studvw_2026_05_rc 2026-05-06 15:42:29 +02:00
Harald Bamberger 44e9b4dff1 Merge branch 'feature-76145/StudVW_Studienverlauf_in_AppMenu' into studvw_2026_05_rc 2026-05-06 15:41:41 +02:00
Harald Bamberger bda42ab347 Merge branch 'feature-75958/StudVW_DatenExport_wie_im_Fas' into studvw_2026_05_rc 2026-05-06 15:06:51 +02:00
Harald Bamberger b5b69878b8 Merge branch 'feature-75901/StudVW_AppMenue_neueVerlinkungen' into studvw_2026_05_rc 2026-05-06 14:40:17 +02:00
Harald Bamberger 6f17ddbbdf Merge branch 'feature-63428/Infomail_Foto' into studvw_2026_05_rc 2026-05-06 14:35:45 +02:00
ma0068 ef1347c7d5 - use function getAktOrNextSemester
- restructure single libs to isolate typeId of data
- refactor Job: use params, details output
- use functions insert, update, delete
- use timeperiode for getAllTags and checkIfExistingTag
- controller: rebuild function: change to POST
- updated insertVon Batchuser
2026-05-05 15:20:02 +02:00
Harald Bamberger ca19306b72 Merge branch 'cis40_2026-02_rc' into cis40_2026-05_ma_rc 2026-05-05 12:36:56 +02:00
Harald Bamberger 66c0c14748 Merge branch 'cis40_2026-02_rc' into cis40_2026-05_ma_rc 2026-05-05 08:53:34 +02:00
Harald Bamberger cffa493984 Merge branch 'feature-68301/cis4_ma_raumreservierung' into cis40_2026-05_ma_rc 2026-05-04 18:17:53 +02:00
Harald Bamberger 9030cdcc76 Merge branch 'feature-70747/Zeitsperren_auf_VueJs_Portieren' into cis40_2026-05_ma_rc 2026-05-04 18:06:21 +02:00
Harald Bamberger c3ef487a6f Merge branch 'feature-76148/MyLvaMA' into cis40_2026-05_ma_rc 2026-05-04 17:59:51 +02:00
Harald Bamberger ac0eddf4c7 Merge branch 'feature-69187/CIS_MA_Pruefungsprotokolle' into cis40_2026-05_ma_rc 2026-05-04 17:05:36 +02:00
Harald Bamberger e373e797f4 Merge branch 'feature-60873/GesamtnoteneingabeCis4' into cis40_2026-05_ma_rc 2026-05-04 17:01:44 +02:00
Harald Bamberger 382244035b Merge branch 'feature-68610/CIS4_Projektabgabe' into cis40_2026-05_ma_rc 2026-05-04 16:09:33 +02:00
Harald Bamberger b9c8c71274 Merge branch 'feature-61236/Cis4MenuErweiterung' into cis40_2026-05_ma_rc 2026-05-04 16:01:18 +02:00
ma0068 d1fa5f64c4 refactor TagJob, TagLib and single TagLibs for dynamic use of typeId, add temporary testfile application/libraries/tags/CoreFiftyFiveTagLib.php for testing person_id 2026-04-30 11:17:39 +02:00
ma0068 21eb95bb23 manually triggering of updateTag 2026-04-28 16:24:30 +02:00
ma0068 e856c8ad6b change settings bezeichnung_mehrsprachig::varchar[] in TagController functions getTag and getTags 2026-04-27 10:01:54 +02:00
ma0068 2723d6a741 get Semester of Model instead of Using CONST, delete unused models 2026-04-24 14:07:14 +02:00
ma0068 e5e22931a9 add db queries for update system, update tags in config 2026-04-24 12:49:00 +02:00
ma0068 21ea277aaf use checks for param studiengang_kurzbz instead of deleting it 2026-04-24 09:20:37 +02:00
ma0068 2c0badf67c bugfix error undefined offset: deletion of unused variable studiengang_kz 2026-04-23 17:28:43 +02:00
ma0068 5e72161ab0 add variables from and to to show time of validity in backend
refactor function getZuordnungIds of all single tagLibs
add description line for editing/showing modal tag
2026-04-23 16:39:21 +02:00
Johann Hoffmann e86e7f0bd8 manage default myLv layout mode via localStorage; actions row takes remaining space via fitDataStretch and wraps around into new row if action buttons take up too much space; added tabulator persistence on TableBuilt event; slight watcher adjustments to combat race conditions; loading spinner while tabulatorUuid has not been defined yet -> maybe worth improving but seems to work fine; 2026-04-23 15:59:38 +02:00
Johann Hoffmann c36f259571 revert back to 1 http request per lva menu list for performance reasons since usually 1-2 lva are slow and the rest load quickly; many menu_lv.inc scripts would need to be loaded via require instead of require_once but that breaks some of them; only table height calc on watcher; why does an english teacher need a mathe online link?; 2026-04-21 17:40:24 +02:00
Johann Hoffmann 501bae585a WIP 2026-04-21 16:59:10 +02:00
Johann Hoffmann 2c72f704d0 remove unnecessary bottom calc functions 2026-04-21 15:32:51 +02:00
Johann Hoffmann 194de7b4e7 change lva fetch to select semesterstunden from a seperate aggregation subquery since they used to be listed by LE but current view should only show LVA to have the correct amounts displayed 2026-04-21 15:29:45 +02:00
Johann Hoffmann c03609142b dropdown menu formatter logic; dropdown menu from action col css fix; phrasen preload; WIP semesterstunden verification with the old page - hours dont add up yet; 2026-04-21 14:05:19 +02:00
Johann Hoffmann 5374f71732 WIP replicating the lvMenu links & dropdowns accurately inside a tabulator5 actionButtons custom formatted column with all its kinks; basic links already working, design/layout definitely not finished, WIP working on how to get a sensible link dropdown in there; also still considering if this page even needs 2 seperate http requests at all 2026-04-20 17:54:27 +02:00
Harald Bamberger c53d451000 Merge branch 'master' into feature-68301/cis4_ma_raumreservierung 2026-04-20 16:34:30 +02:00
ma0068 903f3b99b3 add Studienverlauf to AppMenue 2026-04-20 11:36:54 +02:00
Harald Bamberger f6747713a1 Merge branch 'master' into feature-60873/GesamtnoteneingabeCis4 2026-04-20 11:13:14 +02:00
Harald Bamberger 59ddf175ed Merge branch 'master' into feature-61236/Cis4MenuErweiterung 2026-04-20 10:47:58 +02:00
Johann Hoffmann ccd8d5f871 WIP alternative table representation of Cis4 MyLv page for teachers assigned to a lot of different degree programs -> cards layout would result in a lot of fluff & scroll; finalized getMultipleLvMenu() functionality in LvMenu.php controller to avoid http request overhead; deleted phrasen mixin from old components since this is a plugin bound to the apps globalConfigince some time already; Allow to switch layout similar to calendar modes -> need to further adapt old component structure to resuse lvmenu data 2026-04-20 10:28:49 +02:00
Harald Bamberger bd47ad2b8c Merge branch 'master' into feature-69187/CIS_MA_Pruefungsprotokolle 2026-04-20 09:48:34 +02:00
Harald Bamberger e90019cdd8 Merge branch 'master' into feature-68610/CIS4_Projektabgabe 2026-04-20 09:30:54 +02:00
ma0068 df9edc36e0 save temporary 2026-04-20 08:40:55 +02:00
ma0068 39f1716ae2 set all automatic tags to readonly, add icon lock to automatic tags 2026-04-17 10:47:45 +02:00
Harald Bamberger c7b25e6632 add name to TagComponent, combine action slot definition for tagcomponent with definition for filter active 2026-04-16 16:21:33 +02:00
Harald Bamberger 695f3455e6 add function generateCSSsIncludeIfExtensionCssExists and use to load additional tag.css from extensions if exists 2026-04-16 15:20:11 +02:00
Johann Hoffmann be508c99ea fetch & show lva semesterstunden in cards template in footer area 2026-04-16 11:59:10 +02:00
ma0068 1339077b57 use single tagLibs for dynamisation
new tagLibs (doubleDegree, jgv, missingZgv, Outgoing, StudienbeitragErhoeht, Unterbrecher)
refactor tagJob rebuildAutomatedTags
2026-04-15 09:45:57 +02:00
Johann Hoffmann 3aebccbb9d track activeTab in StudVW Details.js and pass it to :default property of fhcTabs instances to attempt to set the last active tab to current, else use the old default tab of route.params.tab 2026-04-10 13:01:13 +02:00
ma0068 ee6f28c06d Refactor TagJob and TabLib: use dynamic approach with column taglib for creating Tags 2026-04-09 13:58:42 +02:00
Harald Bamberger fdb037da96 poc move logic for single tags to libs, and add lib loading path and name to extra column in tbl_notiztyp 2026-04-09 08:36:13 +02:00
Johann Hoffmann 71a77fc576 dont render lv.js menuitem dropdowntoggle if the item is marked as 'unavailable', even if it has links attached 2026-04-08 16:10:08 +02:00
ma0068 49cbbfc50c update user 2026-04-08 15:24:06 +02:00
ma0068 4f8e98f5d5 restyle button reloadTags 2026-04-08 15:11:58 +02:00
Johann Hoffmann a028297da6 call authInfo api on created in fhc app to provide isStudent, isMitarbeiter & uid; renamed main component of MyLv route from 'student' to 'MyLv' since it fits for employees aswell; adapt Lv.js component to render a bootstrap5 dropdown list when menuItem has a c4_linkList or c4_moodle_links; 2026-04-08 15:11:03 +02:00
ma0068 e9e614aa52 Add Functionality Frontend: triggering of reloading tags for prestudent from studentHeader
Refactoring: Recycling of tags instead of deleting it and creating new ones
2026-04-08 14:26:14 +02:00
Johann Hoffmann ac44b36b59 cis4 myLv employee functionality implemented; wrote new query fetching studiensemester an employee in which he had assigned lehraufträge; afterwards fetch all lva by employee uid and sem_kurzbz so they fit in the existing component structure; MyLv/Lvs/SS2026 etc now also returns a 'student' | 'employee' string based on path taken inside the controller to aid vue components in rendering useful information and avoiding senseless fetch requests -> e.g. employees cant have grades on lva they are teaching; added component names for sanity when using vuedevtools; 2026-04-07 16:07:10 +02:00
ma0068 50af6694d0 show Tags in CoreHeader 2026-03-31 12:48:27 +02:00
ma0068 5dbddb4beb remove char 2026-03-27 09:46:48 +01:00
ma0068 b20613f5d7 Basic Structure Automatic Tagging
- new library libTag
- new Job TagJob
- new entries for automatic tags in config stv.php
- automatic tagging of wiederholer and prewiederholer
2026-03-27 09:40:14 +01:00
Harald Bamberger 0621564be7 Merge branch 'master' into feature-71775/StudVW_ChangeSem_Behaviour 2026-03-19 15:57:13 +01:00
ma0068 f1714db09e add ExcelExport of FAS to StudentList, add slot additional to Filtercomponent 2026-03-19 15:00:51 +01:00
Johann Hoffmann aba4bc2909 merge projektarbeit details & betreuer form and handle several UX changes regarding formData of both 2026-03-19 11:12:14 +01:00
ma0068 70b025da30 add Link Reihungstestverwaltung to AppMenu 2026-03-17 14:59:05 +01:00
ma0068 87dd858358 add entry StudVw to navigation CI Menue 2026-03-17 14:58:39 +01:00
ma0068 51f3edcd72 delete not needed parameter orgform for html version lv planung 2026-03-17 12:57:55 +01:00
ma0068 e7f626bd72 add Lv-Planung to app menue 2026-03-17 12:49:40 +01:00
ma0068 17f94aabdf add loading skeletons for mitarbeiterHeader 2026-03-16 17:16:40 +01:00
ma0068 c49e32c4ac use skeletons for all data in studentHeader 2026-03-16 11:26:23 +01:00
ma0068 df124db84a use primeVue Skeleton while loading Data for Semester, Verband and Gruppe 2026-03-13 12:40:29 +01:00
ma0068 29a4b4aadc use permission PERM_LOGGED for header, add condition one empty space for showing verband 2026-03-13 09:11:00 +01:00
ma0068 2682ea75ab use only one endpoint for detailheader 2026-03-12 16:58:48 +01:00
ma0068 b3a63a60e9 update phrase noTextInSem 2026-03-12 16:02:44 +01:00
Harald Bamberger a54dfaf0c7 Merge branch 'master' into feature-71775/StudVW_ChangeSem_Behaviour 2026-03-12 15:20:53 +01:00
Harald Bamberger 595538d6bb Merge branch 'master' into feature-71775/StudVW_ChangeSem_Behaviour 2026-03-12 15:10:06 +01:00
Harald Bamberger a5329e5bba Merge branch 'master' into feature-71775/StudVW_ChangeSem_Behaviour 2026-03-12 15:03:56 +01:00
Harald Bamberger 37a79c4589 Merge branch 'master' into feature-71775/StudVW_ChangeSem_Behaviour 2026-03-12 15:03:28 +01:00
Harald Bamberger dd760f8210 finetune behavior 2026-03-12 11:10:13 +01:00
ma0068 dd87e893ba working version for relaod header and data without updateUrl 2026-03-11 17:13:59 +01:00
Harald Bamberger 4b767d4a57 Merge branch 'vv_und_studvw_2026_02_rc4_ma0080' into demo-cis40 2026-03-10 08:42:20 +01:00
ma0068 127ce312ea reload Details and Detailheader without updateUrl 2026-03-10 08:28:11 +01:00
Harald Bamberger 5d461a72f6 Merge branch 'vv_und_studvw_2026_02_rc4_ma0080' into demo-cis40 2026-03-09 12:11:14 +01:00
ma0068 2237e9f1b7 refactor detail Header, relaod array if semester changed, show no status if no status, phrase 2026-03-09 11:42:24 +01:00
ma0068 9b4fa132dc add status 2026-03-06 10:58:54 +01:00
Harald Bamberger daf332a102 add query_studiensemester_kurzbz to result of fetchStudents, fetchPrestudents and search 2026-03-06 10:39:37 +01:00
Johann Hoffmann 5a6d20f817 fixed scrollOffset discrepancy issue by UNDEFINING rowHeight and thus let tabulator detect the actual size of a row itself and not trust a sligthly wrong rowHeight value; define centered formatter in js/tabulator/formatter/centered and use it on unformatted columns for consistency; 2026-03-05 15:47:19 +01:00
Harald Bamberger 942b4512fe Merge branch 'vv_und_studvw_2026_02_rc4_ma0080' into demo-cis40 2026-03-04 18:57:47 +01:00
Harald Bamberger 3778b27574 Datenstand 12.02.2026 2026-03-04 18:31:13 +01:00
Harald Bamberger 15b340db56 Merge branch 'vv_und_studvw_2026_02_rc4_ma0080' into demo-cis40 2026-03-04 17:58:14 +01:00
Johann Hoffmann ce5da22180 import error msg phrase; config for noten which dount count towards prüfungsantritt & load & use that; 2026-03-03 16:47:17 +01:00
Johann Hoffmann 05b2c3c42b persistence of order/width/visibility of notenTable predefined columns before applying dynamic pruefung columns in setupData and other table col manipulating routines; fix antrittCalculation reactivity and resulting selection eligibility and the reactivity of that also; added custom sorter for pruefung cols; some minor UX tweaks; 2026-03-03 12:05:05 +01:00
Johann Hoffmann 2dd732b924 Merge branch 'master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	application/views/CisRouterView/CisRouterView.php
2026-02-27 10:38:24 +01:00
ma0068 c127c0900e refactor dropdown for pruefer, add localStorage for formVariables, add multiactions for adding new finalexam 2026-02-25 14:28:41 +01:00
ma0068 fc01fa045e add and use variable semester_berechnet for using ausbildungssemester in status interessent 2026-02-23 13:44:33 +01:00
Johann Hoffmann 110f73e622 WIP notentool table persistence with dyn cols 2026-02-17 14:09:57 +01:00
Johann Hoffmann bb0d118284 Pruefung Termin1/original Note column fixed logic, variable column creation reserved for termin2/termin3 mixtures; remove redundant termintypen from student row in table if backend sent a succesful update response since their cant be multiple termin2/termin3; block notenvorschlag übernehmen once a pruefung exists; 2026-02-13 12:25:55 +01:00
Johann Hoffmann 701ccadff3 approveGrades phrase with counter; computed maxAntritte by config settings; block illegal pruefung creation in frontend (backend was already working); 2026-02-12 13:56:56 +01:00
Johann Hoffmann 34555504df Notenfreigabe email template insert in dbupdate3.4; notenvorschlag übernehmen cellHandler adjustment; WIP testing pruefungen & pruefungs config 2026-02-11 10:20:53 +01:00
Johann Hoffmann d1f5220925 notenschluessel availability check; WIP lvgesamtnote model method that actually fetches the notenvorschlag without join over zeugnisnote; 2026-02-10 12:57:38 +01:00
Johann Hoffmann decd514b22 WIP improving notenimport with punktefeature 2026-02-09 09:50:22 +01:00
Johann Hoffmann 6cf7093293 testing/implementing more config flags; dont select on certain cols; certain cols only available with certain flags; 2026-02-05 16:49:26 +01:00
Johann Hoffmann 17f11fa871 Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	application/controllers/api/frontend/v1/Lehre.php
#	application/models/education/Lehrveranstaltung_model.php
#	application/models/education/Note_model.php
#	application/views/CisRouterView/CisRouterView.php
#	public/js/api/factory/studiensemester.js
#	public/js/components/Bootstrap/Offcanvas.js
#	public/js/components/Overlay/FhcOverlay.js
2026-02-04 15:40:34 +01:00
Johann Hoffmann 96812868a4 WIP 2026-02-04 10:31:20 +01:00
Johann Hoffmann 40c79158f7 punkte feature basically finished; WIP testing & import rewrite 2026-02-03 17:38:33 +01:00
Johann Hoffmann 054663ee00 WIP punkte 2026-02-03 11:34:59 +01:00
Johann Hoffmann 81eee814e9 yellow dropdown styling only on editable tabulator colums for note_vorschlag; fetch note for punkte for notenvorschlag and pruefungsnote if certain config is set; added debounce helper file/function; WIP persisting punkte in backend 2026-02-02 17:07:16 +01:00
Johann Hoffmann 390a3c0d5a Notenschluessel Model + WIP making sense of legacy config flags 2026-01-29 14:50:37 +01:00
Johann Hoffmann 695dd655c0 WIP implementing getNotenvorschlagStudent, currently only works for whole lva/sem 2026-01-26 14:47:21 +01:00
Andreas Österreicher 05838cc477 Merge branch 'master' into demo-cis40 2026-01-21 16:35:41 +01:00
ma0068 c34ffedb42 Autocomplete Field Vertretung
Form with ISO fields for hours
Admin functionality for seeing Timelocks of uid in route
Backend with CI-Validations
Phrases
2026-01-21 10:59:16 +01:00
Andreas Österreicher 96e02c9911 Merge branch 'epic-56039/LV-Evaluierung' into demo-cis40 2026-01-20 10:55:13 +01:00
Andreas Österreicher 248fceb4b5 Merge branch 'epic-56039/LV-Evaluierung' into demo-cis40 2026-01-20 10:34:10 +01:00
ma0068 b5382b1bdf Form and Start Backend 2026-01-15 15:39:42 +01:00
ma0068 519cbc7601 base structure and table 2026-01-14 09:26:15 +01:00
Harald Bamberger 92697d9468 Merge branch 'epic-56039/LV-Evaluierung' into demo-cis40 2026-01-12 16:30:15 +01:00
Harald Bamberger d1ec9c92eb Merge branch 'master' into demo-cis40 2026-01-12 16:10:13 +01:00
Johann Hoffmann 957da460a6 shorter passwort freigabe text; loadCisConfig for Benotungstool via api; anw% in notentable via event; WIP incorporating CIS config into actual noten logic; 2025-12-18 15:24:08 +01:00
ma0048 8f9f447acf cis4 raumreservierung beta version 2025-12-18 11:08:40 +01:00
Johann Hoffmann 2cee36d7b5 try/catch around moodle event & proper error message 2025-12-17 14:29:05 +01:00
Harald Bamberger 454cf5ea64 Merge branch 'studvw_2025-12_rc3' into demo-cis40 2025-12-10 15:36:30 +01:00
Harald Bamberger 4563533e67 Merge branch 'feature-69570/StudVw_Filter_broken' into demo-cis40 2025-12-04 08:43:40 +01:00
Harald Bamberger 640e719eda Merge branch 'feature-69438/FHC4_Studierendenverwaltung/FeedbackPunkte' into demo-cis40 2025-12-02 09:46:25 +01:00
Harald Bamberger aece2b1d90 Merge branch 'feature-69065/Projektarbeiten_Firmen_verwalten' into demo-cis40 2025-11-28 11:16:24 +01:00
Johann Hoffmann 43925e3088 custom sticky css; offcanvas mobility legende; mobiltiy zusatz in seperate column; added getMobilityZusatzForUids & formatZusatz similar to digital anw mobility zusaetze; 2025-11-25 17:23:49 +01:00
Johann Hoffmann 1c236cce02 Merge branch 'master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	application/config/routes.php
#	application/models/crm/Prestudent_model.php
#	application/models/education/Lehreinheit_model.php
#	application/models/education/Lehrveranstaltung_model.php
#	public/js/apps/Dashboard/Fhc.js
#	system/phrasesupdate.php
2025-11-25 10:50:26 +01:00
Johann Hoffmann 4956a517ca loading overlay, notenfreigabe available phrase with counter; placeholder phrasen & date format; WIP handling moodle API errors well 2025-11-25 10:31:52 +01:00
Harald Bamberger cb13655ecd Merge branch 'master' into demo-cis40 2025-11-24 17:14:14 +01:00
Harald Bamberger 9b3b6cec8f Merge branch 'feature-69388/Pruefung_API_insert_Propleme' into demo-cis40 2025-11-24 16:27:06 +01:00
Harald Bamberger 3ca1a1f1c7 Merge branch 'feature-69388/Pruefung_API_insert_Propleme' into demo-cis40 2025-11-24 09:48:38 +01:00
Harald Bamberger a7210208d6 Merge branch 'studvw_2025-11_rc2' into demo-cis40 2025-11-24 09:42:45 +01:00
Harald Bamberger eaad23ff69 Merge branch 'feature-69388/Pruefung_API_insert_Propleme' into demo-cis40 2025-11-21 12:30:37 +01:00
Harald Bamberger c7ddf0a2a9 Merge branch 'studvw_2025-11_rc2' into demo-cis40 2025-11-21 10:56:32 +01:00
Harald Bamberger b129ef873b Merge branch 'master' into demo-cis40 2025-11-18 16:46:11 +01:00
Harald Bamberger d927426d70 Merge branch 'master' into demo-cis40 2025-11-14 11:40:48 +01:00
Alexei Karpenko d9c7df736c Pruefungsprotokolle: added cis header/footer switch, added language in form 2025-11-14 11:01:11 +01:00
Harald Bamberger 9ced137ded Merge branch 'studvw_2025-11_rc' into demo-cis40 2025-11-11 13:18:13 +01:00
Alexei Karpenko 73244df019 Projektabgaben Uebersicht: added comments, changed placehoder search text, added phrase 2025-11-11 12:41:20 +01:00
Alexei Karpenko 91d24ebae8 Projektabgabe Übersicht: added phrases 2025-11-10 17:37:24 +01:00
Alexei Karpenko 6861e26ed6 Projektabgabe Übersicht: added flag "inVisualLibrary with event" 2025-11-10 14:43:28 +01:00
Harald Bamberger a89da50e0a add missing comma 2025-11-10 13:08:36 +01:00
Harald Bamberger 9256046b6c Merge branch 'feature-67518/Studierendenverwaltung_Karteireiter_anzeigen_verstecken_wenn_Interessent_gewaehlt' into demo-cis40 2025-11-10 13:05:32 +01:00
Harald Bamberger fd95dd8023 Merge branch 'feature-63443/Studierendenverwaltung_Tab_Abschlusspruefung_Finetuning' into demo-cis40 2025-11-10 10:58:41 +01:00
Harald Bamberger cda2b84939 Merge branch 'feature-68738/FHC4_Studierendenverwaltung_Studentlist_Export' into demo-cis40 2025-11-10 09:28:36 +01:00
Alexei Karpenko 34242e12ea Projektabgabe Uebersicht: added person status 2025-11-07 02:22:21 +01:00
Harald Bamberger d893fe63e7 Merge branch 'master' into demo-cis40 2025-11-05 17:15:02 +01:00
Harald Bamberger 5aedf85982 Merge branch 'master' into demo-cis40 2025-11-05 16:52:01 +01:00
Harald Bamberger 55ab79c004 Merge branch 'master' into demo-cis40 2025-11-05 16:32:54 +01:00
Harald Bamberger 7c7a72600d Merge branch 'master' into demo-cis40 2025-11-05 16:18:00 +01:00
Alexei Karpenko bd67e41aa6 Projektabgabe Uebersicht: added zip download, bugfixes Projektabgabe search 2025-11-05 11:44:36 +01:00
Harald Bamberger 01357654c0 Merge branch 'feature-67490/studstatus_suche_abort_controller_haengt' into demo-cis40 2025-11-05 11:03:40 +01:00
Harald Bamberger cd5900481b Merge branch 'feature-68745/Menue_zur_Verlinkung_von_Apps' into demo-cis40 2025-11-04 13:12:56 +01:00
Alexei Karpenko 601c6c53e7 CIS4: added Projektabgabe Uebersicht, enabled filtering by Abgabe data and person data 2025-11-03 15:49:31 +01:00
Alexei Karpenko b7ba740a3a Merge branch 'master' into feature-68610/CIS4_Projektabgabe 2025-11-03 15:47:31 +01:00
Harald Bamberger ddef97d49f Merge branch 'master' into demo-cis40 2025-11-03 15:35:39 +01:00
Harald Bamberger 47b0449f22 Merge branch 'feature-68745/Menue_zur_Verlinkung_von_Apps' into demo-cis40 2025-10-29 07:41:17 +01:00
Harald Bamberger 0193460678 Merge branch 'master' into demo-cis40 2025-10-16 15:41:06 +02:00
Harald Bamberger e02b3e78d2 Merge branch 'master' into demo-cis40 2025-10-14 10:41:07 +02:00
Johann Hoffmann a6167583a3 hinweistexte import/freigabe; distinct css for editable table cols notenvorschlag & freigabe; trigger 'getEntschuldigungsStatusForStudentOnDate' event when saving a pruefungstermin -> if akzeptierte entschuldigung is found for student on pruefungsdate it is automatically set to entschuldigt; fix event unmount lifecycle; 2025-10-07 16:26:58 +02:00
Harald Bamberger c91e0a6b74 Merge branch 'feature-68390/Studierendenverwaltung-Details_Header_und_Tabsnav_nicht_mitscrollen' into demo-cis40 2025-10-01 18:47:36 +02:00
Harald Bamberger c6852a9514 Merge branch 'feature-63445/Studierendenverwaltung_Filter' into demo-cis40 2025-10-01 18:12:07 +02:00
Harald Bamberger 04b5240615 Merge branch 'master' into demo-cis40 2025-10-01 17:46:28 +02:00
Harald Bamberger 483547cd9c Merge branch 'feature-63394/StV_Favoriten_64_Zeichen_Variablenbeschraenkung' into demo-cis40 2025-09-30 16:37:34 +02:00
Harald Bamberger 345413cd1f change display date of database dump 2025-09-30 14:15:16 +02:00
Harald Bamberger f57dc6785b Merge branch 'master' into feature-63428/Infomail_Foto 2025-09-30 13:47:04 +02:00
Harald Bamberger 046a60b89c Merge branch 'master' into demo-cis40 2025-09-30 13:26:17 +02:00
Harald Bamberger da725e3ab1 Merge branch 'feature-63444/stv_mehrfachaktion_mail_an_private_oder_interne_adresse' into demo-cis40 2025-09-29 17:40:26 +02:00
Harald Bamberger edd34941cc Merge branch 'feature-61232/Studierendenverwaltung_Karteireiter_Projektarbeit_portieren' into demo-cis40 2025-09-29 16:57:10 +02:00
Harald Bamberger c65686ef42 Merge branch 'feature-63373/FHC4_Studierendenverwaltung_Dokumente_erstellen' into demo-cis40 2025-09-29 13:53:30 +02:00
Harald Bamberger c7729f85df Merge branch 'master' into demo-cis40 2025-09-29 08:09:33 +02:00
Harald Bamberger 53b187c9b6 Merge branch 'feature-63435/Studierendenverwaltung_Studierende_Verbandsgruppen_und_Spezialgruppen_zuordnen_Multiaktion' into demo-cis40 2025-09-18 07:23:43 +02:00
Harald Bamberger 7bc18e41b1 Merge branch 'feature-63374/Studierendenverwaltung_International_Baum_Filter' into demo-cis40 2025-09-18 07:22:24 +02:00
Harald Bamberger 8bc17eded5 Merge branch 'master' into demo-cis40 2025-09-18 07:17:28 +02:00
Harald Bamberger 9adeb1c63a Merge branch 'master' into demo-cis40 2025-09-17 14:39:30 +02:00
Harald Bamberger 091ed0dacd Merge branch 'master' into demo-cis40 2025-09-17 14:16:05 +02:00
Johann Hoffmann 1e68eb0b90 berechtigungsprüfung 'lehre/benotungstool:rw' in Noten Controller; API Method documentation; removed addMeta statements; removed Tabulator Event logging; 2025-08-21 13:35:38 +02:00
Johann Hoffmann 332efd4106 selection fixes; phrases; preserve scroll after redraw when pressing action buttons on far right; take nav offset into account for width calculation; 2025-08-20 16:25:21 +02:00
Harald Bamberger cf0a47cab7 Merge branch 'master' into demo-cis40 2025-08-19 17:36:09 +02:00
Harald Bamberger bb7b24346a Merge branch 'master' into demo-cis40 2025-08-19 17:20:43 +02:00
Johann Hoffmann f303191c54 alternative email per cis global config 2025-08-19 16:33:12 +02:00
Harald Bamberger a68e2e798b Merge branch 'master' into demo-cis40 2025-08-19 16:08:13 +02:00
Harald Bamberger bd49f3c420 Merge branch 'master' into demo-cis40 2025-08-19 14:00:40 +02:00
Harald Bamberger 3c1d67267d Merge branch 'master' into demo-cis40 2025-08-19 13:53:28 +02:00
Johann Hoffmann d6c7f16ceb Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4 2025-08-19 13:43:23 +02:00
Harald Bamberger 61e1933a5b activate button new Student for demo-cis4 2025-08-19 13:33:39 +02:00
Johann Hoffmann f1912fe739 custom selection handling due to bugged tab5 rowSelect, should work as intended now, WIP illegal emails 2025-08-19 13:32:18 +02:00
Harald Bamberger 25c443098c Merge branch 'master' into demo-cis40 2025-08-19 13:23:37 +02:00
ma0068 cf59bcff12 add Functionality sendInfomail
- expand InputComponent for dynamic adding of actionButton to Form Upload Image
- add Phrases for Infomail
2025-08-19 09:16:26 +02:00
Johann Hoffmann 2f7fe05d21 mobility legende; TopCalc Row (sum, negative, prueflinge); fix row selection issues; 2025-08-18 11:25:38 +02:00
Johann Hoffmann ee4b61f549 recommit branch; event naming changes; 2025-08-18 09:13:14 +02:00
Harald Bamberger 352c6e1d68 Merge branch 'master' into demo-cis40 2025-08-14 18:16:36 +02:00
Harald Bamberger 0e2444d7ce Merge branch 'feature-63411/Notizen_Anzahl_im_Tab_Header_anzeigen' into demo-cis40 2025-08-14 16:46:22 +02:00
Harald Bamberger c4390819a2 Merge branch 'feature-63411/Notizen_Anzahl_im_Tab_Header_anzeigen' into demo-cis40 2025-08-14 16:36:43 +02:00
Harald Bamberger 51104268d8 Merge branch 'feature-63411/Notizen_Anzahl_im_Tab_Header_anzeigen' into demo-cis40 2025-08-14 15:34:25 +02:00
Harald Bamberger cb5d43ab21 Merge branch 'master' into demo-cis40 2025-08-14 10:31:23 +02:00
Harald Bamberger 08de5cd377 Merge branch 'master' into demo-cis40 2025-08-13 18:48:08 +02:00
Harald Bamberger 274fa63658 Merge branch 'feature-62779/xul_abloese_haustechnik_schluesselverwaltung' into demo-cis40 2025-08-13 07:34:45 +02:00
Harald Bamberger 81b2ed6551 Merge branch 'merge_FHC4_55354_55991_55992_60874_60876_60875_61229_61230_61231' into demo-cis40 2025-08-13 07:05:21 +02:00
Harald Bamberger d9c6c3947b Merge branch 'feature-60973/komponente_fuer_lehrfaecherverteilung' into demo-cis40 2025-08-13 07:04:51 +02:00
Harald Bamberger 2d6dbbdb16 alter position and styling of demo info 2025-08-12 09:30:12 +02:00
Harald Bamberger b60faa1cab styling demo info 2025-08-12 09:24:09 +02:00
Harald Bamberger fd19953864 add demo info 2025-08-12 09:20:39 +02:00
Harald Bamberger 927bafd93a Merge branch 'master' into demo-cis40 2025-08-12 09:06:31 +02:00
Harald Bamberger d93b21f0af Merge branch 'master' into demo-cis40 2025-08-11 11:28:38 +02:00
Johann Hoffmann 511a4256bc moved LE loading and infoString setup into LehreinheitenModule, which can be bound to an instance of Primevue3 Dropdown via v-bind. WIP modularizing other common selections like LVA & Semester Kurzbz; 2025-08-08 13:35:01 +02:00
Johann Hoffmann 3c9db86df2 Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	application/config/routes.php
#	application/controllers/api/frontend/v1/Lehre.php
#	application/models/education/Lehrveranstaltung_model.php
#	application/views/CisRouterView/CisRouterView.php
#	public/js/apps/Dashboard/Fhc.js
#	system/phrasesupdate.php
2025-08-07 15:17:10 +02:00
Johann Hoffmann 367204a1ee removed legacy classes (except mobility) and moved crud functionality to LePruefungModel, LVgesamtnoteModel & LehrveranstaltungModel; 2025-08-07 14:54:41 +02:00
Harald Bamberger 6a8f6fa05a Merge branch 'master' into demo-cis40 2025-08-06 12:17:25 +02:00
Harald Bamberger 7f997c9411 Merge branch 'rc1_FHC4_C4' into demo-cis40 2025-08-05 18:36:34 +02:00
Harald Bamberger 2e3f98deb0 Merge branch 'rc1_FHC4_C4' into demo-cis40 2025-08-05 16:34:44 +02:00
Harald Bamberger a5feb656e0 Merge branch 'rc1_FHC4_C4' into demo-cis40 2025-08-05 16:19:02 +02:00
Harald Bamberger 9329955328 Merge branch 'rc1_FHC4_C4' into demo-cis40 2025-08-05 13:29:03 +02:00
Harald Bamberger 5b99dd2685 Merge branch 'rc1_FHC4_C4' into demo-cis40 2025-08-05 12:21:03 +02:00
Harald Bamberger 19288fc8f5 Merge branch 'rc1_FHC4_C4' into demo-cis40 2025-08-05 12:00:03 +02:00
Andreas Österreicher 9f97de5952 Merge branch 'rc1_FHC4_C4' into demo-cis40 2025-08-05 09:42:35 +02:00
Andreas Österreicher c476f7339c Merge branch 'rc1_FHC4_C4' into demo-cis40 2025-08-05 09:31:18 +02:00
Harald Bamberger 3dc1cf520a Merge branch 'rc1_FHC4_C4' into demo-cis40 2025-08-05 07:22:01 +02:00
Harald Bamberger 616c335a3b Merge branch 'rc1_FHC4_C4' into demo-cis40 2025-08-05 07:10:49 +02:00
Johann Hoffmann bbe55a75ea noten/pruefungen import; import validation for nr of antritte and date/antritt chronological order; 2025-08-04 14:27:33 +02:00
Johann Hoffmann e58bf3a8cf WIP noten/pruefung import 2025-08-01 09:48:46 +02:00
Johann Hoffmann 1f2f866c61 positiv/negativ/unbenotet filter; reload lva/le correctly on sem/lva dropdown selection change; 2025-07-30 17:10:43 +02:00
Johann Hoffmann 6ccbc95697 createPruefung entry with "noch nicht eingetragen" note as default for a selection of students at given date; filterHeaders in noten cols; antrittCount col; pruefungsformatter with antritt highlighting; 2025-07-29 17:32:07 +02:00
Johann Hoffmann 52d9e0a195 pruefungen columns pro datum nicht pro termintyp; row Selection & modal newPruefungForSelectedStudents mit linked multiselect dropdown; WIP antritte berechnen 2025-07-25 12:50:51 +02:00
Johann Hoffmann 6a3982347b teilnoten/punkte berechnen für notenvorschläge; vorschläge übernehmen; noten freigabe mit passwort; prüfungen generisch anzeigen nach datum gruppiert; prüfungen anlegen/bearbeiten auf mapping termin1/2/3 möglich. 2025-07-24 17:02:57 +02:00
Johann Hoffmann fe7feeb74e Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4 2025-07-15 09:51:11 +02:00
SimonGschnell b2419beca6 refactor(CIS4 MitarbeiterSeiten integration): updates the approveLehrauftrag view to include the CIS4 header and footer 2025-06-04 15:02:33 +02:00
SimonGschnell b752b475d9 update(Lehrauftrag Mitarbeiter View): adds the new CIS4-Header/Footer if in CIS4 context and updates minor layout problems 2025-06-04 11:18:02 +02:00
Johann Hoffmann 658fe79ad7 load Teilnoten via moodle Event trigger; WIP further noten/punkte logic; 2025-05-12 16:45:33 +02:00
Johann Hoffmann 5bbf05ac8a WIP Gesamtnoteneingabe Notenberechnung endpoint 2025-05-12 12:50:56 +02:00
140 changed files with 13879 additions and 891 deletions
+9
View File
@@ -23,6 +23,15 @@ Events::on('loadRenderers', function ($renderers) {
);
});
Events::on('loadRenderers', function ($renderers) {
$fhc_core_renderers =& $renderers();
$fhc_core_renderers["slot_room"] = array(
'modalTitle' => APP_ROOT.'public/js/components/Cis/Renderer/Slot/roomModalTitle.js',
'modalContent' => APP_ROOT.'public/js/components/Cis/Renderer/Slot/roomModalContent.js',
'calendarEventStyles' => APP_ROOT.'public/css/Cis4/CoreCalendarEvents.css'
);
});
Events::on('loadRenderers', function ($renderers) {
$fhc_core_renderers =& $renderers();
$fhc_core_renderers["ferien"] = array(
+7
View File
@@ -170,6 +170,13 @@ $config['navigation_header'] = array(
'expand' => true,
'sort' => 51,
'requiredPermissions' => 'vertrag/mitarbeiter:r'
),
'studierendenverwaltung' => array(
'link' => site_url('studentenverwaltung'),
'description' => 'Studierendenverwaltung',
'expand' => true,
'sort' => 52,
'requiredPermissions' => ['admin:r', 'assistenz:r']
)
)
),
+6
View File
@@ -0,0 +1,6 @@
<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');
// 'entschuldigt' & 'noch nicht eingetragen' -> wirken sich nicht auf Antritte aus
$config['NOTEN_OHNE_ANTRITT'] = [9, 17]; // tbl_note pk
+1
View File
@@ -67,6 +67,7 @@ $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['Cis/Benotungstool/.*'] = 'Cis/Benotungstool/index/$1';
$route['Abgabetool/Assistenz'] = 'Cis/Abgabetool/Assistenz';
$route['Abgabetool/Assistenz/(:any)'] = 'Cis/Abgabetool/Assistenz/$1';
+15 -7
View File
@@ -133,13 +133,21 @@ $config['students_tab_order'] = [
$config['stv_prestudent_tags'] = [
'prioone' => ['readonly' => false],
'priotwo' => ['readonly' => true],
'priotwo' => ['readonly' => false],
'hinweis' => ['readonly' => false],
'hinweis_assistenz' => ['readonly' => true],
'hinweis_kf' => ['readonly' => true],
'hinweis_assistenz' => ['readonly' => false],
'hinweis_kf' => ['readonly' => false],
'hinweis_lehrende' => ['readonly' => false],
'hinweis_stg_kf' => ['readonly' => true],
'finished_stg' => ['readonly' => true],
'finished_kf' => ['readonly' => true],
'inwork_kf' => ['readonly' => true],
'hinweis_stg_kf' => ['readonly' => false],
'finished_stg' => ['readonly' => false],
'finished_kf' => ['readonly' => false],
'inwork_kf' => ['readonly' => false],
'dd_auto' => ['readonly' => true],
'wh_auto' => ['readonly' => true],
'prewh_auto' => ['readonly' => true],
'out_auto' => ['readonly' => true],
'zgv_auto' => ['readonly' => true],
'unterbrecher_auto' => ['readonly' => true],
'stbtr_erh_auto' => ['readonly' => true],
'jgv_auto' => ['readonly' => true],
];
@@ -0,0 +1,37 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*
*/
class Benotungstool extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct([
'index' => self::PERM_LOGGED
]);
$this->_ci =& get_instance();
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @return void
*/
public function index()
{
$viewData = array(
'uid'=>getAuthUID(),
);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Benotungstool']);
}
}
@@ -0,0 +1,30 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*
*/
class ProjektabgabeUebersicht extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct([
'index' => ['basis/cis:r']
]);
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @return void
*/
public function index()
{
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'ProjektabgabeUebersicht']);
}
}
@@ -0,0 +1,30 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Zeitsperren extends Auth_Controller
{
public function __construct()
{
parent::__construct([
'index' => ['basis/cis:r'],
]);
// Load Libraries
$this->load->library('VariableLib', ['uid' => getAuthUID()]);
}
/**
* index loads the view Zeitsperren
* @access public
* @return void
*/
public function index()
{
$viewData = array(
'uid'=>getAuthUID(),
);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'zeitsperren']);
}
}
@@ -18,18 +18,9 @@
if (! defined('BASEPATH')) exit('No direct script access allowed');
//require_once('../../../include/studiengang.class.php');
//require_once('../../../include/student.class.php');
//require_once('../../../include/datum.class.php');
//require_once('../../../include/mail.class.php');
//require_once('../../../include/benutzerberechtigung.class.php');
//require_once('../../../include/phrasen.class.php');
//require_once('../../../include/projektarbeit.class.php');
//require_once('../../../include/projektbetreuer.class.php');
class Lehre extends FHCAPI_Controller
{
/**
* Object initialization
*/
@@ -40,38 +31,56 @@ class Lehre extends FHCAPI_Controller
'LV' => self::PERM_LOGGED,
'Pruefungen' => self::PERM_LOGGED,
'semesterAverageGrade' => self::PERM_LOGGED,
'getZugewieseneLv' => self::PERM_LOGGED,
'getLeForLv' => self::PERM_LOGGED
]);
$this->load->library('PhrasesLib');
$this->loadPhrases(
array(
'global',
'ui',
'abgabetool'
)
);
$this->load->helper('hlp_sancho_helper');
require_once(FHCPATH . 'include/studiengang.class.php');
require_once(FHCPATH . 'include/student.class.php');
require_once(FHCPATH . 'include/projektarbeit.class.php');
require_once(FHCPATH . 'include/projektbetreuer.class.php');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
/**
* constructs the emails of the groups from a lehrveranstaltung
*/
public function lvStudentenMail()
public function lvStudentenMail()
{
$lehreinheit_id = $this->input->get("lehreinheit_id",TRUE);
// return early if the required parameter is missing
if(!isset($lehreinheit_id))
{
$this->terminateWithError('Missing required parameter', self::ERROR_TYPE_GENERAL);
}
$lehreinheit_id = $this->input->get("lehreinheit_id",TRUE);
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$studentenMails = $this->LehreinheitModel->getStudentenMail($lehreinheit_id);
// return early if the required parameter is missing
if(!isset($lehreinheit_id))
{
$this->terminateWithError('Missing required parameter', self::ERROR_TYPE_GENERAL);
}
$studentenMails = $this->getDataOrTerminateWithError($studentenMails);
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$studentenMails = $this->LehreinheitModel->getStudentenMail($lehreinheit_id);
$studentenMails = $this->getDataOrTerminateWithError($studentenMails);
//convert array of objects into array of strings
$studentenMails = array_map(function($element){
return $element->mail;
}, $studentenMails);
$this->terminateWithSuccess($studentenMails);
$this->terminateWithSuccess($studentenMails);
}
public function LV($studiensemester_kurzbz, $lehrveranstaltung_id)
@@ -81,13 +90,13 @@ class Lehre extends FHCAPI_Controller
$result = $this->LehrveranstaltungModel->getLvsByStudentWithGrades(getAuthUID(), $studiensemester_kurzbz, getUserLanguage(), $lehrveranstaltung_id);
$result = current($this->getDataOrTerminateWithError($result));
$this->terminateWithSuccess($result);
}
/**
* fetches all Pruefungen of a student for a specific lehrveranstaltung
* if the student passed the Pruefung on the first attempt, no information about the Pruefungen is stored in the database
* if the student passed the Pruefung on the first attempt, no information about the Pruefungen is stored in the database
* @param mixed $lehrveranstaltung_id
* @return void
*/
@@ -145,5 +154,46 @@ class Lehre extends FHCAPI_Controller
$this->terminateWithSuccess(['average_grade' => $averageGrade, 'weighted_average_grade' => $weightedAverageGrade]);
}
}
/**
* fetches all assigned lehrveranstaltungen of a mitarbeiter for a given semester
* @param mixed $uid
* @param mixed $sem_kurzbz
* @return void
*/
public function getZugewieseneLv() {
$uid = $this->input->get("uid",TRUE);
$sem_kurzbz = $this->input->get("sem_kurzbz",TRUE);
// TODO: error messages
if(!isset($sem_kurzbz) || isEmptyString($sem_kurzbz))
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
if (!isset($uid) || isEmptyString($uid))
$uid = getAuthUID();
// querying other ma_uids data requires admin permission
if($uid !== getAuthUID()) {
$this->load->library('PermissionLib');
$isAdmin = $this->permissionlib->isBerechtigt('admin');
if(!$isAdmin) $this->terminateWithError($this->p->t('ui', 'keineBerechtigung'), 'general');
}
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$result = $this->LehrveranstaltungModel->getLvForLektorInSemester($sem_kurzbz, $uid);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function getLeForLv() {
$lv_id = $this->input->get("lv_id",TRUE);
$sem_kurzbz = $this->input->get("sem_kurzbz",TRUE);
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
// $this->terminateWithSuccess($this->LehreinheitModel->getLesForLv($lv_id, $sem_kurzbz));
$this->terminateWithSuccess($this->LehreinheitModel->getAllLehreinheitenForLvaAndMaUid($lv_id, getAuthUID(), $sem_kurzbz));
}
}
@@ -36,7 +36,8 @@ class LvMenu extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getLvMenu' => self::PERM_LOGGED
'getLvMenu' => self::PERM_LOGGED,
'getMultipleLvMenu' => self::PERM_LOGGED
]);
$this->load->model("ressource/Mitarbeiter_model");
@@ -61,24 +62,23 @@ class LvMenu extends FHCAPI_Controller
/**
* alternative function to get multiple lvMenus with a single http request
* not yet working as intended as the menu_lv.inc.php scripts called by the
* lvMenuBuild event have logic coupled to require_once import which results in
* a wrong logic after the first invocation -> faulty results for lvinfo, moodle
* and several others
*/
public function getMultipleLvMenu($lvMenuOptionList){
public function getMultipleLvMenu(){
$lvMenuOptionList = $this->input->post('lvMenuOptionList', true);
$result =[];
foreach($lvMenuOptionList as $lvMenuOptions){
$lvMenu = $this->getLvMenu($lvMenuOptions['lvid'],$lvMenuOptions['studiensemester_kurzbz']);
if(isError($lvMenu)){
// TODO: some lvMenu threw an error, handle error here
}
$lvMenu = $this->getLvMenuInternal($lvMenuOptions['lvid'],$lvMenuOptions['studiensemester_kurzbz']);
$result[$lvMenuOptions['lvid']]=$lvMenu;
}
$this->terminateWithSuccess($result);
}
/**
*
*/
public function getLvMenu($lvid, $studiensemester_kurzbz)
{
private function getLvMenuInternal($lvid, $studiensemester_kurzbz) {
// return early if parameters are missing
if(!isset($lvid) || !isset($studiensemester_kurzbz))
@@ -89,14 +89,14 @@ class LvMenu extends FHCAPI_Controller
// get the user
if (!$user=getAuthUID())
$this->terminateWithError($this->p->t('global', 'nichtAngemeldet'));
$this->terminateWithError($this->p->t('global', 'nichtAngemeldet'));
// check if is_lector
$is_lector = false;
$mares = $this->Mitarbeiter_model->isMitarbeiter($user);
if(hasData($mares))
{
$is_lector = getData($mares);
$is_lector = getData($mares);
}
// definition of user_is_allowed_to_upload
@@ -105,7 +105,7 @@ class LvMenu extends FHCAPI_Controller
// load lehrveranstaltung
$lvres = $this->Lehrveranstaltung_model->load($lvid);
if(!hasData($lvres))
if(!hasData($lvres))
{
$this->terminateWithError('LV ' . $lvid . ' not found.');
}
@@ -124,7 +124,7 @@ class LvMenu extends FHCAPI_Controller
$stgres = $this->Studiengang_model->load(strval($studiengang_kz));
if(!hasData($stgres))
{
$this->terminateWithError('Stg ' . $lv->studiengang_kz . ' not found.');
$this->terminateWithError('Stg ' . $lv->studiengang_kz . ' not found.');
}
$stg = (getData($stgres))[0];
$kurzbz = strtoupper($stg->typ . $stg->kurzbz);
@@ -139,7 +139,7 @@ class LvMenu extends FHCAPI_Controller
$angemeldet = false;
$lesres = $this->Lehreinheit_model->getLehreinheitenForStudentAndStudienSemester(
$lvid, $user, $angezeigtes_stsem
$lvid, $user, $angezeigtes_stsem
);
if(hasData($lesres) && count(getData($lesres)) > 0)
@@ -148,7 +148,7 @@ class LvMenu extends FHCAPI_Controller
// lehrfach
$lehrfach_id='';
if(defined('CIS_LEHRVERANSTALTUNG_LEHRFACH_ANZEIGEN') && CIS_LEHRVERANSTALTUNG_LEHRFACH_ANZEIGEN)
{
// Wenn der eingeloggte User zu einer der Lehreinheiten zugeteilt ist
@@ -211,8 +211,8 @@ class LvMenu extends FHCAPI_Controller
foreach($fbs as $row)
{
$lehrfach_oe_kurzbz_arr[] = $row->oe_kurzbz;
if($this->PermissionLib->isBerechtigt('lehre', null, $row->oe_kurzbz)
|| $this->PermissionLib->isBerechtigt('assistenz', null, $stg->oe_kurzbz))
if($this->PermissionLib->isBerechtigt('lehre', null, $row->oe_kurzbz)
|| $this->PermissionLib->isBerechtigt('assistenz', null, $stg->oe_kurzbz))
{
$user_is_allowed_to_upload=true;
}
@@ -224,21 +224,21 @@ class LvMenu extends FHCAPI_Controller
$menu = array();
$this->fhc_menu_lvinfo($menu, $lvid, $studiengang_kz, $lektor_der_lv, $is_lector, $lehrfach_oe_kurzbz_arr);
$this->fhc_menu_feedback($menu, $angemeldet, $lvid);
$this->fhc_menu_gesamtnote($menu, $angemeldet, $lvid, $lv, $is_lector, $angezeigtes_stsem);
$this->fhc_menu_emailStudierende($menu, $user, $angemeldet, $lvid, $angezeigtes_stsem);
$this->fhc_menu_abmeldung($menu, $user, $is_lector, $lvid, $angezeigtes_stsem);
$this->fhc_menu_lehretools($menu, $lvid, $angezeigtes_stsem, $sprache);
$this->fhc_menu_anrechnungStudent($menu, $lvid, $angezeigtes_stsem);
$this->fhc_menu_anrechnungLector($menu, $angezeigtes_stsem);
// Addons Menu Logic
// ##########################################################################################
@@ -272,18 +272,18 @@ class LvMenu extends FHCAPI_Controller
'permissionLib' => &$this->PermissionLib,
'phrasesLib' => &$this->PhrasesLib
];
Events::trigger('lvMenuBuild',
// passing $menu per reference
function & () use (&$menu) {
return $menu;
},
$params
Events::trigger('lvMenuBuild',
// passing $menu per reference
function & () use (&$menu) {
return $menu;
},
$params
);
// Menu sortieren
// ##########################################################################################
foreach ($menu as $key => $row){
// removes menu points that are not needed in the c4 lvUebersicht
@@ -291,7 +291,7 @@ class LvMenu extends FHCAPI_Controller
unset($menu[$key]);
continue;
}
// fills pos array to sort the menu
$pos[$key] = $row['position'];
@@ -299,11 +299,18 @@ class LvMenu extends FHCAPI_Controller
array_multisort($pos, SORT_ASC, SORT_NUMERIC, $menu);
// HTTP response
// ##########################################################################################
return $menu;
}
/**
*
*/
public function getLvMenu($lvid, $studiensemester_kurzbz)
{
$menu = $this->getLvMenuInternal($lvid, $studiensemester_kurzbz);
$this->terminateWithSuccess($menu);
}
private function fhc_menu_lvinfo(&$menu, $lvid, $studiengang_kz, $lektor_der_lv, $is_lector, $lehrfach_oe_kurzbz_arr){
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,67 @@
<?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 Studiensemester extends FHCAPI_Controller
{
private $_ci;
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getStudiensemester'=> self::PERM_LOGGED,
]);
$this->_ci =& get_instance();
$this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
* GET METHOD
* returns List of all studiensemester as well as current one
*/
public function getStudiensemester()
{
$this->_ci->StudiensemesterModel->addOrder("start", "DESC");
$result = $this->_ci->StudiensemesterModel->load();
$studiensemester = getData($result);
$result = $this->_ci->StudiensemesterModel->getAkt();
$aktuell = getData($result);
$this->terminateWithSuccess(array($studiensemester, $aktuell));
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
}
@@ -0,0 +1,367 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Zeitsperren extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'getZeitsperrenUser' => self::PERM_LOGGED,
'getTypenZeitsperren' => self::PERM_LOGGED,
'getTypenErreichbarkeit' => self::PERM_LOGGED,
'getStunden' => self::PERM_LOGGED,
'loadZeitsperre' => self::PERM_LOGGED,
'add' => self::PERM_LOGGED,
'update' => self::PERM_LOGGED,
'delete' => self::PERM_LOGGED,
]);
// Load Libraries
$this->load->library('VariableLib', ['uid' => getAuthUID()]);
$this->load->library('form_validation');
// Load language phrases
$this->loadPhrases([
'ui',
'person',
'zeitsperren'
]);
// Load models
$this->load->model('ressource/Zeitsperre_model', 'ZeitsperreModel');
$this->load->model('ressource/Zeitsperretyp_model', 'ZeitsperretypModel');
$this->load->model('ressource/Erreichbarkeit_model', 'ErreichbarkeitModel');
$this->load->model('ressource/Stunde_model', 'StundeModel');
$this->load->model('ressource/Zeitaufzeichnung_model', 'ZeitaufzeichnungModel');
}
public function getZeitsperrenUser($uid)
{
//check if $uid is passedUser
$loggedInUser = getAuthUID();
if($loggedInUser != $uid) {
$this->load->library('PermissionLib');
$isAdmin = $this->permissionlib->isBerechtigt('admin');
if(!$isAdmin) {
$this->terminateWithError($this->p->t('ui', 'noAdmin'), self::ERROR_TYPE_GENERAL);
}
}
$result = $this->ZeitsperreModel->getZeitsperrenUser($uid);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$this->terminateWithSuccess((getData($result) ?: []));
}
public function getTypenZeitsperren()
{
$this->ZeitsperretypModel->addOrder('beschreibung', 'ASC');
$result = $this->ZeitsperretypModel->load();
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$this->terminateWithSuccess((getData($result) ?: []));
}
public function getTypenErreichbarkeit()
{
$result = $this->ErreichbarkeitModel->load();
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$this->terminateWithSuccess((getData($result) ?: []));
}
public function getStunden()
{
$this->StundeModel->addOrder('stunde', 'ASC');
$result = $this->StundeModel->load();
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$this->terminateWithSuccess((getData($result) ?: []));
}
public function loadZeitsperre($zeitsperre_id)
{
$this->ZeitsperreModel->addSelect(
'campus.tbl_zeitsperre.*, typ.*,
ma.person_id AS ma_person_id, ma.vorname AS ma_vorname, ma.nachname AS ma_nachname,
ma.titelpre AS ma_titelpre, ma.titelpost AS ma_titelpost'
);
$this->ZeitsperreModel->addJoin('campus.tbl_zeitsperretyp typ', 'ON (typ.zeitsperretyp_kurzbz = campus.tbl_zeitsperre.zeitsperretyp_kurzbz)');
$this->ZeitsperreModel->addJoin('public.tbl_benutzer ben', 'ON (ben.uid = campus.tbl_zeitsperre.vertretung_uid)', 'LEFT');
$this->ZeitsperreModel->addJoin('public.tbl_person ma', 'ON (ma.person_id = ben.person_id)', 'LEFT');
$result = $this->ZeitsperreModel->loadWhere(
array('zeitsperre_id' => $zeitsperre_id)
);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$this->terminateWithSuccess((current(getData($result)) ?: []));
}
public function add($mitarbeiter_uid)
{
$loggedInUser = getAuthUID();
if($mitarbeiter_uid != $loggedInUser)
$this->terminateWithError($this->p->t('ui', 'noPermission'), self::ERROR_TYPE_GENERAL);
$this->form_validation->set_rules('zeitsperretyp_kurzbz', 'Grund Zeitsperre', 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Grund Zeitsperre'])
]);
$this->form_validation->set_rules('vondatum', 'VonDatum', 'required|is_valid_date', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'VonDatum']),
'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field' => 'VonDatum'])
]);
$this->form_validation->set_rules('bisdatum', 'BisDatum', 'required|is_valid_date|callback_check_von_bis_datum|callback_check_diff_intval', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'BisDatum']),
'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field' => 'BisDatum']),
'check_von_bis_datum' => $this->p->t('zeitsperre', 'error_VonDatumGroesserAlsBisDatum'),
'check_diff_intval' => $this->p->t('zeitsperre', 'error_zeitraumAuffallendHoch')
]);
if ($this->form_validation->run() == false)
{
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
$bezeichnung = $this->input->post('bezeichnung');
$vondatum = $this->input->post('vondatum');
$vonstunde = $this->input->post('vonstunde');
$bisdatum = $this->input->post('bisdatum');
$bisstunde = $this->input->post('bisstunde');
//$vonIso = $this->input->post('vonISO'); //Timestamp für Stunde
//$bisIso = $this->input->post('bisISO'); //Timestamp für Stunde
$erreichbarkeit_kurzbz = $this->input->post('erreichbarkeit_kurzbz');
$vertretung_uid = $this->input->post('vertretung_uid');
$zeitsperretyp_kurzbz = $this->input->post('zeitsperretyp_kurzbz');
//check if existing zeitsperre
$result = $this->ZeitsperreModel->getSperreByDate($mitarbeiter_uid, $vondatum, $vonstunde, true);
$data = $this->getDataOrTerminateWithError($result);
if(hasData($result))
{
$this->terminateWithError($this->p->t('zeitsperren', 'error_existingZeitsperre', ['typ'=> current($data)->zeitsperretyp_kurzbz]), self::ERROR_TYPE_GENERAL);
}
//check if existing zeitaufzeichnung
if(in_array($zeitsperretyp_kurzbz, Zeitsperre_model::BLOCKIERENDE_ZEITSPERREN))
{
$result = $this->ZeitsperreModel->existsZeitaufzeichnung($mitarbeiter_uid, $vondatum, $bisdatum);
if(hasData($result))
$this->terminateWithError($this->p->t('zeitsperren', 'error_existingZeitaufzeichnung'), self::ERROR_TYPE_GENERAL);
}
$result = $this->ZeitsperreModel->insert(
[
'mitarbeiter_uid' => $mitarbeiter_uid,
'bezeichnung' => $bezeichnung,
'vondatum' => $vondatum,
'vonstunde' => $vonstunde,
'bisdatum' => $bisdatum,
'bisstunde' => $bisstunde,
'erreichbarkeit_kurzbz' => $erreichbarkeit_kurzbz,
'zeitsperretyp_kurzbz' => $zeitsperretyp_kurzbz,
'vertretung_uid' => $vertretung_uid,
'insertvon' => $loggedInUser,
'insertamum' => date('c'),
]
);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function update($zeitsperre_id)
{
//check if loggedin User is owner of the zeitsperre
$loggedInUser = getAuthUID();
$result = $this->ZeitsperreModel->load($zeitsperre_id);
$data = $this->getDataOrTerminateWithError($result);
$uid = current($data)->mitarbeiter_uid;
if($uid != $loggedInUser)
$this->terminateWithError($this->p->t('ui', 'noPermission'), self::ERROR_TYPE_GENERAL);
if(!$zeitsperre_id)
{
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Zeitsperre_id']), self::ERROR_TYPE_GENERAL);
}
//get current params
$array_update = [
'bezeichnung',
'vondatum',
'vonstunde',
'bisdatum',
'bisstunde',
// 'vonISO', //Timestamp für Stunde
// 'bisISO', //Timestamp für Stunde
'erreichbarkeit_kurzbz',
'vertretung_uid',
'zeitsperretyp_kurzbz',
'mitarbeiter_uid',
];
$post = $this->input->post();
$update = [];
foreach ($array_update as $prop)
{
if (array_key_exists($prop, $post))
{
$update[$prop] = $post[$prop];
}
}
// Validation
$rulesDefined = false; //necessary, otherwise CI validation will always be triggered, even without rules
foreach ($update as $key => $val) {
switch ($key) {
case 'zeitsperretyp_kurzbz':
$this->form_validation->set_rules(
$key,
'Grund Zeitsperre',
'required',
['required' => $this->p->t('ui', 'error_fieldRequired', ['field'=>'Grund Zeitsperre'])]
);
$rulesDefined = true;
break;
case 'vondatum':
$this->form_validation->set_rules(
$key,
'VonDatum',
'required|is_valid_date',
[
'required' => $this->p->t('ui', 'error_fieldRequired', ['field'=>'VonDatum']),
'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field'=>'VonDatum'])
]
);
$rulesDefined = true;
break;
case 'bisdatum':
$rules = 'required|is_valid_date';
if (array_key_exists('vondatum', $update)) {
$rules .= '|callback_check_von_bis_datum|callback_check_diff_intval';
}
$this->form_validation->set_rules(
$key,
'BisDatum',
$rules,
[
'required' => $this->p->t('ui', 'error_fieldRequired', ['field'=>'BisDatum']),
'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field'=>'BisDatum']),
'check_von_bis_datum' => $this->p->t('zeitsperre', 'error_VonDatumGroesserAlsBisDatum'),
'check_diff_intval' => $this->p->t('zeitsperre', 'error_zeitraumAuffallendHoch')
]
);
$rulesDefined = true;
break;
}
}
if ($rulesDefined && $this->form_validation->run() == false) {
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
if(array_key_exists('vondatum', $post) || array_key_exists('bisdatum', $post))
{
$result = $this->ZeitsperreModel->load($zeitsperre_id);
$data = $this->getDataOrTerminateWithError($result);
$data = current($data);
$mitarbeiter_uid = array_key_exists('mitarbeiter_uid', $post) ? $update['mitarbeiter_uid'] : $data->mitarbeiter_uid;
$vondatum = array_key_exists('vondatum', $post) ? $update['vondatum'] : $data->vondatum;
$bisdatum = array_key_exists('bisdatum', $post) ? $update['bisdatum'] : $data->bisdatum;
$vonstunde = array_key_exists('vonstunde', $post) ? $update['vonstunde'] : $data->vonstunde;
$zeitsperretyp_kurzbz = array_key_exists('zeitsperretyp_kurzbz', $post) ? $update['zeitsperretyp_kurzbz'] : $data->zeitsperretyp_kurzbz;
$result = $this->ZeitsperreModel->getSperreByDate($mitarbeiter_uid, $vondatum, $vonstunde, true);
$data = $this->getDataOrTerminateWithError($result);
if(hasData($result))
{
$this->terminateWithError($this->p->t('zeitsperren', 'error_existingZeitsperre', ['typ'=> current($data)->zeitsperretyp_kurzbz]), self::ERROR_TYPE_GENERAL);
}
//check if existing zeitaufzeichnung
if(in_array($zeitsperretyp_kurzbz, Zeitsperre_model::BLOCKIERENDE_ZEITSPERREN))
{
$result = $this->ZeitsperreModel->existsZeitaufzeichnung($mitarbeiter_uid, $vondatum, $bisdatum);
if(hasData($result))
$this->terminateWithError($this->p->t('zeitsperren', 'error_existingZeitaufzeichnung'), self::ERROR_TYPE_GENERAL);
}
}
if (!empty($update)) {
$update['updatevon'] = $loggedInUser;
$update['updateamum'] = date('c');
$result = $this->ZeitsperreModel->update($zeitsperre_id, $update);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
else
$this->terminateWithSuccess("no update");
}
public function delete($zeitsperre_id)
{
if (!is_numeric($zeitsperre_id) || (int)$zeitsperre_id <= 0)
{
$this->terminateWithError($this->p->t('ui', 'error_missingId', ['id' => 'Zeitsperre_id']), self::ERROR_TYPE_GENERAL);
}
//check if loggedin User is owner of the zeitsperre
$loggedInUser = getAuthUID();
$result = $this->ZeitsperreModel->load($zeitsperre_id);
$data = $this->getDataOrTerminateWithError($result);
$uid = current($data)->mitarbeiter_uid;
if($uid != $loggedInUser)
$this->terminateWithError($this->p->t('ui', 'noPermission'), self::ERROR_TYPE_GENERAL);
$result = $this->ZeitsperreModel->delete(
array('zeitsperre_id' => $zeitsperre_id)
);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$this->terminateWithSuccess((getData($result) ?: []));
}
public function check_von_bis_datum($bisdatum)
{
$vondatum = $this->input->post('vondatum');
return $vondatum <= $bisdatum;
}
public function check_diff_intval($bisdatum)
{
$vondatum = $this->input->post('vondatum');
// Intervall in days
$vonTs = strtotime($vondatum);
$bisTs = strtotime($bisdatum);
$tage = ($bisTs - $vonTs) / 86400;
// if intervall > 14
return $tage <= 14;
}
}
@@ -0,0 +1,232 @@
<?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 RoomPlan extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'addRoomReservation' => self::PERM_LOGGED,
'deleteRoomReservation' => self::PERM_LOGGED,
'getRoomCreationInfo' => self::PERM_LOGGED,
'getGruppen' => self::PERM_LOGGED,
'getLektor' => self::PERM_LOGGED,
'getReservableMap' => self::PERM_LOGGED,
]);
$this->load->library('LogLib');
$this->loglib->setConfigs(array(
'classIndex' => 5,
'functionIndex' => 5,
'lineIndex' => 4,
'dbLogType' => 'API',
'dbExecuteUser' => 'RESTful API'
));
$this->load->library('form_validation');
$this->load->library('PermissionLib');
$this->load->library('StundenplanLib');
$this->loadPhrases(['ui']);
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function addRoomReservation()
{
$this->form_validation->set_rules('selectedStart', "Start", "required");
$this->form_validation->set_rules('selectedEnd', "End", "required");
$this->form_validation->set_rules('title', "Title", "required|max_length[10]");
$this->form_validation->set_rules('beschreibung', "Beschreibung", "required|max_length[32]");
$this->form_validation->set_rules('ort_kurzbz', "Ort", "required|max_length[16]");
$this->form_validation->set_rules('studiengang', 'Studiengang', 'numeric');
$this->form_validation->set_rules('semester', 'Semester', 'integer|greater_than_equal_to[0]');
$this->form_validation->set_rules('verband', 'Verband', 'trim');
$this->form_validation->set_rules('gruppe', 'Gruppe', 'trim');
$this->form_validation->set_rules('spezialgruppe', 'Spezialgruppe', 'max_length[32]');
$this->form_validation->set_rules('lektoren', 'Lektoren');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$start = $this->input->post('selectedStart');
$end = $this->input->post('selectedEnd');
$title = $this->input->post('title');
$beschreibung = $this->input->post('beschreibung');
$ort_kurzbz = $this->input->post('ort_kurzbz');
$studiengang_kz = $this->input->post('studiengang');
$semester = $this->input->post('semester');
$verband = $this->input->post('verband');
$gruppe = $this->input->post('gruppe');
$spezialgruppe = $this->input->post('spezialgruppe');
$lektoren = $this->input->post('lektoren');
$result = $this->stundenplanlib->addReservation($start, $end, $title, $beschreibung, $ort_kurzbz, $lektoren, $studiengang_kz, $semester, $verband, $gruppe, $spezialgruppe);
if (isError($result))
$this->terminateWithError($result);
$this->terminateWithSuccess($result);
}
public function deleteRoomReservation()
{
$reservierung_id = $this->input->post('reservierung_id');
$result = $this->stundenplanlib->deleteReservation($reservierung_id);
if (isError($result))
$this->terminateWithError($result);
$this->terminateWithSuccess($result);
}
public function getRoomCreationInfo()
{
$return_array = array('berechtigt' => false, 'studiengaenge' => []);
if (!$this->permissionlib->isBerechtigt('lehre/reservierung'))
$this->terminateWithSuccess($return_array);
$stg_berechtigungen = $this->permissionlib->getSTG_isEntitledFor('lehre/reservierung');
if (isEmptyArray($stg_berechtigungen))
$this->terminateWithSuccess($return_array);
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$this->StudiengangModel->addSelect('studiengang_kz, UPPER(CONCAT(typ, kurzbz)) as kuerzel, kurzbzlang');
$this->StudiengangModel->addOrder('typ, kurzbz');
$this->StudiengangModel->db->where_in('studiengang_kz', $stg_berechtigungen);
$studiengaenge = $this->StudiengangModel->loadWhere(array('aktiv' => true));
if (isError($studiengaenge))
$this->terminateWithError($studiengaenge);
$return_array['studiengaenge'] = hasData($studiengaenge) ? getData($studiengaenge) : [];
$return_array['berechtigt'] = true;
$this->terminateWithSuccess($return_array);
}
public function getGruppen()
{
$query = $this->input->get('query');
if (is_null($query))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$stg_berechtigungen = $this->permissionlib->getSTG_isEntitledFor('lehre/reservierung');
if (isEmptyArray($stg_berechtigungen))
$this->terminateWithSuccess([]);
$this->load->model('organisation/gruppe_model', 'GruppeModel');
$query_words = explode(' ', urldecode($query));
$this->GruppeModel->addOrder('gruppe_kurzbz');
$this->GruppeModel->db->group_start();
foreach ($query_words as $word)
{
$this->GruppeModel->db->group_start();
$this->GruppeModel->db->where('gruppe_kurzbz ILIKE', "%" . $word . "%");
$this->GruppeModel->db->or_where('bezeichnung ILIKE', "%" . $word . "%");
$this->GruppeModel->db->or_where('beschreibung ILIKE', "%" . $word . "%");
$this->GruppeModel->db->or_where('orgform_kurzbz ILIKE', "%" . $word . "%");
if (is_numeric($word))
{
$this->GruppeModel->db->or_where('studiengang_kz', $word);
}
$this->GruppeModel->db->group_end();
}
$this->GruppeModel->db->group_end();
$this->GruppeModel->db->where_in('studiengang_kz', $stg_berechtigungen);
$gruppen = $this->GruppeModel->loadWhere(array('sichtbar' => true, 'lehre' => true));
if (isError($gruppen))
$this->terminateWithError($gruppen);
$this->terminateWithSuccess(hasData($gruppen) ? getData($gruppen) : []);
}
public function getLektor()
{
$query = $this->input->get('query');
if (is_null($query))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$stg_berechtigungen = $this->permissionlib->getSTG_isEntitledFor('lehre/reservierung');
if (isEmptyArray($stg_berechtigungen))
$this->terminateWithSuccess([]);
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$query_words = explode(' ', urldecode($query));
$this->MitarbeiterModel->addSelect('uid, person_id, vorname, nachname');
$this->MitarbeiterModel->addJoin('public.tbl_benutzer', 'uid = mitarbeiter_uid');
$this->MitarbeiterModel->addJoin('public.tbl_person', 'person_id');
$this->MitarbeiterModel->db->where('public.tbl_benutzer.aktiv', true);
$this->MitarbeiterModel->db->group_start();
foreach ($query_words as $word)
{
$this->MitarbeiterModel->db->group_start();
$this->MitarbeiterModel->db->where('tbl_person.vorname ILIKE', "%" . $word . "%");
$this->MitarbeiterModel->db->or_where('tbl_person.nachname ILIKE', "%" . $word . "%");
$this->MitarbeiterModel->db->or_where('uid ILIKE', "%" . $word . "%");
$this->MitarbeiterModel->db->group_end();
}
$this->MitarbeiterModel->db->group_end();
$this->MitarbeiterModel->addOrder('nachname');
$this->MitarbeiterModel->addOrder('vorname');
$mitarbeiter = $this->MitarbeiterModel->load();
if (isError($mitarbeiter))
$this->terminateWithError($mitarbeiter);
$this->terminateWithSuccess(hasData($mitarbeiter) ? getData($mitarbeiter) : []);
}
public function getReservableMap($ort_kurzbz = null)
{
$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);
$result = $this->stundenplanlib->getReservableMap($ort_kurzbz, $start_date, $end_date);
$this->terminateWithSuccess(array('reservierbarMap' => hasData($result) ? getData($result) : []));
}
}
@@ -9,9 +9,10 @@ class Detailheader extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getHeader' => ['vertrag/mitarbeiter:r'],
'getPersonAbteilung' => ['vertrag/mitarbeiter:r'],
'getLeitungOrg' => ['vertrag/mitarbeiter:r'],
'getHeader' => self::PERM_LOGGED,
'getPersonAbteilung' => self::PERM_LOGGED,
'getLeitungOrg' => self::PERM_LOGGED,
'getSemesterStati' => self::PERM_LOGGED,
]);
}
@@ -48,6 +49,17 @@ class Detailheader extends FHCAPI_Controller
$this->terminateWithSuccess(current($data));
}
public function getSemesterStati($prestudent_id)
{
$this->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel');
$result = $this->PrestudentstatusModel->getAllPrestudentstatiWithStudiensemester($prestudent_id);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
}
@@ -0,0 +1,218 @@
<?php
/**
* FH-Complete
*
* @package FHC-API
* @author FHC-Team
* @copyright Copyright (c) 2016, fhcomplete.org
* @license GPLv3
* @link http://fhcomplete.org
* @since Version 1.0
* @filesource
*/
// ------------------------------------------------------------------------
if (!defined('BASEPATH')) exit('No direct script access allowed');
use CI3_Events as Events;
class PaabgabeUebersicht extends FHCAPI_Controller
{
const DOWNLOAD_PERMISSION = 'lehre/abgabetool:download';
const ABGABE_TYPES = ['Bachelor', 'Diplom'];
/**
* PaabgabeUebersicht API constructor.
*/
public function __construct()
{
parent::__construct([
'viewData' => self::PERM_LOGGED,
'getPaAbgaben' => array('lehre/abgabetool:r'),
'getStudiengaenge' => array('lehre/abgabetool:r'),
'getTermine' => array('lehre/abgabetool:r'),
'getPaAbgabetypen' => array('lehre/abgabetool:r'),
'downloadZip' => array('lehre/abgabetool:r'),
//'downloadProjektarbeit' => array('lehre/abgabetool:r')
]);
$this->load->model('education/Paabgabe_model', 'PaabgabeModel');
$this->load->library('PermissionLib');
// Load Phrases
$this->loadPhrases([
'abgabetool'
]);
}
public function viewData()
{
$viewData = [
"uid" => getAuthUID(),
// TODO create permission
"showEdit" => true,
];
$this->terminateWithSuccess($viewData);
}
/**
* Get Projektabgaben for search criteria.
*/
public function getPaAbgaben()
{
$studiengang_kz = $this->input->get('studiengang_kz');
$abgabetyp_kurzbz = $this->input->get('abgabetyp_kurzbz');
$abgabedatum = $this->input->get('abgabedatum');
$personSearchString = $this->input->get('personSearchString');
$result = $this->PaabgabeModel->getPaAbgaben(self::ABGABE_TYPES, $studiengang_kz, $abgabetyp_kurzbz, $abgabedatum, $personSearchString);
if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_DB);
// check wether Abgabe is in visual library
if (hasData($result))
{
Events::trigger('in_visual_library', getData($result));
}
$this->terminateWithSuccess(getData($result) ?: []);
}
/**
* Get all Studiengänge for which user is entitled for
*/
public function getStudiengaenge()
{
$studiengang_kz_arr = $this->permissionlib->getSTG_isEntitledFor(self::DOWNLOAD_PERMISSION);
if (!$studiengang_kz_arr) $this->terminateWithSuccess([]);
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$this->StudiengangModel->addSelect('tbl_studiengang.*, UPPER(tbl_studiengang.typ || tbl_studiengang.kurzbz) AS kuerzel', $studiengang_kz_arr);
$this->StudiengangModel->db->where_in('studiengang_kz', $studiengang_kz_arr);
$this->StudiengangModel->addOrder('typ, kurzbz');
$result = $this->StudiengangModel->load();
if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_DB);
$this->terminateWithSuccess((getData($result) ?: []));
}
/**
* Get projekt work due dates, depending on search criteria.
*/
public function getTermine()
{
$studiengang_kz = $this->input->get('studiengang_kz');
$abgabetyp_kurzbz = $this->input->get('abgabetyp_kurzbz');
$result = $this->PaabgabeModel->getTermine(self::ABGABE_TYPES, $studiengang_kz, $abgabetyp_kurzbz);
if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_DB);
$this->terminateWithSuccess((getData($result) ?: []));
}
/**
* Get all submission types.
*/
public function getPaAbgabetypen()
{
// Load model PaabgabetypModel
$this->load->model('education/Paabgabetyp_model', 'PaabgabetypModel');
$this->PaabgabetypModel->addOrder('bezeichnung');
$result = $this->PaabgabetypModel->load();
if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_DB);
$this->terminateWithSuccess((getData($result) ?: []));
}
/**
* Download zip files with project works matching submission search criteria.
*/
public function downloadZip()
{
$studiengang_kz = $this->input->get('studiengang_kz');
$abgabetyp_kurzbz = $this->input->get('abgabetyp_kurzbz');
$abgabedatum = $this->input->get('abgabedatum');
$personSearchString = $this->input->get('personSearchString');
if (!isset($studiengang_kz) && !isset($abgabetyp_kurzbz) && !isset($abgabedatum) && !isset($personSearchString))
$this->terminateWithFileOutput('text/plain', $this->p->t('abgabetool', 'nichtsAusgewaehlt'));
$this->load->library('zip');
$result = $this->PaabgabeModel->getPaAbgaben(self::ABGABE_TYPES, $studiengang_kz, $abgabetyp_kurzbz, $abgabedatum, $personSearchString);
if (isError($result)) $this->terminateWithFileOutput('text/plain', getError($result));
$fileExists = false;
$studiengang_kuerzel = null;
if (!hasData($result)) $this->terminateWithFileOutput('text/plain', $this->p->t('abgabetool', 'keineDateienVorhanden'));
$abgaben = getData($result);
foreach ($abgaben as $abgabe)
{
$path = PAABGABE_PATH.$abgabe->paabgabe_id.'_'.$abgabe->uid.'.pdf';
if (file_exists($path))
{
$fileExists = true;
$studiengang_kuerzel = $abgabe->studiengang_kuerzel;
$this->zip->read_file($path);
}
}
if (!$fileExists) $this->terminateWithFileOutput('text/plain', $this->p->t('abgabetool', 'keineDateienVorhanden'));
$studiengang_kz = $this->input->get('studiengang_kz');
$zipFileName = 'Abgabe'.(isset($studiengang_kz) && isset($studiengang_kuerzel) ? '_'.$studiengang_kuerzel : '').'.zip';
$this->zip->download($zipFileName);
}
/**
* Download Projektarbeit document.
*/
//~ public function downloadProjektarbeit()
//~ {
//~ $paabgabe_id = $this->input->get('paabgabe_id');
//~ if (!is_numeric($paabgabe_id))
//~ $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id' => 'Abgabe ID']), self::ERROR_TYPE_GENERAL);
//~ //$abgabeRes = $this->PaabgabeModel->getEndabgabe($projektarbeit_id);
//~ $this->PaabgabeModel->addSelect("paabgabe_id, student_uid, tbl_paabgabe.datum, tbl_paabgabe.abgabedatum, projekttyp_kurzbz, titel, titel_english,
//~ paabgabe_id || '_' || student_uid || '.pdf' AS filename");
//~ $this->PaabgabeModel->addJoin('lehre.tbl_projektarbeit', 'projektarbeit_id');
//~ $abgabeRes = $this->PaabgabeModel->load($paabgabe_id);
//~ if (isError($abgabeRes))
//~ show_error(getError($abgabeRes));
//~ if (hasData($abgabeRes))
//~ {
//~ $endabgabe = getData($abgabeRes)[0];
//~ $filepath = PAABGABE_PATH.$endabgabe->filename;
//~ if (file_exists($filepath))
//~ {
//~ $this->output
//~ ->set_status_header(200)
//~ ->set_content_type('application/pdf', 'utf-8')
//~ ->set_header('Content-Disposition: attachment; filename="'.$endabgabe->filename.'"')
//~ ->set_output(file_get_contents($filepath))
//~ ->_display();
//~ }
//~ else
//~ {
//~ show_error("File does not exist.");
//~ }
//~ }
//~ }
}
@@ -246,12 +246,12 @@ class Abschlusspruefung extends FHCAPI_Controller
{
$searchString = $this->input->get('searchString') ?? '';
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$this->load->model('person/Person_model', 'PersonModel');
$result = $this->MitarbeiterModel->searchMitarbeiter($searchString, 'ohneMaUid');
$result = $this->PersonModel->searchPerson($searchString, 'mitMaUid');
if (isError($result)) {
$this->terminateWithError($result, self::ERROR_TYPE_GENERAL);
$this->terminateWithError($result, self::ERROR_TYPE_GENERAL);
}
$this->terminateWithSuccess($result ?: []);
@@ -90,6 +90,15 @@ class Projektarbeit extends FHCAPI_Controller
if (!isset($projektarbeit_id) || !is_numeric($projektarbeit_id)) return $this->terminateWithError('Projektarbeit Id missing', self::ERROR_TYPE_GENERAL);
$result = $this->fetchProjektarbeitByID($projektarbeit_id);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(current($data));
}
private function fetchProjektarbeitById($projektarbeit_id) {
$this->ProjektarbeitModel->resetQuery();
$this->ProjektarbeitModel->addSelect(
'lehre.tbl_projektarbeit.projektarbeit_id, titel, titel_english, themenbereich, projekttyp_kurzbz, lehrveranstaltung_id, lehreinheit_id,
firma_id, beginn, ende, gesperrtbis, note, final, freigegeben, tbl_projektarbeit.anmerkung, fa.name AS firma_name'
@@ -97,13 +106,10 @@ class Projektarbeit extends FHCAPI_Controller
$this->ProjektarbeitModel->addJoin('lehre.tbl_lehreinheit le', 'lehreinheit_id');
$this->ProjektarbeitModel->addJoin('lehre.tbl_lehrveranstaltung lv', 'lehrveranstaltung_id');
$this->ProjektarbeitModel->addJoin('public.tbl_firma fa', 'firma_id', 'LEFT');
$result = $this->ProjektarbeitModel->loadWhere(
return $this->ProjektarbeitModel->loadWhere(
array('projektarbeit_id' => $projektarbeit_id)
);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(current($data));
}
/**
@@ -132,7 +138,8 @@ class Projektarbeit extends FHCAPI_Controller
);
$data = $this->getDataOrTerminateWithError($result);
$data = $this->getDataOrTerminateWithError($this->fetchProjektarbeitById($data));
$this->terminateWithSuccess($data);
}
@@ -144,6 +144,7 @@ class Student extends FHCAPI_Controller
. $this->PrestudentModel->escape($studiensemester_kurzbz)
. ") AS statusofsemester"
);
$this->PrestudentModel->addSelect($this->PrestudentModel->escape($studiensemester_kurzbz) . ' as query_studiensemester_kurzbz');
$this->PrestudentModel->addJoin('public.tbl_student s', 'prestudent_id', 'LEFT');
$this->PrestudentModel->addJoin('public.tbl_benutzer b', 'student_uid = uid', 'LEFT');
@@ -147,7 +147,7 @@ class Students extends FHCAPI_Controller
$data = $this->getDataOrTerminateWithError($result);
$this->decodeTagsJsonInResult($data);
$this->terminateWithSuccess($data);
}
@@ -214,7 +214,7 @@ class Students extends FHCAPI_Controller
$data = $this->getDataOrTerminateWithError($result);
$this->decodeTagsJsonInResult($data);
$this->terminateWithSuccess($data);
}
@@ -267,7 +267,7 @@ class Students extends FHCAPI_Controller
$data = $this->getDataOrTerminateWithError($result);
$this->decodeTagsJsonInResult($data);
$this->terminateWithSuccess($data);
}
@@ -468,13 +468,15 @@ class Students extends FHCAPI_Controller
$this->PrestudentModel->addSelect("'' AS verband");
$this->PrestudentModel->addSelect("'' AS gruppe");
$this->addSelectPrioRel();
$query_studiensemester_kurzbz = $studiensemester_kurzbz ? $this->PrestudentModel->escape($studiensemester_kurzbz) : '\'NULL\'';
$this->PrestudentModel->addSelect($query_studiensemester_kurzbz . ' as query_studiensemester_kurzbz');
$this->addFilter($studiensemester_kurzbz);
$result = $this->PrestudentModel->loadWhere($where);
$data = $this->getDataOrTerminateWithError($result);
$this->decodeTagsJsonInResult($data);
$this->terminateWithSuccess($data);
}
@@ -588,6 +590,7 @@ class Students extends FHCAPI_Controller
$this->PrestudentModel->addSelect('v.verband');
$this->PrestudentModel->addSelect('v.gruppe');
$this->PrestudentModel->addSelect("'' AS priorisierung_relativ");
$this->PrestudentModel->addSelect($this->PrestudentModel->escape($studiensemester_kurzbz) . ' as query_studiensemester_kurzbz');
$where = [];
@@ -628,10 +631,22 @@ class Students extends FHCAPI_Controller
$result = $this->PrestudentModel->loadWhere($where);
$data = $this->getDataOrTerminateWithError($result);
$this->decodeTagsJsonInResult($data);
$this->terminateWithSuccess($data);
}
protected function decodeTagsJsonInResult(&$data)
{
if(defined('STV_TAGS_ENABLED') && STV_TAGS_ENABLED) {
array_walk($data, function($item, $key) {
if(isset($item->tags))
{
$item->tags = json_decode($item->tags);
}
});
}
}
/**
* @param string $prestudent_id
*
@@ -676,7 +691,7 @@ class Students extends FHCAPI_Controller
]);
$data = $this->getDataOrTerminateWithError($result);
$this->decodeTagsJsonInResult($data);
$this->terminateWithSuccess($data);
}
@@ -719,7 +734,7 @@ class Students extends FHCAPI_Controller
]);
$data = $this->getDataOrTerminateWithError($result);
$this->decodeTagsJsonInResult($data);
$this->terminateWithSuccess($data);
}
@@ -761,7 +776,7 @@ class Students extends FHCAPI_Controller
]);
$data = $this->getDataOrTerminateWithError($result);
$this->decodeTagsJsonInResult($data);
$this->terminateWithSuccess($data);
}
@@ -798,6 +813,7 @@ class Students extends FHCAPI_Controller
$this->PrestudentModel->addSelect("COALESCE(v.semester::text, CASE WHEN public.get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL) IN ('Aufgenommener', 'Bewerber', 'Wartender', 'interessent') THEN public.get_absem_prestudent(tbl_prestudent.prestudent_id, NULL)::text ELSE ''::text END) AS semester", false);
$this->PrestudentModel->addSelect('v.verband');
$this->PrestudentModel->addSelect('v.gruppe');
$this->PrestudentModel->addSelect($this->PrestudentModel->escape($studiensemester_kurzbz) . ' as query_studiensemester_kurzbz');
//add status per semester
$this->PrestudentModel->addSelect(
@@ -836,7 +852,7 @@ class Students extends FHCAPI_Controller
$result = $this->PrestudentModel->load();
$data = $this->getDataOrTerminateWithError($result);
$this->decodeTagsJsonInResult($data);
$this->terminateWithSuccess($data);
}
@@ -878,12 +894,31 @@ class Students extends FHCAPI_Controller
n.text AS notiz,
nt.style,
n.erledigt AS done,
nz.prestudent_id
nz.prestudent_id,
n.start,
n.ende
FROM public.tbl_notizzuordnung AS nz
JOIN public.tbl_notiz AS n ON nz.notiz_id = n.notiz_id
JOIN public.tbl_notiz AS n ON nz.notiz_id = n.notiz_id AND nz.prestudent_id IS NOT NULL
JOIN public.tbl_notiz_typ AS nt ON n.typ = nt.typ_kurzbz "
. $whereTags .
"
WHERE
COALESCE(n.start, '1970-01-01') <= (
SELECT
ende
FROM
public.tbl_studiensemester
WHERE
studiensemester_kurzbz = '{$studiensemester_kurzbz}'
)
AND COALESCE(n.ende, '2170-12-31') >= (
SELECT
start
FROM
public.tbl_studiensemester
WHERE
studiensemester_kurzbz = '{$studiensemester_kurzbz}'
)
) AS tag
GROUP BY tag.prestudent_id
) AS tag_data_agg
@@ -953,6 +988,13 @@ class Students extends FHCAPI_Controller
$this->PrestudentModel->addSelect('pls.status_kurzbz AS status');
$this->PrestudentModel->addSelect('pls.datum AS status_datum');
$this->PrestudentModel->addSelect('pls.bestaetigtam AS status_bestaetigung');
$this->PrestudentModel->addSelect("
CASE
WHEN pls.status_kurzbz = 'Interessent'
THEN pls.ausbildungssemester
ELSE s.semester
END AS semester_berechnet
");
$this->PrestudentModel->addSelect(
"(SELECT kontakt FROM public.tbl_kontakt WHERE kontakttyp='email' AND person_id=p.person_id AND zustellung LIMIT 1) AS mail_privat",
false
@@ -45,4 +45,5 @@ class Tags extends Tag_Controller
{
parent::doneTag($this->config->item('stv_prestudent_tags'));
}
}
+42 -13
View File
@@ -13,12 +13,13 @@ class Mylv extends Auth_Controller
*/
public function __construct()
{
parent::__construct([
'Student' => ['student/anrechnung_beantragen:r','user:r'], // TODO(chris): permissions?
'Studiensemester' => ['student/anrechnung_beantragen:r','user:r'], // TODO(chris): permissions?
'Lvs' => ['student/anrechnung_beantragen:r','user:r'], // TODO(chris): permissions?
'Info' => ['student/anrechnung_beantragen:r','user:r'], // TODO(chris): permissions?
'Pruefungen' => ['student/anrechnung_beantragen:r','user:r'] // TODO(chris): permissions?
'Student' => ['student/anrechnung_beantragen:r','user:r', 'basis/cis:r'], // TODO(chris): permissions?
'Studiensemester' => ['student/anrechnung_beantragen:r','user:r', 'basis/cis:r'], // TODO(chris): permissions?
'Lvs' => ['student/anrechnung_beantragen:r','user:r', 'basis/cis:r'], // TODO(chris): permissions?
'Info' => ['student/anrechnung_beantragen:r','user:r', 'basis/cis:r'], // TODO(chris): permissions?
'Pruefungen' => ['student/anrechnung_beantragen:r','user:r', 'basis/cis:r'] // TODO(chris): permissions?
]);
}
@@ -44,13 +45,27 @@ class Mylv extends Auth_Controller
public function Studiensemester()
{
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$this->load->model('crm/Student_model', 'StudentModel');
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$result = $this->StudiensemesterModel->getWhereStudentHasLvs(getAuthUID());
$isMitarbeiter = getData($this->MitarbeiterModel->isMitarbeiter(getAuthUID())) ?? false;
if($isMitarbeiter) {
$result = $this->StudiensemesterModel->getWhereMitarbeiterHasLvs(getAuthUID());
if (isError($result))
return $this->outputJsonError(getError($result));
if (isError($result))
return $this->outputJsonError(getError($result));
$this->outputJsonSuccess(getData($result));
$this->outputJsonSuccess(getData($result));
} else if(getData($this->StudentModel->isStudent(getAuthUID())) ?? false) { // $isStudent
$result = $this->StudiensemesterModel->getWhereStudentHasLvs(getAuthUID());
if (isError($result))
return $this->outputJsonError(getError($result));
$this->outputJsonSuccess(getData($result));
} else {
$this->outputJsonError('neither student or mitarbeiter');
}
}
/**
@@ -58,13 +73,27 @@ class Mylv extends Auth_Controller
public function Lvs($studiensemester_kurzbz)
{
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->load->model('crm/Student_model', 'StudentModel');
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$result = $this->LehrveranstaltungModel->getLvsByStudentWithGrades(getAuthUID(), $studiensemester_kurzbz, getUserLanguage());
$isMitarbeiter = getData($this->MitarbeiterModel->isMitarbeiter(getAuthUID())) ?? false;
if($isMitarbeiter) {
$result = $this->LehrveranstaltungModel->getLvsByMitarbeiterInSemester(getAuthUID(), $studiensemester_kurzbz);
if (isError($result))
return $this->outputJsonError(getError($result));
if (isError($result))
return $this->outputJsonError(getError($result));
$this->outputJsonSuccess(getData($result));
$this->outputJsonSuccess(getData($result));
} else if(getData($this->StudentModel->isStudent(getAuthUID())) ?? false) { // $isStudent
$result = $this->LehrveranstaltungModel->getLvsByStudentWithGrades(getAuthUID(), $studiensemester_kurzbz, getUserLanguage());
if (isError($result))
return $this->outputJsonError(getError($result));
$this->outputJsonSuccess(getData($result));
} else {
$this->outputJsonError('neither student or mitarbeiter');
}
}
/**
+117
View File
@@ -0,0 +1,117 @@
<?php
if (!defined("BASEPATH")) exit("No direct script access allowed");
use \DateTime as DateTime;
class TagJob extends JOB_Controller
{
const BATCHUSER = 'sftest';
/**
* API constructor
*/
public function __construct()
{
parent::__construct();
// Configs
$this->load->config('stv');
// Library
$this->load->library('TagLib');
// Load Models
$this->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel');
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$this->load->model('system/Notiztyp_model', 'NotiztypModel');
$this->loadPhrases([
'lehre'
]);
}
public function rebuildAutomatedTags()
{
$automatedTagsRes = $this->NotiztypModel->loadWhere(array('automatisiert' => true, 'taglib IS NOT NULL' => null));
$automatedTags = hasData($automatedTagsRes) ? getData($automatedTagsRes) : [];
$result = $this->StudiensemesterModel->getAktOrNextSemester();
if (isError($result))
{
$this->logInfo('Start Job rebuild Automated Tags');
$this->logError('Error occurred during retrieving studiensemester');
return $this->logInfo('End Job rebuild Automated Tags');
}
if (empty($result->retval) || !isset($result->retval[0])) {
$this->logInfo('Start Job rebuild Automated Tags');
$this->logError('No Studiensemester found');
return $this->logInfo('End Job rebuild Automated Tags');
}
$studiensemester_kurzbz = $result->retval[0]->studiensemester_kurzbz ?? null;
$params = array(
'studiensemester_kurzbz' => $studiensemester_kurzbz
);
$this->logInfo('Start Job rebuild Automated Tags ' . $studiensemester_kurzbz);
foreach($automatedTags as $autoTag)
{
// getPath: must not be lost
$filePath = APPPATH . 'libraries/' . $autoTag->taglib . '.php'; // APPPATH = application/
if(file_exists($filePath)) {
require_once($filePath);
} else {
$this->logInfo("File not found: " . $filePath);
continue;
}
$kurz_bz = $autoTag->typ_kurzbz;
// className without PATH (basename)
$className = basename($autoTag->taglib);
$obj = new $className();
$outputArray = $obj->getZuordnungIds($params);
$typeId = $outputArray->typeId;
$paramsTag = array(
'studiensemester_kurzbz' => $studiensemester_kurzbz,
'kurzbz' => $kurz_bz,
'data' => $outputArray->data,
'typeId' => $typeId
);
$result = $this->taglib->updateAutomatedTags($paramsTag);
if (isError($result)) {
$this->logError('Error occurred during updateAutomatedTags ' . $kurz_bz);
continue;
}
$data = is_array($result) ? $result['retval'] : $result->retval;
//SUMMARY
$this->logInfo("Tag " . $result->retval['input']['tag'] . " | type_id " . $typeId . " --"
. " Count Recycled: " . $result->retval['summary']['recycled']
. " Count Added: ". $result->retval['summary']['added']
. " Count Deleted: ". $result->retval['summary']['deleted']);
//DETAILS
if($result->retval['results']['newTags'])
$this->logInfo("Tag " . $result->retval['input']['tag'] . "New tag(s): " . implode(', ', $result->retval['results']['newTags']));
if($result->retval['results']['deletedTagsIds'])
$this->logInfo("Tag " . $result->retval['input']['tag'] . "Deleted tags(s: " . implode(', ', $result->retval['results']['deletedTagsIds']));
if ($result->retval['results']['retaggedIds'])
$this->logInfo("Tag " . $result->retval['input']['tag'] . "Recycled tag(s): " . implode(', ', $result->retval['results']['retaggedIds']));
}
$this->logInfo( "End Job rebuild Automated Tags");
}
}
@@ -215,8 +215,11 @@ class Pruefungsprotokoll extends Auth_Controller
if (hasData($abschlusspruefung))
{
$abschlusspruefung_data = getData($abschlusspruefung);
if ($this->permissionlib->isBerechtigt('admin') ||
(isset($abschlusspruefung_data->studiengang_kz) && $this->permissionlib->isBerechtigt('assistenz', 'suid', $abschlusspruefung_data->studiengang_kz))
if ($this->permissionlib->isBerechtigt('admin')
|| (
isset($abschlusspruefung_data->studiengang_kz)
&& $this->permissionlib->isBerechtigt('assistenz', 'suid', $abschlusspruefung_data->studiengang_kz)
)
|| $this->_uid === $abschlusspruefung_data->uid_vorsitz)
$result = $abschlusspruefung;
else
+15 -3
View File
@@ -198,7 +198,19 @@ class Gradelist extends Auth_Controller
if (!isset($row_noten->found))
{
$result_lv = $this->LehrveranstaltungModel->load($row_noten->lehrveranstaltung_id);
$result_stg = $this->StudiengangModel->load($result_lv->retval[0]->studiengang_kz);
$studiengang_kz = null;
if (!empty($result_lv->retval) && isset($result_lv->retval[0]) && isset($result_lv->retval[0]->studiengang_kz))
{
$result_stg = $this->StudiengangModel->load($result_lv->retval[0]->studiengang_kz);
if (!empty($result_stg->retval) && isset($result_stg->retval[0]) && is_object($result_stg->retval[0]) && isset($result_stg->retval[0]->kurzbzlang))
{
$studiengang_kz = $result_stg->retval[0]->kurzbzlang;
}
}
$courses['semester'][$row_noten->studiensemester_kurzbz]['lvs_nonstpl'][] = array(
'lehrveranstaltung_id' => $row_noten->lehrveranstaltung_id,
'lehrtyp_kurzbz' => $result_lv->retval[0]->lehrtyp_kurzbz,
@@ -212,8 +224,8 @@ class Gradelist extends Auth_Controller
'semester' => $result_lv->retval[0]->semester,
'note' => $row_noten->note,
'datum' => $row_noten->benotungsdatum,
'zugeordnet' => true,
'studiengang_kurzbz' => $result_stg->retval[0]->kurzbzlang
'studiengang_kurzbz' => $studiengang_kz,
'zugeordnet' => true
);
if(!isset($courses['semester'][$row_noten->studiensemester_kurzbz]['data']['ectssumme_nonstpl']))
$courses['semester'][$row_noten->studiensemester_kurzbz]['data']['ectssumme_nonstpl'] = 0;
+86 -7
View File
@@ -15,10 +15,11 @@ class Tag_Controller extends FHCAPI_Controller
'getTag' => self::BERECHTIGUNG_KURZBZ,
'getTags' => self::BERECHTIGUNG_KURZBZ,
'addTag' => self::BERECHTIGUNG_KURZBZ,
'updateTag' => self::BERECHTIGUNG_KURZBZ,
'doneTag' => self::BERECHTIGUNG_KURZBZ,
'deleteTag' => self::BERECHTIGUNG_KURZBZ,
'getAllTags' => self::BERECHTIGUNG_KURZBZ,
'rebuildTagsForTypeId' => self::BERECHTIGUNG_KURZBZ,
];
$merged_permissions = array_merge($default_permissions, $permissions);
@@ -26,6 +27,10 @@ class Tag_Controller extends FHCAPI_Controller
parent::__construct($merged_permissions);
$this->_setAuthUID();
// Library
$this->load->library('TagLib');
$this->load->model('person/Notiz_model', 'NotizModel');
$this->load->model('system/Notiztyp_model', 'NotiztypModel');
$this->load->model('person/Notizzuordnung_model', 'NotizzuordnungModel');
@@ -37,7 +42,6 @@ class Tag_Controller extends FHCAPI_Controller
public function getTag($readonly_tags = null)
{
$language = $this->_getLanguageIndex();
$id = $this->input->get('id');
if (is_array($readonly_tags) && !isEmptyArray($readonly_tags))
@@ -62,14 +66,17 @@ class Tag_Controller extends FHCAPI_Controller
$this->NotizModel->addSelect(
"tbl_notiz.titel,
tbl_notiz.text,
array_to_json(bezeichnung_mehrsprachig::varchar[])->>". $language. " as bezeichnung,
array_to_json(bezeichnung_mehrsprachig::varchar[])->>0 as bezeichnung,
tbl_notiz.notiz_id,
tbl_notiz_typ.style,
tbl_notiz_typ.automatisiert,
tbl_notiz.erledigt as done,
tbl_notiz.insertamum,
tbl_notiz.updateamum,
(verfasserperson.vorname || ' ' || verfasserperson.nachname || ' ' || '(' || verfasserbenutzer.uid || ')') as verfasser,
(bearbeiterperson.vorname || ' ' || bearbeiterperson.nachname || ' ' || '(' || bearbeiterbenutzer.uid || ')') as bearbeiter
(bearbeiterperson.vorname || ' ' || bearbeiterperson.nachname || ' ' || '(' || bearbeiterbenutzer.uid || ')') as bearbeiter,
tbl_notiz.start,
tbl_notiz.ende
"
);
$this->NotizModel->addJoin('public.tbl_notiz_typ', 'public.tbl_notiz.typ = public.tbl_notiz_typ.typ_kurzbz');
@@ -82,18 +89,22 @@ class Tag_Controller extends FHCAPI_Controller
$notiz = $this->NotizModel->loadWhere(array('notiz_id' => $id));
$this->terminateWithSuccess(hasData($notiz) ? getData($notiz)[0] : array());
}
public function getTags($tags = null)
{
$language = $this->_getLanguageIndex();
$this->NotiztypModel->addSelect(
'typ_kurzbz as tag_typ_kurzbz,
"typ_kurzbz as tag_typ_kurzbz,
array_to_json(bezeichnung_mehrsprachig::varchar[])->>0 as bezeichnung,
style,
beschreibung,
tag
'
tag,
automatisiert
"
);
$this->NotiztypModel->addOrder('prioritaet');
@@ -271,6 +282,74 @@ class Tag_Controller extends FHCAPI_Controller
$this->terminateWithSuccess($deleteNotiz);
}
public function getAllTags($readonly_tags = false){
$prestudent_id = $this->input->get('prestudent_id');
//TODO check for readonly: necessary?
if (is_array($readonly_tags) && !isEmptyArray($readonly_tags))
{
$readonly_tags = $this->_filterTag($readonly_tags, true);
foreach ($readonly_tags as $key => $tag)
{
$readonly_tags[$key] = $this->NotizModel->db->escape($tag);
}
$tags = '(' . implode(',', $readonly_tags) . ')';
$this->NotizModel->addSelect("
CASE
WHEN tbl_notiz_typ.typ_kurzbz IN $tags
THEN TRUE
ELSE FALSE
END as readonly
");
}
$this->NotizModel->addSelect(
"tbl_notiz.titel,
tbl_notiz.text,
array_to_json(bezeichnung_mehrsprachig::varchar[])->>0 as bezeichnung,
tbl_notiz.notiz_id,
tbl_notiz_typ.style,
tbl_notiz_typ.automatisiert,
tbl_notiz.erledigt as done,
tbl_notiz.insertamum,
tbl_notiz.updateamum,
(verfasserperson.vorname || ' ' || verfasserperson.nachname || ' ' || '(' || verfasserbenutzer.uid || ')') as verfasser,
(bearbeiterperson.vorname || ' ' || bearbeiterperson.nachname || ' ' || '(' || bearbeiterbenutzer.uid || ')') as bearbeiter,
tbl_notiz.start,
tbl_notiz.ende
"
);
$this->NotizModel->addJoin('public.tbl_notiz_typ', 'public.tbl_notiz.typ = public.tbl_notiz_typ.typ_kurzbz');
$this->NotizModel->addJoin('public.tbl_benutzer verfasserbenutzer', 'tbl_notiz.verfasser_uid = verfasserbenutzer.uid', 'LEFT');
$this->NotizModel->addJoin('public.tbl_person verfasserperson', 'verfasserbenutzer.person_id = verfasserperson.person_id', 'LEFT');
$this->NotizModel->addJoin('public.tbl_benutzer bearbeiterbenutzer', 'tbl_notiz.bearbeiter_uid = bearbeiterbenutzer.uid', 'LEFT');
$this->NotizModel->addJoin('public.tbl_person bearbeiterperson', 'bearbeiterbenutzer.person_id = bearbeiterperson.person_id', 'LEFT');
$this->NotizModel->addJoin('public.tbl_notizzuordnung notizzuordnung', 'tbl_notiz.notiz_id = notizzuordnung.notiz_id');
$notiz = $this->NotizModel->loadWhere(array('prestudent_id' => $prestudent_id));
$this->terminateWithSuccess(hasData($notiz) ? getData($notiz) : array());
}
public function rebuildTagsForTypeId()
{
$id = $this->input->post('id');
$typeId = $this->input->post('typeId');
$semester = $this->input->post('sem');
$result = $this->taglib->rebuildTagsForTypeId($typeId, $id, $semester);
if (isError($result))
return error ('Error occurred during updateAutomatedTags');
$this->terminateWithSuccess($result);
}
private function _setAuthUID()
{
$this->_uid = getAuthUID();
+22
View File
@@ -270,6 +270,28 @@ function absoluteJsImportUrl($relurl)
return $url;
}
/*
* Generate Css File Include if Extension contains file
*
* @param $relativeFilePath path relative to Extension public/css dir
*/
function generateCSSsIncludeIfExtensionCssExists($relativeFilePath)
{
$fsiterator = new FilesystemIterator(FHCPATH . 'application/extensions');
foreach ($fsiterator as $fsitem)
{
if(preg_match('/^FHC-Core-/', $fsitem->getBasename()))
{
$extensionfile = 'public/extensions/' . $fsitem->getBasename()
. '/css/' . $relativeFilePath;
if(is_readable(FHCPATH . $extensionfile))
{
generateCSSsInclude($extensionfile);
}
}
}
}
/*
* Manipulate CI views includes Array to load
* - public/js/FhcApps.js via customJSs and
+247
View File
@@ -114,6 +114,7 @@ class StundenplanLib
return $stundenplan_data;
$stundenplan_data = getData($stundenplan_data) ?? [];
$this->_ci->addMeta("stundenplanData", $stundenplan_data);
$function_error = $this->expandObjectInformation($stundenplan_data);
if ($function_error)
return $function_error;
@@ -374,6 +375,39 @@ class StundenplanLib
$item->gruppe = $gruppe_obj_array;
$item->lektor = $lektor_obj_array;
$this->_ci->load->library('PermissionLib');
$berechtigt_begrenzt = $this->_ci->permissionlib->isBerechtigt('lehre/reservierung:begrenzt', 'sui');
$now = time();
$res_lektor_start = $this->jump_day($now, RES_TAGE_LEKTOR_MIN - 1);
$res_lektor_ende = mktime(0, 0, 0, date('m', $now), date('d', $now) + RES_TAGE_LEKTOR_BIS, date('Y', $now));
$start_date = is_numeric($item->beginn) ? $item->beginn : strtotime($item->beginn);
if (!date('w', $start_date)) {
$start_date = $this->jump_day($start_date, 1);
}
$start_date_str = date('Y-m-d', $start_date);
$res_lektor_start_str = date('Y-m-d', $res_lektor_start);
$res_lektor_ende_str = date('Y-m-d', $res_lektor_ende);
$show_delete = (
(
$berechtigt_begrenzt &&
(
(isset($item->insertvon) && $item->insertvon == getAuthUID()) ||
(isset($item->uids) && in_array(getAuthUID(), $item->uids))
)
) &&
$start_date_str >= $res_lektor_start_str &&
$start_date_str <= $res_lektor_ende_str
);
if ($show_delete)
$item->deletable = true;
else
$item->deletable = false;
}
}
@@ -469,6 +503,219 @@ class StundenplanLib
return success($stundenplan_data);
}
public function addReservation($start, $end, $title, $beschreibung, $ort_kurzbz, $lektoren = null, $studiengang = null, $semester = null, $verband = null, $gruppe = null, $spezialgruppe = null)
{
$this->_ci =& get_instance();
$this->_ci->load->model('ressource/Stunde_model', 'StundeModel');
$this->_ci->load->model('ressource/Reservierung_model', 'ReservierungModel');
$this->_ci->load->model('ressource/stundenplandev_model', 'StundenplandevModel');
$this->_ci->load->model('ressource/stundenplan_model', 'StundenplanModel');
$this->_ci->load->library('PermissionLib');
$startTime = new DateTime($start);
$endTime = new DateTime($end);
$stunden = $this->_ci->StundeModel->loadWhere(array(
'beginn <' => $endTime->format('H:i:s'),
'ende >' => $startTime->format('H:i:s')
));
if (!hasData($stunden))
{
return error("Keine Stunden vorhanden");
}
$stunden = array_column(getData($stunden), 'stunde');
$this->_ci->StundenplandevModel->db->select('1');
$this->_ci->StundenplandevModel->db->where('datum', $startTime->format('Y-m-d'));
$this->_ci->StundenplandevModel->db->where('ort_kurzbz', $ort_kurzbz);
$this->_ci->StundenplandevModel->db->where_in('stunde', $stunden);
$stundenplandev_belegung = $this->_ci->StundenplandevModel->load();
$this->_ci->StundenplanModel->db->select('1');
$this->_ci->StundenplanModel->db->where('ort_kurzbz', $ort_kurzbz);
$this->_ci->StundenplanModel->db->where('datum', $startTime->format('Y-m-d'));
$this->_ci->StundenplanModel->db->where_in('stunde', $stunden);
$stundenplan_belegung = $this->_ci->StundenplanModel->load();
if ((hasData($stundenplandev_belegung) || hasData($stundenplan_belegung))
&& !$this->_ci->permissionlib->isBerechtigt('lehre/reservierungAdvanced'))
return error ('lvplan/bereitsReserviert');
$this->_ci->ReservierungModel->addSelect('stunde');
$reservation_hours = $this->_ci->ReservierungModel->loadWhere(array('datum' => $startTime->format('Y-m-d'), 'ort_kurzbz' => $ort_kurzbz));
if (isError($reservation_hours))
return $reservation_hours;
$reservation_hours = hasData($reservation_hours) ? array_column(getData($reservation_hours), 'stunde') : array();
if (!empty(array_intersect($stunden, $reservation_hours))
&& !$this->_ci->permissionlib->isBerechtigt('lehre/reservierungAdvanced'))
return error("lvplan/bereitsReserviert");
if (!empty($lektoren))
{
foreach ($lektoren as $lektor)
{
$insert = array('ort_kurzbz' => $ort_kurzbz,
'datum' => $startTime->format('Y-m-d'),
'titel' => $title,
'studiengang_kz' => is_null($studiengang) ? 0 : $studiengang,
'beschreibung' => $beschreibung,
'insertvon' => getAuthUID(),
'uid' => $lektor,
'semester' => is_null($semester) ? null : $semester,
'verband' => is_null($verband) ? null : $verband,
'gruppe' => is_null($gruppe) ? null : $gruppe,
'gruppe_kurzbz' => is_null($spezialgruppe) ? null : $spezialgruppe,
);
foreach ($stunden as $stunde)
{
$insert['stunde'] = $stunde;
$check_insert = $this->_ci->ReservierungModel->insert($insert);
if (isError($check_insert))
return $check_insert;
}
}
}
else
{
foreach ($stunden as $stunde)
{
$check_insert = $this->_ci->ReservierungModel->insert(array(
'ort_kurzbz' => $ort_kurzbz,
'uid' => getAuthUID(),
'stunde' => $stunde,
'datum' => $startTime->format('Y-m-d'),
'titel' => $title,
'studiengang_kz' => is_null($studiengang) ? 0 : $studiengang,
'beschreibung' => $beschreibung,
'insertvon' => getAuthUID()
));
if (isError($check_insert))
return $check_insert;
}
}
return success("Erfolgreich");
}
public function deleteReservation($reservierung_id)
{
$this->_ci =& get_instance();
$this->_ci->load->model('ressource/Reservierung_model', 'ReservierungModel');
$this->_ci->load->model('ressource/Stunde_model', 'StundeModel');
$this->_ci->load->library('PermissionLib');
$this->_ci->ReservierungModel->db->where_in('reservierung_id', $reservierung_id);
$reservation = $this->_ci->ReservierungModel->load();
if (isError($reservation))
return $reservation;
if (!hasData($reservation))
return error("Reservierungen nicht gefunden");
$reservations = getData($reservation);
$today = new DateTime();
foreach ($reservations as $reservierung)
{
if ($today->format('Y-m-d') > $reservierung->datum)
return error("Vergangene Reservierungen können nicht gelöscht werden");
if (($this->_ci->permissionlib->isBerechtigt('lehre/reservierung:begrenzt')) && ($reservierung->insertvon == getAuthUID() || $reservierung->uid === getAuthUID()))
{
$delete_result = $this->_ci->ReservierungModel->delete($reservierung->reservierung_id);
if (isError($delete_result))
return $delete_result;
}
}
return success("Erfolgreich");
}
public function getReservableMap($ort_kurzbz, $start_date, $end_date)
{
$this->_ci =& get_instance();
$this->_ci->load->model('ressource/Ort_model', 'OrtModel');
$this->_ci->load->library('PermissionLib');
$berechtigt_begrenzt = $this->_ci->permissionlib->isBerechtigt('lehre/reservierung:begrenzt', 'suid');
$berechtigt_erweitert = $this->_ci->permissionlib->isBerechtigt('lehre/reservierung', 'suid');
$ort_data = $this->_ci->OrtModel->load($ort_kurzbz);
if (isError($ort_data) || !hasData($ort_data))
return [];
$ort_data = getData($ort_data)[0];
if (!$ort_data->reservieren)
return [];
if (!$berechtigt_begrenzt && !$berechtigt_erweitert)
return [];
$start_ts = is_numeric($start_date) ? (int)$start_date : strtotime($start_date);
$end_ts = is_numeric($end_date) ? (int)$end_date : strtotime($end_date);
if (!$start_ts || !$end_ts)
return [];
if ($end_ts < $start_ts)
{
$tmp = $start_ts;
$start_ts = $end_ts;
$end_ts = $tmp;
}
$now = time();
$tage_min = defined('RES_TAGE_LEKTOR_MIN') ? (int)RES_TAGE_LEKTOR_MIN : 0;
$tage_bis = defined('RES_TAGE_LEKTOR_BIS') ? (int)RES_TAGE_LEKTOR_BIS : 0;
$datum_res_lektor_start = $this->jump_day($now, $tage_min - 1);
$datum_res_lektor_ende = $this->jump_day($now, $tage_bis);
$start_ymd_allowed = date('Y-m-d', $datum_res_lektor_start);
$end_ymd_allowed = date('Y-m-d', $datum_res_lektor_ende);
$result = [];
$current = strtotime(date('Y-m-d', $start_ts) . ' 00:00:00');
$end_day = strtotime(date('Y-m-d', $end_ts) . ' 00:00:00');
while ($current <= $end_day)
{
$ymd = date('Y-m-d', $current);
if ((int)date('w', $current) === 0)
{
$result[$ymd] = false;
$current = $this->jump_day($current, 1);
continue;
}
$result[$ymd] = ($ymd >= $start_ymd_allowed && $ymd <= $end_ymd_allowed) ? true : false;
$current = $this->jump_day($current, 1);
}
return success($result);
}
private function jump_day($timestamp, $days)
{
$days = (int)$days;
$prefix = ($days >= 0 ? '+' : '');
return strtotime($prefix . $days . ' days', $timestamp);
}
// start of the private functions ########################################################################################################
// function used to sort an array of studiensemester strings
+392
View File
@@ -0,0 +1,392 @@
<?php
/**
* FH-Complete
*
* @package FHC-Helper
* @author FHC-Team
* @copyright Copyright (c) 2026 fhcomplete.net
* @license GPLv3
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
use \DateTime as DateTime;
use \stdClass as stdClass;
class TagLib
{
const BATCHUSER = 'sftest';
/**
* Object initialization
*/
public function __construct()
{
$this->_ci =& get_instance();
// Configs
$this->_ci->load->config('stv');
// Models
$this->_ci->load->model('person/Notiz_model', 'NotizModel');
$this->_ci->load->model('system/Notiztyp_model', 'NotiztypModel');
$this->_ci->load->model('person/Notizzuordnung_model', 'NotizzuordnungModel');
$this->_ci->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel');
$this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
// Libraries
$this->_ci->load->library('PermissionLib');
$this->_ci->load->library('PrestudentLib');
}
public function updateAutomatedTags($paramsTag)
{
// ---------------------------------
// check params
// ---------------------------------
$required = ['kurzbz', 'data', 'typeId'];
foreach ($required as $key) {
if (!isset($paramsTag[$key])) {
return error('Missing Parameter: ' . $key);
}
}
$studiensemester_kurzbz = (isset ($paramsTag['studiensemester_kurzbz']) ? $paramsTag['studiensemester_kurzbz'] : null);
$tag = $paramsTag['kurzbz'];
$inputData = $paramsTag['data'];
$typeId = $paramsTag['typeId'];
// ---------------------------------
// prepare input
// ---------------------------------
$zeitraum = [];
$arrayIds = [];
foreach ($inputData as $item) {
$id = $item['id'];
$arrayIds[] = $id;
$zeitraum[$id] = [
'von' => $item['von'] ?? null,
'bis' => $item['bis'] ?? null
];
}
$arrayIds = array_unique($arrayIds);
$result = $this->_ci->StudiensemesterModel->load($studiensemester_kurzbz);
if (isError($result)) {
return $result;
}
$data = $result->retval[0] ?? null;
$von = $data->start ?? null;
$bis = $data->ende ?? null;
// ---------------------------------
// load existing tags
// ---------------------------------
$allTags = [];
$resultAllTags = $this->_ci->NotizModel->getAllTags($tag, $von, $bis);
if (isError($resultAllTags)) {
return $resultAllTags;
}
$allTagsData = getData($resultAllTags);
if (!empty($allTagsData)) {
foreach ($allTagsData as $item) {
$allTags[$item->$typeId] = $item->notiz_id;
}
}
// ---------------------------------
// map the data
// ---------------------------------
$toRecycle = [];
$toAdd = [];
$toDelete = [];
foreach ($arrayIds as $id) {
if (isset($allTags[$id])) {
$toRecycle[$id] = $allTags[$id];
} else {
$toAdd[] = $id;
}
}
foreach ($allTags as $id => $notizId) {
if (!in_array($id, $arrayIds)) {
$toDelete[$id] = $notizId;
}
}
// ---------------------------------
// recycle (update existing)
// ---------------------------------
$countRecycled = 0;
$retagged = [];
foreach ($toRecycle as $id => $notizId)
{
$this->_updateTag($notizId, $zeitraum[$id]['von'], $zeitraum[$id]['bis']);
$countRecycled++;
$retagged[] = $id;
}
// ---------------------------------
// ADD
// ---------------------------------
$countAdded = 0;
$tagged = [];
foreach ($toAdd as $id)
{
$this->_insertTag($typeId, $id, $tag, $zeitraum[$id]['von'], $zeitraum[$id]['bis']);
$countAdded++;
$tagged[] = $id;
}
// ---------------------------------
// delete
// ---------------------------------
$countDeleted = 0;
$deleted = [];
foreach ($toDelete as $id => $notizId)
{
$result = $this->_deleteTag($notizId);
if (isError($result)) {
return $result;
}
$countDeleted++;
$deleted[] = $id;
}
// ---------------------------------
// return
// ---------------------------------
return success([
'input' => [
'tag' => $tag,
'arrayIds' => $arrayIds
],
'summary' => [
'recycled' => $countRecycled,
'added' => $countAdded,
'deleted' => $countDeleted
],
'details' => [
'existingTags' => $allTags,
'toAdd' => $toAdd,
'toRecycle' => $toRecycle,
'toDelete' => $toDelete
],
'results' => [
'retaggedIds' => $retagged,
'newTags' => $tagged,
'deletedTagsIds' => $deleted
]
]);
}
public function updateAutomatedTagsForTypeId(array $params)
{
$return = null;
$notiz_id = null;
$von = $params['von'];
$bis = $params['bis'];
$tag = $params['kurzbz'];
$id = $params['id'];
$typeId = $params['typeId'];
$this->_ci->NotizModel->addSelect('nz.notiz_id');
$this->_ci->NotizModel->addSelect($typeId);
$this->_ci->NotizModel->addJoin('public.tbl_notizzuordnung nz', 'notiz_id');
$resultAllTags = $this->_ci->NotizModel->loadWhere([
'typ' => $tag,
$typeId => $id
]);
if(hasData($resultAllTags))
{
$notiz_id = $resultAllTags->retval[0]->notiz_id;
}
//RECYCLE
if ($notiz_id !== null)
{
$resultUpdateNotiz = $this->_updateTag($notiz_id, $von, $bis);
$return = ['recycled' => $resultUpdateNotiz];
}
else
//ADD
{
$resultInsertNotiz = $this->_insertTag($typeId, $id, $tag, $von, $bis);
$return = ['added' => $resultInsertNotiz];
}
return success($return);
}
/*
* main function for rebuild Tags for typeId
* */
public function rebuildTagsForTypeId($typeId, $id, $studiensemester_kurzbz)
{
$automatedTagsRes = $this->_ci->NotiztypModel->loadWhere(array('automatisiert' => true, 'taglib IS NOT NULL' => null));
$automatedTags = hasData($automatedTagsRes) ? getData($automatedTagsRes) : [];
$result = $this->_ci->StudiensemesterModel->load($studiensemester_kurzbz);
if (isError($result))
return error('Error occurred during retrieving studiensemester');
if (empty($result->retval) || !isset($result->retval[0])) {
return error('No studiensemester found');
}
$startSem = $result->retval[0]->start ?? null;
$endeSem = $result->retval[0]->ende ?? null;
$return = [];
foreach ($automatedTags as $autoTag)
{
// getPath: must not be lost
$filePath = APPPATH . 'libraries/' . $autoTag->taglib . '.php'; // APPPATH = application/
if (file_exists($filePath)) {
require_once($filePath);
} else {
echo "File not found: " . $filePath;
continue;
}
$className = basename($autoTag->taglib);
$kurz_bz = $autoTag->typ_kurzbz;
$obj = new $className();
$criteriaIsSet = $obj->isCriteriaSetFor([
'typeId' => $typeId,
'id' => $id,
'studiensemester_kurzbz' => $studiensemester_kurzbz
]);
if (hasData($criteriaIsSet))
{
$von = isset($criteriaIsSet->retval[0]->von) ? $criteriaIsSet->retval[0]->von : '';
$bis = isset($criteriaIsSet->retval[0]->bis) ? $criteriaIsSet->retval[0]->bis : '';
$params = [
'von' => $von,
'bis' => $bis,
'kurzbz' => $autoTag->typ_kurzbz,
'typeId' => $typeId,
'id' => $id,
];
$result = $this->updateAutomatedTagsForTypeId($params);
if (isError($result))
return error('Error occurred during updateAutomatedTags' . $kurz_bz);
$return[$kurz_bz] = $result;
}
else
{
//CHECK FOR DELETE
$params = [
'von' => $startSem,
'bis' => $endeSem,
'kurzbz' => $autoTag->typ_kurzbz,
'typeId' => $typeId,
'id' => $id,
];
$result = $this->_ci->NotizModel->checkIfExistingTag($kurz_bz, $typeId, $id, $startSem, $endeSem);
if (hasData($result))
{
$notizId = $result->retval[0]->notiz_id;
$this->_deleteTag($notizId);
$return[$kurz_bz] = ['deleted' => $notizId];
}
}
}
return success($return);
}
private function _insertTag($typeId, $id, $tag, $von, $bis)
{
$resultInsert = $this->_ci->NotizModel->insert([
'titel' => 'TAG',
'text' => 'AUTOMATED TAG',
'verfasser_uid' => self::BATCHUSER,
'erledigt' => false,
'insertamum' => date('Y-m-d H:i:s'),
'insertvon' => 'BatchJobTagAdd',
'typ' => $tag,
'start' => $von,
'ende' => $bis
]);
if (isError($resultInsert)) {
return error('Error inserting tag for ' . $typeId . ': ' . $id);
}
$notizId = $resultInsert->retval;
$resultZuordnung = $this->_ci->NotizzuordnungModel->insert([
'notiz_id' => $notizId,
$typeId => $id
]);
if (isError($resultZuordnung)) {
return error('Error inserting relation for ' . $typeId . ': ' . $id);
}
return $notizId;
}
private function _updateTag($notiz_id, $von, $bis)
{
$resultUpdateNotiz = $this->_ci->NotizModel->update(
[
'notiz_id' => $notiz_id
],
array(
'updateamum' => date('Y-m-d H:i:s'),
'updatevon' => 'BatchJobTagUpdate',
'start' => $von,
'ende' => $bis
));
if (isError($resultUpdateNotiz))
return error ('Error occurred during Update ' . $notiz_id);
return $notiz_id;
}
private function _deleteTag($notiz_id)
{
$result = $this->_ci->NotizzuordnungModel->delete([
'notiz_id' => $notiz_id
]);
if (isError($result)) {
return error('Error occurred during delete Notizzuordnung ' . $notiz_id);
}
$result = $this->_ci->NotizModel->delete([
'notiz_id' => $notiz_id
]);
if (isError($result)) {
return error('Error occurred during delete Notiz ' . $notiz_id);
}
return success([
'deleted' => $notiz_id
]);
}
}
@@ -0,0 +1,81 @@
<?php
/**
* Description of dd_auto
*
* @author ma0068
*/
class CoreDoubleDegreeTagLib
{
protected $ci;
public function __construct()
{
$this->ci = get_instance();
$this->ci->load->model('codex/Mobilitaet_model', 'MobilitaetModel');
}
public function getZuordnungIds(array $params)
{
if(!isset($params['studiensemester_kurzbz']))
{
return (object) array(
'idArray' => []
);
}
$semester = $params['studiensemester_kurzbz'];
$this->ci->MobilitaetModel->addJoin('bis.tbl_gsprogramm', 'gsprogramm_id');
$this->ci->MobilitaetModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz');
$result = $this->ci->MobilitaetModel-> loadWhere(array(
'gsprogrammtyp_kurzbz' => 'Double',
'studiensemester_kurzbz' => $semester
));
$data = $result->retval;
$doubledegree_data = array_map(function($item) {
return [
'id' => $item->prestudent_id,
'von' => $item->start,
'bis' => $item->ende
];
}, $data);
return (object) array(
'data' => $doubledegree_data,
'typeId' => 'prestudent_id'
);
}
public function isCriteriaSetFor(array $params)
{
if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id')
return false;
$semester = $params['studiensemester_kurzbz'];
$prestudent_id = $params['id'];
$this->ci->MobilitaetModel->addSelect('prestudent_id');
$this->ci->MobilitaetModel->addSelect('start as von');
$this->ci->MobilitaetModel->addSelect('ende as bis');
$this->ci->MobilitaetModel->addJoin('bis.tbl_gsprogramm', 'gsprogramm_id');
$this->ci->MobilitaetModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz');
$result = $this->ci->MobilitaetModel->loadWhere(array(
'gsprogrammtyp_kurzbz' => 'Double',
'studiensemester_kurzbz' => $semester,
'prestudent_id' => $prestudent_id
));
if(hasData($result))
{
//array mit prestudent_id, von und bis
return $result;
}
else
return null;
}
}
@@ -0,0 +1,81 @@
<?php
/**
* Description unruly
* Test for different typeId
*
* @author ma0068
*/
class CoreFiftyFiveTagLib
{
protected $ci;
public function __construct()
{
$this->ci = get_instance();
$this->ci->load->model('person/Person_model', 'PersonModel');
$this->ci-> load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
}
public function getZuordnungIds(array $params)
{
if(!isset($params['studiensemester_kurzbz']))
{
return (object) array(
'person_id' => []
);
}
$semester = $params['studiensemester_kurzbz'];
$result = $this->ci->StudiensemesterModel->loadWhere(array(
'studiensemester_kurzbz' => $semester
));
$data = $result->retval[0];
$semVon = $data->start;
$semBis = $data->ende;
$result = $this->ci->PersonModel->getFiftyFivers($semVon, $semBis);
$data = $result->retval;
$fiftyFiveData = array_map(function($item) {
return [
'id' => $item->person_id
];
}, $data);
return (object) array(
'data' => $fiftyFiveData,
'typeId' => 'person_id'
);
}
public function isCriteriaSetFor(array $params)
{
if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'person_id')
return false;
$semester = $params['studiensemester_kurzbz'];
$person_id = $params['id'];
$typeId = $params['typeId'];
$result = $this->ci->StudiensemesterModel->loadWhere(array(
'studiensemester_kurzbz' => $semester
));
$data = $result->retval[0];
$semVon = $data->start;
$semBis = $data->ende;
$result = $this->ci->PersonModel->isFiftyFive($semVon, $semBis, $person_id);
if(hasData($result))
{
return $result;
}
else
return null;
}
}
@@ -0,0 +1,64 @@
<?php
/**
* Description of jgv_auto (Jahrgangsvertretung)
*
* @author ma0068
*/
class CoreJgvTagLib
{
protected $ci;
public function __construct()
{
$this->ci = get_instance();
$this->ci->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
}
public function getZuordnungIds(array $params)
{
if(!isset($params['studiensemester_kurzbz']))
{
return (object) array(
'idArray' => []
);
}
$semester = $params['studiensemester_kurzbz'];
$result = $this->ci->BenutzerfunktionModel->getPrestudentsOfJgv($semester);
$data = $result->retval;
$jgv_data = array_map(function($item) {
return [
'id' => $item->prestudent_id,
'von' => $item->datum_von,
'bis' => $item->datum_bis
];
}, $data);
return (object) array(
'data' => $jgv_data,
'typeId' => 'prestudent_id'
);
}
public function isCriteriaSetFor(array $params)
{
if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id')
return false;
$semester = $params['studiensemester_kurzbz'];
$prestudent_id = $params['id'];
$result = $this->ci->BenutzerfunktionModel->isJgv($semester, $prestudent_id);
if(hasData($result))
{
return $result;
}
else
return null;
}
}
@@ -0,0 +1,81 @@
<?php
/**
* Description of zgv_auto
*
* @author ma0068
*/
class CoreMissingZgvTagLib
{
protected $ci;
public function __construct()
{
$this->ci = get_instance();
$this->ci->load->model('crm/Prestudent_model', 'PrestudentModel');
}
public function getZuordnungIds(array $params)
{
if(!isset($params['studiensemester_kurzbz']))
{
return (object) array(
'idArray' => []
);
}
$semester = $params['studiensemester_kurzbz'];
$this->ci->PrestudentModel->addJoin('public.tbl_prestudentstatus', 'prestudent_id');
$this->ci->PrestudentModel->addJoin('public.tbl_benutzer bn', 'person_id');
$this->ci->PrestudentModel->addJoin('public.tbl_studiengang', 'studiengang_kz');
$result = $this->ci->PrestudentModel-> loadWhere(array(
'bn.aktiv' => true, //check if necessary
'zgvdatum' => null,
'typ' => 'b',
'studiensemester_kurzbz' => $semester
));
$data = $result->retval;
$zgvmissing_data = array_map(function($item) {
return [
'id' => $item->prestudent_id,
'von' => null,
'bis' => null
];
}, $data);
return (object) array(
'typeId' => 'prestudent_id',
'data' => $zgvmissing_data
);
}
public function isCriteriaSetFor(array $params)
{
if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id')
return false;
$semester = $params['studiensemester_kurzbz'];
$prestudent_id = $params['id'];
$this->ci->PrestudentModel->addJoin('public.tbl_prestudentstatus', 'prestudent_id');
$this->ci->PrestudentModel->addJoin('public.tbl_benutzer bn', 'person_id');
$this->ci->PrestudentModel->addJoin('public.tbl_studiengang', 'studiengang_kz');
$result = $this->ci->PrestudentModel->loadWhere(array(
'bn.aktiv' => true, //check if necessary
'zgvdatum' => null,
'typ' => 'b',
'studiensemester_kurzbz' => $semester,
'prestudent_id' => $prestudent_id
));
if(hasData($result))
{
return $result;
}
else
return null;
}
}
@@ -0,0 +1,63 @@
<?php
/**
* Description of out_auto
*
* @author ma0068
*/
class CoreOutgoingTagLib
{
protected $ci;
public function __construct()
{
$this->ci = get_instance();
$this->ci->load->model('codex/Bisio_model', 'BisioModel');
}
public function getZuordnungIds(array $params)
{
if(!isset($params['studiensemester_kurzbz']))
{
return (object) array(
'idArray' => []
);
}
$semester = $params['studiensemester_kurzbz'];
$result = $this->ci->BisioModel->getOutgoingsOfSemester($semester);
$data = $result->retval;
$outgoing_data = array_map(function($item) {
return [
'id' => $item->prestudent_id,
'von' => $item->von,
'bis' => $item->bis
];
}, $data);
return (object) array(
'data' => $outgoing_data,
'typeId' => 'prestudent_id',
);
}
public function isCriteriaSetFor(array $params)
{
if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id')
return false;
$semester = $params['studiensemester_kurzbz'];
$prestudent_id = $params['id'];
$result = $this->ci->BisioModel->isPrestudentOutgoing($semester, $prestudent_id);
if (hasData($result)) {
return $result;
}
else
return null;
}
}
@@ -0,0 +1,76 @@
<?php
/**
* Description of prewh_auto
*
* @author bambi
*/
class CorePrewiederholerTagLib
{
protected $ci;
public function __construct()
{
$this->ci = get_instance();
$this->ci->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel');
}
public function getZuordnungIds(array $params)
{
if(!isset($params['studiensemester_kurzbz']))
{
return (object) array(
'idArray' => []
);
}
$semester = $params['studiensemester_kurzbz'];
$this->ci->PrestudentstatusModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz');
$result = $this->ci->PrestudentstatusModel-> loadWhere(array(
'statusgrund_id' => 15,
'studiensemester_kurzbz' => $semester
));
$data = $result->retval;
$prewiederholer_data = array_map(function($item) {
return [
'id' => $item->prestudent_id,
'von' => $item->start,
'bis' => $item->ende
];
}, $data);
return (object) array(
'data' => $prewiederholer_data,
'typeId' => 'prestudent_id',
);
}
public function isCriteriaSetFor(array $params)
{
if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id')
return false;
$semester = $params['studiensemester_kurzbz'];
$prestudent_id = $params['id'];
$this->ci->PrestudentstatusModel->addSelect('prestudent_id');
$this->ci->PrestudentstatusModel->addSelect('start as von');
$this->ci->PrestudentstatusModel->addSelect('ende as bis');
$this->ci->PrestudentstatusModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz');
$result = $this->ci->PrestudentstatusModel->loadWhere(array(
'statusgrund_id' => 15,
'studiensemester_kurzbz' => $semester,
'prestudent_id' => $prestudent_id
));
if(hasData($result))
{
return $result;
}
else
return null;
}
}
@@ -0,0 +1,79 @@
<?php
/**
* Description of dd_auto
*
* @author ma0068
*/
class CoreStbErhoehtTagLib
{
protected $ci;
public function __construct()
{
$this->ci = get_instance();
$this->ci->load->model('crm/Konto_model', 'KontoModel');
}
public function getZuordnungIds(array $params)
{
if(!isset($params['studiensemester_kurzbz']))
{
return (object) array(
'idArray' => []
);
}
$semester = $params['studiensemester_kurzbz'];
$this->ci->KontoModel->addJoin('public.tbl_prestudent', 'person_id');
$this->ci->KontoModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz');
$result = $this->ci->KontoModel-> loadWhere(array(
'buchungstyp_kurzbz' => 'StudiengebuehrErhoeht',
'studiensemester_kurzbz' => $semester
));
$data = $result->retval;
$konto_data = array_map(function($item) {
return [
'id' => $item->prestudent_id,
'von' => $item->start,
'bis' => $item->ende
];
}, $data);
return (object) array(
'data' => $konto_data,
'typeId' => 'prestudent_id'
);
}
public function isCriteriaSetFor(array $params)
{
if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id')
return false;
$semester = $params['studiensemester_kurzbz'];
$prestudent_id = $params['id'];
$this->ci->KontoModel->addSelect('prestudent_id');
$this->ci->KontoModel->addSelect('start as von');
$this->ci->KontoModel->addSelect('ende as bis');
$this->ci->KontoModel->addJoin('public.tbl_prestudent', 'person_id');
$this->ci->KontoModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz');
$result = $this->ci->KontoModel-> loadWhere(array(
'buchungstyp_kurzbz' => 'StudiengebuehrErhoeht',
'studiensemester_kurzbz' => $semester,
'prestudent_id' => $prestudent_id
));
if(hasData($result))
{
return $result;
}
else
return null;
}
}
@@ -0,0 +1,76 @@
<?php
/**
* Description of wiedereinstieg_auto
*
* @author ma0068
*/
class CoreUnterbrecherTagLib
{
protected $ci;
public function __construct()
{
$this->ci = get_instance();
$this->ci->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel');
}
public function getZuordnungIds(array $params)
{
if(!isset($params['studiensemester_kurzbz']))
{
return (object) array(
'idArray' => []
);
}
$semester = $params['studiensemester_kurzbz'];
$this->ci->PrestudentstatusModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz');
$result = $this->ci->PrestudentstatusModel-> loadWhere(array(
'status_kurzbz' => 'Unterbrecher',
'studiensemester_kurzbz' => $semester
));
$data = $result->retval;
$unterbrecher_data = array_map(function($item) {
return [
'id' => $item->prestudent_id,
'von' => $item->start,
'bis' => $item->ende
];
}, $data);
return (object) array(
'data' => $unterbrecher_data,
'typeId' => 'prestudent_id'
);
}
public function isCriteriaSetFor(array $params)
{
if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id')
return false;
$semester = $params['studiensemester_kurzbz'];
$prestudent_id = $params['id'];
$this->ci->PrestudentstatusModel->addSelect('prestudent_id');
$this->ci->PrestudentstatusModel->addSelect('start as von');
$this->ci->PrestudentstatusModel->addSelect('ende as bis');
$this->ci->PrestudentstatusModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz');
$result = $this->ci->PrestudentstatusModel-> loadWhere(array(
'status_kurzbz' => 'Unterbrecher',
'studiensemester_kurzbz' => $semester,
'prestudent_id' => $prestudent_id
));
if(hasData($result))
{
return $result;
}
else
return null;
}
}
@@ -0,0 +1,76 @@
<?php
/**
* Description of wh_auto
*
* @author bambi
*/
class CoreWiederholerTagLib
{
protected $ci;
public function __construct()
{
$this->ci = get_instance();
$this->ci->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel');
}
public function getZuordnungIds(array $params)
{
if(!isset($params['studiensemester_kurzbz']))
{
return (object) array(
'idArray' => []
);
}
$semester = $params['studiensemester_kurzbz'];
$this->ci->PrestudentstatusModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz');
$result = $this->ci->PrestudentstatusModel-> loadWhere(array(
'statusgrund_id' => 16,
'studiensemester_kurzbz' => $semester
));
$data = $result->retval;
$wiederholer_data = array_map(function($item) {
return [
'id' => $item->prestudent_id,
'von' => $item->start,
'bis' => $item->ende
];
}, $data);
return (object) array(
'typeId' => 'prestudent_id',
'data' => $wiederholer_data
);
}
public function isCriteriaSetFor(array $params)
{
if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id')
return false;
$semester = $params['studiensemester_kurzbz'];
$prestudent_id = $params['id'];
$this->ci->PrestudentstatusModel->addSelect('prestudent_id');
$this->ci->PrestudentstatusModel->addSelect('start as von');
$this->ci->PrestudentstatusModel->addSelect('ende as bis');
$this->ci->PrestudentstatusModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz');
$result = $this->ci->PrestudentstatusModel->loadWhere(array(
'statusgrund_id' => 16,
'studiensemester_kurzbz' => $semester,
'prestudent_id' => $prestudent_id,
));
if(hasData($result))
{
return $result;
}
else
return null;
}
}
+52
View File
@@ -44,4 +44,56 @@ class Bisio_model extends DB_Model
else
return success("Bisio not found");
}
/**
* Gets outgoing students of certain Semester
* @param String studiensemester_kurzbz
* @return array of prestudent_ids
*/
public function getOutgoingsOfSemester($studiensemester_kurzbz)
{
$query = "
SELECT DISTINCT ps.prestudent_id, tbl_bisio.von, tbl_bisio.bis
FROM bis.tbl_bisio
JOIN public.tbl_student USING (student_uid)
JOIN public.tbl_prestudent ps USING (prestudent_id)
JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id)
JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz)
WHERE ss.studiensemester_kurzbz = ?
AND (
tbl_bisio.von <= ss.ende
AND (
tbl_bisio.bis >= ss.start
OR tbl_bisio.bis IS NULL
)
)
";
return $this->execQuery($query, array($studiensemester_kurzbz));
}
public function isPrestudentOutgoing($studiensemester_kurzbz, $prestudent_id)
{
$query = "
SELECT
ps.prestudent_id, tbl_bisio.von, tbl_bisio.bis
FROM bis.tbl_bisio
JOIN public.tbl_student USING (student_uid)
JOIN public.tbl_prestudent ps USING (prestudent_id)
JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id)
JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz)
WHERE ss.studiensemester_kurzbz = ?
--AND pss.status_kurzbz = 'Student'
AND (
tbl_bisio.von <= ss.ende
AND (
tbl_bisio.bis >= ss.start
OR tbl_bisio.bis IS NULL
)
)
AND ps.prestudent_id = ?
";
return $this->execQuery($query, array($studiensemester_kurzbz, $prestudent_id));
}
}
@@ -11,4 +11,73 @@ class Mobilitaet_model extends DB_Model
$this->dbTable = 'bis.tbl_mobilitaet';
$this->pk = 'mobilitaet_id';
}
public function getMobilityZusatzForUids($uids) {
$qry = "SELECT distinct on(nachname, vorname, public.tbl_benutzer.person_id) uid,
tbl_mitarbeiter.mitarbeiter_uid,
tbl_note.lkt_ueberschreibbar, tbl_note.anmerkung,
tbl_mobilitaet.mobilitaetstyp_kurzbz,
(CASE WHEN bis.tbl_mobilitaet.studiensemester_kurzbz = vw_student_lehrveranstaltung.studiensemester_kurzbz THEN 1 ELSE 0 END) as doubledegree,
public.tbl_prestudent.gsstudientyp_kurzbz as ddtype,
(SELECT status_kurzbz FROM public.tbl_prestudentstatus
WHERE prestudent_id=tbl_student.prestudent_id
ORDER BY datum DESC, insertamum DESC, ext_id DESC LIMIT 1) as studienstatus
FROM
campus.vw_student_lehrveranstaltung
JOIN public.tbl_benutzer USING(uid)
JOIN public.tbl_person USING(person_id)
LEFT JOIN public.tbl_student ON(uid=student_uid)
LEFT JOIN public.tbl_mitarbeiter ON(uid=mitarbeiter_uid)
LEFT JOIN public.tbl_studentlehrverband USING(student_uid,studiensemester_kurzbz)
LEFT JOIN lehre.tbl_zeugnisnote on(vw_student_lehrveranstaltung.lehrveranstaltung_id=tbl_zeugnisnote.lehrveranstaltung_id
AND tbl_zeugnisnote.student_uid=tbl_student.student_uid
AND tbl_zeugnisnote.studiensemester_kurzbz=tbl_studentlehrverband.studiensemester_kurzbz)
LEFT JOIN lehre.tbl_note USING (note)
LEFT JOIN bis.tbl_bisio ON(uid=tbl_bisio.student_uid)
LEFT JOIN public.tbl_studiengang ON(tbl_student.studiengang_kz=tbl_studiengang.studiengang_kz)
LEFT JOIN bis.tbl_mobilitaet USING(prestudent_id)
LEFT JOIN public.tbl_prestudent USING(prestudent_id)
WHERE uid IN ?";
return $this->execReadOnlyQuery($qry, [$uids]);
}
public function formatZusatz($entry, $erhalter_kz) {
$zusatz = '';
if (isset($entry->studienstatus) && $entry->studienstatus === 'Incoming') {
$zusatz = '(i)';
}
if (isset($entry->lkt_ueberschreibbar) && $entry->lkt_ueberschreibbar === false) {
$zusatz .= ' (' . ($entry->anmerkung ?? '') . ')';
}
if (isset($entry->mitarbeiter_uid) && $entry->mitarbeiter_uid !== null) {
$zusatz .= ' (ma)';
}
if (isset($entry->stg_kz_student) && $entry->stg_kz_student == $erhalter_kz) {
$zusatz .= ' (a.o.)';
}
if (
isset($entry->mobilitaetstyp_kurzbz) && $entry->mobilitaetstyp_kurzbz &&
isset($entry->doubledegree) && $entry->doubledegree === 1
) {
$zusatz .= ' (d.d.';
$ddtype = $entry->ddtype ?? null;
if ($ddtype == 'Intern') {
$zusatz .= 'i.)';
} elseif ($ddtype == 'Extern') {
$zusatz .= 'o.)';
} else {
$zusatz .= ')';
}
}
return $zusatz;
}
}
@@ -100,12 +100,14 @@ class Abschlusspruefung_model extends DB_Model
if (isError($abschlussarbeit))
return $abschlussarbeit;
if (hasData($abschlussarbeit))
{
$abschlussarbeit = getData($abschlussarbeit)[0];
$abschlusspruefungdata->projektarbeit_studiengangstyp_name = $abschlussarbeit->projekttyp_kurzbz;
$abschlusspruefungdata->abschlussarbeit_titel = $abschlussarbeit->titel;
$abschlusspruefungdata->abschlussarbeit_note = $abschlussarbeit->note;
$abschlusspruefungdata->abschlussarbeit_sprache = $abschlussarbeit->sprache_bezeichnung;
}
}
}
@@ -52,4 +52,53 @@ class LePruefung_model extends DB_Model
'student_uid' => $student_uid
]);
}
public function getPruefungenByLvStudiensemester($lv_id, $sem_kurzbz) {
$qry = "SELECT lehre.tbl_pruefung.*, tbl_lehrveranstaltung.bezeichnung as lehrveranstaltung_bezeichnung, tbl_lehrveranstaltung.lehrveranstaltung_id,
tbl_note.bezeichnung as note_bezeichnung, tbl_pruefungstyp.beschreibung as typ_beschreibung, tbl_lehreinheit.studiensemester_kurzbz as studiensemester_kurzbz
FROM lehre.tbl_pruefung, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, lehre.tbl_note, lehre.tbl_pruefungstyp
WHERE lehre.tbl_pruefung.lehreinheit_id=tbl_lehreinheit.lehreinheit_id
AND tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id
AND lehre.tbl_pruefung.note = tbl_note.note
AND lehre.tbl_pruefung.pruefungstyp_kurzbz=tbl_pruefungstyp.pruefungstyp_kurzbz
AND tbl_lehrveranstaltung.lehrveranstaltung_id = ?
AND tbl_lehreinheit.studiensemester_kurzbz = ?
ORDER BY datum DESC;";
return $this->execReadOnlyQuery($qry, array($lv_id, $sem_kurzbz));
}
public function getPruefungenByUidTypLvStudiensemester($uid, $typ = null, $lv_id = null, $sem_kurzbz = null) {
$params = [$uid];
$qry = "SELECT tbl_pruefung.*, tbl_lehrveranstaltung.bezeichnung as lehrveranstaltung_bezeichnung, tbl_lehrveranstaltung.lehrveranstaltung_id,
tbl_note.bezeichnung as note_bezeichnung, tbl_pruefungstyp.beschreibung as typ_beschreibung, tbl_lehreinheit.studiensemester_kurzbz as studiensemester_kurzbz
FROM lehre.tbl_pruefung, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, lehre.tbl_note, lehre.tbl_pruefungstyp
WHERE student_uid= ?
AND tbl_pruefung.lehreinheit_id=tbl_lehreinheit.lehreinheit_id
AND tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id
AND tbl_pruefung.note = tbl_note.note
AND tbl_pruefung.pruefungstyp_kurzbz=tbl_pruefungstyp.pruefungstyp_kurzbz";
if ($typ != null)
{
$qry .= " AND tbl_pruefungstyp.pruefungstyp_kurzbz = ?";
$params[] = $typ;
}
if ($lv_id != null)
{
$qry .= " AND tbl_lehrveranstaltung.lehrveranstaltung_id = ?";
$params[] = $lv_id;
}
if ($sem_kurzbz != null)
{
$qry .= " AND tbl_lehreinheit.studiensemester_kurzbz = ?";
$params[] = $sem_kurzbz;
}
$qry .= " ORDER BY datum DESC";
return $this->execReadOnlyQuery($qry, $params);
}
}
@@ -739,4 +739,26 @@ EOSQL;
)";
}
public function getAllLehreinheitenForLvaAndMaUid($lva_id, $ma_uid, $sem_kurzbz)
{
$query = "SELECT DISTINCT tbl_lehreinheitmitarbeiter.lehreinheit_id, tbl_lehreinheit.lehrveranstaltung_id, tbl_lehreinheit.lehrform_kurzbz,
tbl_lehreinheitmitarbeiter.mitarbeiter_uid,
tbl_lehreinheitgruppe.semester,
tbl_lehreinheitgruppe.verband,
tbl_lehreinheitgruppe.gruppe,
tbl_lehreinheitgruppe.gruppe_kurzbz,
tbl_lehrveranstaltung.kurzbz,
tbl_studiengang.kurzbzlang,
(SELECT COUNT(DISTINCT datum) FROM campus.vw_stundenplan WHERE lehreinheit_id = lehre.tbl_lehreinheit.lehreinheit_id) as termincount,
(SELECT COUNT(*) FROM campus.vw_student_lehrveranstaltung WHERE lehreinheit_id = lehre.tbl_lehreinheit.lehreinheit_id) as studentcount
FROM lehre.tbl_lehreinheit JOIN lehre.tbl_lehreinheitmitarbeiter USING(lehreinheit_id)
JOIN lehre.tbl_lehreinheitgruppe USING(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang ON (tbl_lehreinheitgruppe.studiengang_kz = tbl_studiengang.studiengang_kz)
WHERE lehrveranstaltung_id = ? AND studiensemester_kurzbz = ? AND mitarbeiter_uid = ?
ORDER BY tbl_lehreinheitgruppe.gruppe_kurzbz";
return $this->execQuery($query, [$lva_id, $sem_kurzbz, $ma_uid]);
}
}
@@ -317,7 +317,7 @@ class Lehrveranstaltung_model extends DB_Model
tbl_bisio.bisio_id, tbl_bisio.von, tbl_bisio.bis, tbl_student.studiengang_kz AS stg_kz_student,
tbl_zeugnisnote.note, tbl_mitarbeiter.mitarbeiter_uid, tbl_person.matr_nr, tbl_benutzer.uid,
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as kuerzel, tbl_studiengang.orgform_kurzbz, vw_student_lehrveranstaltung.semester, vw_student_lehrveranstaltung.studiensemester_kurzbz, vw_student_lehrveranstaltung.bezeichnung,
tbl_student.prestudent_id
tbl_student.prestudent_id, campus.vw_student_lehrveranstaltung.lehreinheit_id
FROM
campus.vw_student_lehrveranstaltung
JOIN public.tbl_benutzer USING(uid)
@@ -1346,4 +1346,65 @@ class Lehrveranstaltung_model extends DB_Model
return $this->execQuery($qry, $params);
}
public function getLvForLektorInSemester($sem_kurzbz, $uid) {
$qry = "SELECT DISTINCT (tbl_lehrveranstaltung.lehrveranstaltung_id),
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as stg_kurzbz,
tbl_lehrveranstaltung.semester as lv_semester,
tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung,
(SELECT kurzbz FROM public.tbl_mitarbeiter
WHERE mitarbeiter_uid=tbl_lehreinheitmitarbeiter.mitarbeiter_uid) as lektor
FROM
lehre.tbl_lehreinheit JOIN lehre.tbl_lehreinheitmitarbeiter USING(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang USING(studiengang_kz)
JOIN lehre.tbl_lehrveranstaltung as lehrfach ON(tbl_lehreinheit.lehrfach_id=lehrfach.lehrveranstaltung_id)
WHERE
tbl_lehreinheit.studiensemester_kurzbz = ?
AND mitarbeiter_uid = ?
ORDER BY stg_kurzbz,lv_semester,lv_bezeichnung";
return $this->execReadOnlyQuery($qry, array($sem_kurzbz, $uid));
}
// used for cis4 mylv mitarbeiter
public function getLvsByMitarbeiterInSemester($mitarbeiter_uid, $sem_kurzbz) {
$qry = "SELECT * FROM (
SELECT DISTINCT ON (lehre.tbl_lehrveranstaltung.lehrveranstaltung_id)
public.tbl_studiengang.studiengang_kz,
lehre.tbl_lehrveranstaltung.semester,
public.tbl_studiengang.bezeichnung as sg_bezeichnung,
public.tbl_studiengang.english as sg_bezeichnung_eng,
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as studiengang_kuerzel,
lehre.tbl_lehrveranstaltung.lehrveranstaltung_id,
lehre.tbl_lehrveranstaltung.bezeichnung,
lehre.tbl_lehrveranstaltung.bezeichnung_english as bezeichnung_eng,
lehre.tbl_lehrveranstaltung.farbe,
lehre.tbl_lehrveranstaltung.lvinfo,
lehre.tbl_lehrveranstaltung.benotung,
lehre.tbl_lehrveranstaltung.orgform_kurzbz,
lehre.tbl_lehrveranstaltung.sprache,
lehre.tbl_lehrveranstaltung.ects,
lehre.tbl_lehrveranstaltung.incoming
FROM
lehre.tbl_lehreinheit JOIN lehre.tbl_lehreinheitmitarbeiter USING(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang USING(studiengang_kz)
JOIN lehre.tbl_lehrveranstaltung as lehrfach ON(tbl_lehreinheit.lehrfach_id=lehrfach.lehrveranstaltung_id)
WHERE
tbl_lehreinheit.studiensemester_kurzbz = ?
AND mitarbeiter_uid = ?) as distincted_by_lva_id
JOIN (
SELECT lehrveranstaltung_id, TRUNC(SUM(lehre.tbl_lehreinheitmitarbeiter.semesterstunden)) as semesterstunden
FROM lehre.tbl_lehreinheit
JOIN lehre.tbl_lehreinheitmitarbeiter USING(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
WHERE tbl_lehreinheit.studiensemester_kurzbz = ?
AND mitarbeiter_uid = ?
GROUP BY lehrveranstaltung_id
) semesterstundenAggregatedSubquery USING(lehrveranstaltung_id)
ORDER BY studiengang_kuerzel, semester, bezeichnung";
return $this->execReadOnlyQuery($qry, [$sem_kurzbz, $mitarbeiter_uid, $sem_kurzbz, $mitarbeiter_uid]);
}
}
@@ -14,7 +14,7 @@ class Lvgesamtnote_model extends DB_Model
}
/**
* Laedt die Noten
* Laedt die Noten - lvgesamtnote (Vorschlag) JOIN tbl.note (zeugnisnote)
*
* @param integer $lehrveranstaltung_id
* @param string $student_uid
@@ -46,4 +46,19 @@ class Lvgesamtnote_model extends DB_Model
return $this->loadWhere($where);
}
public function getLvGesamtNoteVorschlag($lehrveranstaltung_id, $student_uid, $studiensemester_kurzbz)
{
$qry = "SELECT * FROM campus.tbl_lvgesamtnote
WHERE campus.tbl_lvgesamtnote.student_uid = ?
AND campus.tbl_lvgesamtnote.studiensemester_kurzbz = ?";
$params = [$student_uid, $studiensemester_kurzbz];
if ($lehrveranstaltung_id) {
$qry .= "AND campus.tbl_lvgesamtnote.lehrveranstaltung_id = ?";
$params[] = $lehrveranstaltung_id;
}
return $this->execReadOnlyQuery($qry, $params);
}
}
+22 -1
View File
@@ -11,12 +11,33 @@ class Note_model extends DB_Model
$this->dbTable = 'lehre.tbl_note';
$this->pk = 'note';
}
public function getAllActive() {
$qry ="SELECT *
FROM lehre.tbl_note
WHERE aktiv = true";
return $this->execReadOnlyQuery($qry);
}
// used to determine the primary key of note "entschuldigt" to avoid hardcoded magic numbers
// that might differ in a different installation of fhcomplete
public function getEntschuldigtNote() {
$qry ="SELECT *
FROM lehre.tbl_note
WHERE bezeichnung = 'entschuldigt'";
return $this->execReadOnlyQuery($qry);
}
// used to determine the primary key of note "noch nicht eingetragen" to avoid hardcoded magic numbers
// that might differ in a different installation of fhcomplete
public function getNochNichtEingetragenNote() {
$qry ="SELECT *
FROM lehre.tbl_note
WHERE bezeichnung = 'Noch nicht eingetragen'";
return $this->execReadOnlyQuery($qry);
}
}
@@ -26,6 +26,9 @@ class Notenschluesselaufteilung_model extends DB_Model
$this->load->model('education/Notenschluesselzuordnung_model', 'NotenschluesselzuordnungModel');
$notenschluessel_kurzbz = $this->NotenschluesselzuordnungModel->getKurzbzForLv($lehrveranstaltung_id, $studiensemester_kurzbz);
if($notenschluessel_kurzbz == null)
return success(null);
$this->addSelect("note");
$this->addOrder("punkte", "DESC");
$this->addLimit(1);
@@ -61,6 +61,174 @@ class Paabgabe_model extends DB_Model
return $this->execReadOnlyQuery($qry, array($person_id));
}
/**
* Gets project submissions for search criteria.
* @param array $projekttyp_kurzbz_arr contains all relevant project types (e.g. Bachelor)
* @param int $studiengang_kz study program
* @param string $abgabetyp_kurzbz project submission type (e.g. end upload, intermediate submission)
* @param string $abgabedatum due date for hand-in
* @param string $personSearchString for searching by person, i.e. name, uid, person/prestudent id
* @param int $limit limiting max number of results if search criteria is not precise enough
* @return object
*/
public function getPaAbgaben(
$projekttyp_kurzbz_arr,
$studiengang_kz = null,
$abgabetyp_kurzbz = null,
$abgabedatum = null,
$personSearchString = null,
$limit = 1000
) {
$params = [];
$qry = "
SELECT
stg.bezeichnung AS stgbez, paabg.datum AS termin,
paabg.paabgabe_id, paabg.projektarbeit_id, paabg.paabgabetyp_kurzbz, paabg.abgabedatum,
abgabetyp.bezeichnung AS paabgabetyp_bezeichnung, ben.uid, pers.vorname, pers.nachname, pa.projekttyp_kurzbz, pa.titel,
UPPER(stg.typ || stg.kurzbz) AS studiengang_kuerzel,
(
/* show all relevant Studiengänge of person and wether it is an employee*/
SELECT
STRING_AGG(studiengang || ' ' || last_status, ' | ')
|| (CASE WHEN EXISTS (
SELECT 1 FROM public.tbl_mitarbeiter ma
JOIN public.tbl_benutzer ben ON ma.mitarbeiter_uid = ben.uid
WHERE person_id = prestudents.person_id
AND ben.aktiv
) THEN ' | Mitarbeiter' ELSE '' END)
FROM (
SELECT
DISTINCT person_id, prestudent_id, UPPER(stg.typ || stg.kurzbz) AS studiengang,
get_rolle_prestudent(ps.prestudent_id, null) AS last_status
FROM
public.tbl_prestudent ps
JOIN public.tbl_studiengang stg USING (studiengang_kz)
WHERE
person_id = pers.person_id
ORDER BY
prestudent_id DESC
) prestudents
WHERE
last_status IN ('Abgewiesener','Aufgenommener', 'Student', 'Incoming', 'Diplomand', 'Abbrecher', 'Unterbrecher', 'Absolvent')
GROUP BY
person_id
LIMIT 1;
) AS status
FROM
lehre.tbl_projektarbeit pa
JOIN campus.tbl_paabgabe paabg USING(projektarbeit_id)
JOIN campus.tbl_paabgabetyp abgabetyp USING(paabgabetyp_kurzbz)
LEFT JOIN public.tbl_benutzer ben ON(uid=student_uid)
LEFT JOIN public.tbl_person pers ON(ben.person_id=pers.person_id)
LEFT JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang stg USING(studiengang_kz)
WHERE
TRUE";
if (isset($projekttyp_kurzbz_arr) && !isEmptyArray($projekttyp_kurzbz_arr))
{
$qry .= " AND projekttyp_kurzbz IN ?";
$params[] = $projekttyp_kurzbz_arr;
}
if (isset($studiengang_kz) && is_numeric($studiengang_kz))
{
$qry .= " AND stg.studiengang_kz=?";
$params[] = $studiengang_kz;
}
if (isset($abgabetyp_kurzbz))
{
$qry .= " AND paabg.paabgabetyp_kurzbz=?";
$params[] = $abgabetyp_kurzbz;
}
if (isset($abgabedatum))
{
$qry .= " AND paabg.datum=?";
$params[] = $abgabedatum;
}
if (is_numeric($personSearchString))
{
$personSearchString = (int) $personSearchString;
$params = array_merge($params, [$personSearchString, $personSearchString]);
$qry .= " AND (
pers.person_id = ?
OR EXISTS (SELECT 1 FROM public.tbl_prestudent WHERE person_id = pers.person_id AND prestudent_id = ?)
)";
}
elseif (is_string($personSearchString))
{
// remove empty spaces and lowercase
$personSearchString = strtolower(str_replace(' ', '', $personSearchString));
$qry .= " AND (
LOWER(REPLACE(pers.nachname || pers.vorname || pers.nachname, ' ', '')) LIKE ".$this->db->escape('%'.$personSearchString.'%')."
OR ben.uid LIKE ".$this->db->escape('%'.$personSearchString.'%')."
)";
}
$qry .= " ORDER BY nachname";
if (isset($limit) && is_numeric($limit) && (!isset($studiengang_kz) || !is_numeric($studiengang_kz)) && !isset($abgabedatum))
{
$qry .= " LIMIT ?";
$params[] = $limit;
}
return $this->execReadOnlyQuery($qry, $params);
}
/**
* Gets due dates for projekt submission search criteria.
* @param array $projekttyp_kurzbz_arr contains all relevant project types (e.g. Bachelor)
* @param int $studiengang_kz study program
* @param string $abgabetyp_kurzbz project submission type (e.g. end upload, intermediate submission)
* @return object
*/
public function getTermine($projekttyp_kurzbz_arr, $studiengang_kz, $abgabetyp_kurzbz)
{
$params = [];
$qry = "
SELECT
DISTINCT tbl_paabgabe.datum as termin, to_char(tbl_paabgabe.datum, 'DD.MM.YYYY') as termin_anzeige
FROM
lehre.tbl_projektarbeit
JOIN campus.tbl_paabgabe USING(projektarbeit_id)
LEFT JOIN public.tbl_benutzer ON(uid=student_uid)
LEFT JOIN public.tbl_person ON(tbl_benutzer.person_id=tbl_person.person_id)
LEFT JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang USING(studiengang_kz)
WHERE
TRUE";
if (isset($projekttyp_kurzbz_arr) && !isEmptyArray($projekttyp_kurzbz_arr))
{
$qry .= " AND projekttyp_kurzbz IN ?";
$params[] = $projekttyp_kurzbz_arr;
}
if (isset($studiengang_kz) && is_numeric($studiengang_kz))
{
$qry .= " AND public.tbl_studiengang.studiengang_kz=?";
$params[] = $studiengang_kz;
}
if (isset($abgabetyp_kurzbz))
{
$qry .= " AND campus.tbl_paabgabe.paabgabetyp_kurzbz=?";
$params[] = $abgabetyp_kurzbz;
}
$qry .= " ORDER BY termin DESC";
return $this->execReadOnlyQuery($qry, $params);
}
public function findAbgabenNewOrUpdatedSince($interval, $relevantTypes)
{
@@ -23,9 +23,11 @@ class Projektarbeit_model extends DB_Model
*/
public function getProjektarbeit($student_uid, $studiengang_kz = null, $studiensemester_kurzbz = null, $projekttyp = null, $final = null)
{
$sprache_index = "COALESCE((SELECT index FROM public.tbl_sprache WHERE sprache=" . $this->escape(getUserLanguage()) . " LIMIT 1), 1)";
$qry = "SELECT
pa.*, tbl_projekttyp.bezeichnung,
tbl_lehreinheit.studiensemester_kurzbz, tbl_lehrveranstaltung.lehrveranstaltung_id,
tbl_sprache.bezeichnung[".$sprache_index."] AS sprache_bezeichnung,
tbl_firma.name AS firma_name,
(
SELECT
@@ -44,6 +46,7 @@ class Projektarbeit_model extends DB_Model
JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_firma USING (firma_id)
LEFT JOIN public.tbl_sprache ON pa.sprache = tbl_sprache.sprache
WHERE
pa.student_uid = ?";
@@ -306,4 +306,5 @@ class Pruefung_model extends DB_Model
return $this->loadWhereCommitteeExamsFailed();
}
}
@@ -59,6 +59,37 @@ class Studienplan_model extends DB_Model
'tbl_studienplan_lehrveranstaltung.semester' => $semester
));
}
public function getStudienplanByLvaSemKurzbz($lehrveranstaltung_id, $studiensemester_kurzbz) {
$qry= "
SELECT
DISTINCT tbl_studienplan.*
FROM
lehre.tbl_studienplan
JOIN lehre.tbl_studienplan_lehrveranstaltung
USING(studienplan_id)
WHERE
tbl_studienplan_lehrveranstaltung.lehrveranstaltung_id IN (
SELECT
lv.lehrveranstaltung_id
FROM
lehre.tbl_lehrveranstaltung AS lv
LEFT JOIN lehre.tbl_lehrveranstaltung AS t ON t.lehrveranstaltung_id=lv.lehrveranstaltung_template_id
WHERE
lv.lehrtyp_kurzbz<>'tpl'
AND (lv.lehrveranstaltung_id= ? OR (lv.lehrveranstaltung_template_id= ? AND t.lehrtyp_kurzbz='tpl'))
)
AND EXISTS (
SELECT 1
FROM
lehre.tbl_studienplan_semester
WHERE studienplan_id=tbl_studienplan.studienplan_id
AND studiensemester_kurzbz= ?
AND semester = tbl_studienplan_lehrveranstaltung.semester)
ORDER BY bezeichnung";
return $this->execReadOnlyQuery($qry, array($lehrveranstaltung_id, $lehrveranstaltung_id, $studiensemester_kurzbz));
}
public function getStudienplanLehrveranstaltungForPrestudent($studienplan_id, $semester, $prestudent_id)
{
@@ -242,6 +242,30 @@ class Studiensemester_model extends DB_Model
return $this->loadWhere(['uid' => $student_uid, 'v.lehre' => true]);
}
public function getWhereMitarbeiterHasLvs($uid) {
// first order by year with last 2 letter from right,
// then order by WS/SS inside the years
// query it asc so the ordering magic in cis4 turns it around again
$qry = "WITH unique_semesters AS (
SELECT DISTINCT ON (studiensemester_kurzbz)
studiensemester_kurzbz,
start,
ende,
bezeichnung,
studienjahr_kurzbz
FROM lehre.tbl_lehreinheit
JOIN lehre.tbl_lehreinheitmitarbeiter USING(lehreinheit_id)
JOIN public.tbl_studiensemester USING(studiensemester_kurzbz)
WHERE mitarbeiter_uid = ?
)
SELECT * FROM unique_semesters
ORDER BY
RIGHT(studiensemester_kurzbz, 2) ASC,
LEFT(studiensemester_kurzbz, 2) ASC;";
return $this->execReadOnlyQuery($qry, [$uid]);
}
public function getAktAndFutureSemester()
{
$query = 'SELECT studiensemester_kurzbz
@@ -350,5 +350,64 @@ class Benutzerfunktion_model extends DB_Model
return success($funktionJson);
}
/**
* Gets all Prestudents with details for a given Benutzerfunktion and optionally semester
*
* @param String $studiensemester_kurzbz
* @return object |null
*/
public function getPrestudentsOfJgv($semester)
{
$query = "
SELECT DISTINCT ps.prestudent_id, bf.datum_von, bf.datum_bis
FROM public.tbl_benutzerfunktion bf
JOIN public.tbl_benutzer bn USING (uid)
JOIN public.tbl_prestudent ps USING (person_id)
JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id)
JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz)
WHERE ss.studiensemester_kurzbz = ?
AND bf.funktion_kurzbz = 'jgv'
AND (
bf.datum_von <= ss.ende
AND (
bf.datum_bis >= ss.start
OR bf.datum_bis IS NULL
)
)
";
return $this->execQuery($query, array($semester));
}
/**
* Checks if a certain prestudent has the Benutzerfunktion jgv for a certain semester
*
* @param String $studiensemester_kurzbz
* @param $prestudent_id
* @return object |null
*/
public function isJgv($semester, $prestudent_id)
{
$query = "
SELECT ps.prestudent_id, ss.start as von, ss.ende as bis
FROM public.tbl_benutzerfunktion bf
JOIN public.tbl_benutzer bn USING (uid)
JOIN public.tbl_prestudent ps USING (person_id)
JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id)
JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz)
WHERE ss.studiensemester_kurzbz = ?
AND bf.funktion_kurzbz = 'jgv'
AND (
bf.datum_von <= ss.ende
AND (
bf.datum_bis >= ss.start
OR bf.datum_bis IS NULL
)
)
AND ps.prestudent_id = ?
";
return $this->execQuery($query, array($semester, $prestudent_id));
}
}
+55
View File
@@ -296,4 +296,59 @@ class Notiz_model extends DB_Model
return $this->loadWhere(array('anrechnung_id' => $anrechnung_id));
}
/**
* check if a given Tag for a certain notizzuordnung id is valid
*
* @param $tag typ_kurzbz to check
* @param $typeId typeId to check
* @param $id id to check
* @param $von start of time period or NULL
* @param $bis end of time period or NULL
*
* @return array
*/
public function checkIfExistingTag($tag, $typeId, $id, $von=null, $bis=null)
{
$query = "
SELECT *
FROM public.tbl_notiz
JOIN public.tbl_notizzuordnung nz USING (notiz_id)
WHERE typ = ?
AND {$typeId} = ?
AND (
start IS NULL
OR ende IS NULL
OR (start <= ? AND ende >= ?)
)
";
return $this->execQuery($query, [$tag, $id, $bis, $von]);
}
/**
* returns all existing tags of a certain tag within a time period
*
* @param $tag typ_kurzbz of tag
* @param $von start of time period or NULL
* @param $bis end of time period or NULL
*
* @return array
*/
public function getAllTags($tag, $von=null, $bis=null)
{
$query = "
SELECT *
FROM public.tbl_notiz
JOIN public.tbl_notizzuordnung nz USING (notiz_id)
WHERE typ = ?
AND (
start IS NULL
OR ende IS NULL
OR (start <= ? AND ende >= ?)
);
";
return $this->execQuery($query, array($tag, $bis, $von));
}
}
+50 -1
View File
@@ -149,7 +149,7 @@ class Person_model extends DB_Model
* @param $filter Term to search for.
* @return DB-result
*/
public function searchPerson($filter)
public function searchPerson($filter, $mode=null)
{
$this->addSelect('vorname, nachname, gebdatum, person_id, titelpre, titelpost');
$this->addSelect("CASE
@@ -161,6 +161,26 @@ class Person_model extends DB_Model
THEN 'Student'
ELSE 'Person'
END AS status");
if($mode == 'mitMaUid')
{
$this->addSelect("(
SELECT m.mitarbeiter_uid
FROM public.tbl_benutzer b
JOIN public.tbl_mitarbeiter m
ON b.uid = m.mitarbeiter_uid
WHERE b.person_id = tbl_person.person_id
LIMIT 1
)
AS uid");
$this->addOrder('uid, lower(nachname), lower(vorname)');
}
else
{
$this->addOrder('lower(nachname), lower(vorname)');
}
$result = $this->loadWhere(
'lower(nachname) like '.$this->db->escape('%'.mb_strtolower($filter).'%')."
OR lower(vorname) like ".$this->db->escape('%'.$filter.'%')."
@@ -433,4 +453,33 @@ class Person_model extends DB_Model
return $this->execReadOnlyQuery($qry, [$person_id]);
}
//just a test function for a person_id tag
//alle personen die innerhalb dieses Zeitraumens 55 werden
public function getFiftyFivers($von, $bis)
{
$qry = "
SELECT
p.person_id
FROM public.tbl_person p
WHERE p.gebdatum >= DATE ? - INTERVAL '55 years'
AND p.gebdatum <= DATE ? - INTERVAL '55 years';
";
return $this->execReadOnlyQuery($qry, [$von, $bis]);
}
//just a test function for a person_id tag
//check if Person gets 55 in this time
public function isFiftyFive($von, $bis, $person_id)
{
$qry = "
SELECT
p.person_id
FROM public.tbl_person p
WHERE p.gebdatum >= DATE ? - INTERVAL '55 years'
AND p.gebdatum <= DATE ? - INTERVAL '55 years'
AND p.persond_id = ?;
";
return $this->execReadOnlyQuery($qry, [$von, $bis, $person_id]);
}
}
@@ -50,11 +50,12 @@ class Reservierung_model extends DB_Model
$query_result = $this->execReadOnlyQuery("
SELECT
'reservierung' as type, beginn, ende, datum,
DISTINCT(insertvon),
'reservierung' as type, beginn, ende, datum, array_agg(DISTINCT reservierung_id) AS reservierung_id,
COALESCE(titel, beschreibung) as topic,
array_agg(DISTINCT mitarbeiter_kurzbz) as lektor,
array_agg(DISTINCT (gruppe,verband,semester,studiengang_kz,gruppen_kuerzel)) as gruppe,
array_agg(DISTINCT(uid)) as uids,
ort_kurzbz, 'FFFFFF' as farbe
FROM
@@ -62,7 +63,7 @@ class Reservierung_model extends DB_Model
" . $subquery . "
) AS subquery
GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung
GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung, insertvon
ORDER BY datum, beginn
", is_null($ort_kurzbz) ? [$uid ?? getAuthUID(), $uid ?? getAuthUID(), $start_date, $end_date] : [$ort_kurzbz, $start_date, $end_date]);
@@ -94,11 +95,12 @@ class Reservierung_model extends DB_Model
$query_result = $this->execReadOnlyQuery("
SELECT
DISTINCT(insertvon),
'reservierung' as type, beginn, ende, datum,
COALESCE(titel, beschreibung) as topic,
array_agg(DISTINCT mitarbeiter_kurzbz) as lektor,
array_agg(DISTINCT (gruppe,verband,semester,studiengang_kz,gruppen_kuerzel)) as gruppe,
array_agg(DISTINCT(uid)) as uids,
ort_kurzbz, 'FFFFFF' as farbe
FROM
@@ -106,7 +108,7 @@ class Reservierung_model extends DB_Model
" . $subquery . "
) AS subquery
GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung
GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung, insertvon
ORDER BY datum, beginn
", [$uid ?? getAuthUID(), $start_date, $end_date]);
@@ -12,6 +12,8 @@ class Zeitsperre_model extends DB_Model
$this->pk = 'zeitsperre_id';
}
const BLOCKIERENDE_ZEITSPERREN = ['Krank','Urlaub','ZA','DienstV','PflegeU','DienstF','CovidSB','CovidKS'];
/**
* Save or update Zeitsperre.
*
@@ -61,4 +63,128 @@ class Zeitsperre_model extends DB_Model
return $this->execQuery($qry);
}
/**
* get Zeitsperren of a user
*
* @param $uid mitarbeiteruid
* @param $bisgrenze @true show only entries of actual business year (1.9.- 31.8.)
*
* @return array
*/
public function getZeitsperrenUser($uid, $bisgrenze = true)
{
$qry = "
SELECT
tbl_zeitsperre.*, tbl_zeitsperretyp.*, tbl_erreichbarkeit.farbe AS erreichbarkeit_farbe,
tbl_erreichbarkeit.beschreibung AS erreichbarkeit_beschreibung,
CONCAT (ps.vorname, ' ', ps.nachname) as vertretung
FROM (campus.tbl_zeitsperre JOIN campus.tbl_zeitsperretyp USING (zeitsperretyp_kurzbz))
LEFT JOIN campus.tbl_erreichbarkeit USING (erreichbarkeit_kurzbz)
LEFT JOIN public.tbl_benutzer ON campus.tbl_zeitsperre.vertretung_uid = public.tbl_benutzer.uid
LEFT JOIN public.tbl_person ps USING (person_id)
WHERE mitarbeiter_uid= ?
";
if($bisgrenze)
{
$qry.="
AND (
(date_part('month',vondatum)>=9 AND date_part('year', vondatum)>='".(date('Y')-1)."')
OR
(date_part('month',vondatum)<9 AND date_part('year', vondatum)>='".(date('Y'))."')
)";
}
$qry.= " ORDER BY vondatum DESC";
return $this->execQuery($qry, array('mitarbeiter_uid' => $uid));
}
/**
* check a date for existing zeitsperre
*
* @param $uid mitarbeiteruid
* @param $datum datum to check
* @param $stunde stunde (default = null)
* @param bool $nurblockierend if only hr relevante zeitsperren have to be checked
*
* @return array
*/
public function getSperreByDate($uid, $datum, $stunde = null, $nurblockierend = false)
{
$parametersArray = [$datum, $datum];
$qry = "
SELECT
*
FROM
campus.tbl_zeitsperre
WHERE
vondatum <= ?
AND bisdatum>= ?";
if($nurblockierend)
{
$qry .= " AND zeitsperretyp_kurzbz IN ('"
. implode("','", self::BLOCKIERENDE_ZEITSPERREN)
. "')";
}
if(!is_null($stunde))
{
$parametersArray = array_merge(
$parametersArray,
[$datum, $stunde, $datum, $datum, $stunde, $datum]
);
$qry.=" AND
((vondatum= ? AND vonstunde<= ? OR vonstunde is null OR vondatum<> ?) AND
(bisdatum= ? AND bisstunde>= ? OR bisstunde is null OR bisdatum<> ?))";
}
array_push($parametersArray, $uid);
$qry .= "AND mitarbeiter_uid= ? ";
return $this->execQuery($qry, $parametersArray);
}
/**
* check a date for existing zeitsperre
*
* @param $uid mitarbeiteruid
* @param $vondatum datum in Format IS0
* @param $bisdatum datum in Format ISO
*
* @return array
*/
public function existsZeitaufzeichnung($uid, $vonDay, $bisDay)
{
try {
$from = new DateTime($vonDay);
$to = new DateTime($bisDay);
} catch (Exception $e) {
throw new Exception("Invalid date format");
}
//remove hour stamps
$from->setTime(0, 0, 0);
$to->setTime(0, 0, 0)->modify('+1 day');
$fromSql = $from->format('Y-m-d');
$toSql = $to->format('Y-m-d');
$params = [$uid, $fromSql, $toSql];
$qry = "
SELECT *
FROM campus.tbl_zeitaufzeichnung
WHERE uid = ?
AND start >= ?
AND ende < ? ";
$result = $this->execQuery($qry, $params);
return $result;
}
}
@@ -11,6 +11,7 @@ $includesArray = array(
'skipID' => '#fhccontent',
'vuedatepicker11' => true,
'customCSSs' => array(
'vendor/vuejs/vuedatepicker_css/main.css',
'public/css/components/verticalsplit.css',
'public/css/components/searchbar/searchbar.css',
'public/css/Fhc.css',
@@ -23,7 +24,9 @@ $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',
'public/css/Cis4/Benotungstool.css',
'public/css/Cis4/Zeitsperren.css',
),
'customJSs' => array(
'vendor/npm-asset/primevue/accordion/accordion.min.js',
@@ -37,11 +40,15 @@ $includesArray = array(
'vendor/npm-asset/primevue/timeline/timeline.min.js',
'vendor/npm-asset/primevue/inplace/inplace.min.js',
'vendor/npm-asset/primevue/message/message.min.js',
'vendor/npm-asset/primevue/divider/divider.min.js',
'vendor/npm-asset/primevue/password/password.js',
'vendor/npm-asset/primevue/multiselect/multiselect.js',
'vendor/npm-asset/primevue/tieredmenu/tieredmenu.js',
'vendor/moment/luxonjs/luxon.min.js'
),
'customJSModules' => array(
'public/js/apps/Cis/Cis.js',
'vendor/olifolkerd/tabulator5/src/js/modules/ColumnCalcs/ColumnCalcs.js'
),
);
@@ -1,7 +1,5 @@
<?php
$this->load->view(
'templates/FHC-Header',
array(
$includesArray = array(
'title' => 'Lehrauftrag bestellen',
'jquery3' => true,
'jqueryui1' => true,
@@ -12,8 +10,15 @@ $this->load->view(
'dialoglib' => true,
'navigationwidget' => true,
'addons' => true,
)
);
$this->load->view(
'templates/FHC-Header',
$includesArray
);
?>
<?php echo $this->widgetlib->widget('NavigationWidget'); ?>
@@ -33,4 +38,12 @@ $this->load->view(
</div>
</div>
<?php $this->load->view('templates/FHC-Footer'); ?>
<?php
$this->load->view(
'templates/FHC-Footer',
$includesArray
);
?>
@@ -1,7 +1,6 @@
<?php
$this->load->view(
'templates/FHC-Header',
array(
$includesArray = array(
'title' => 'Lehrauftrag bestellen',
'jquery3' => true,
'bootstrap3' => true,
@@ -9,8 +8,13 @@ $this->load->view(
'sbadmintemplate3' => true,
'ajaxlib' => true,
'navigationwidget' => true,
)
);
$this->load->view(
'templates/FHC-Header',
$includesArray
);
?>
<?php echo $this->widgetlib->widget('NavigationWidget'); ?>
@@ -34,4 +38,11 @@ $this->load->view(
</div>
</div>
<?php $this->load->view('templates/FHC-Footer'); ?>
<?php
$this->load->view(
'templates/FHC-Footer',
$includesArray
);
?>
@@ -1,89 +1,101 @@
<?php
$this->load->view(
'templates/FHC-Header',
array(
'title' => 'Lehrauftrag annehmen',
'jquery3' => true,
'jqueryui1' => true,
'jquerycheckboxes1' => true,
'bootstrap5' => true,
'fontawesome6' => true,
'sbadmintemplate' => false,
'tabulator5' => true,
'tabulator5JQuery' => true,
'cis'=>true,
'momentjs2' => true,
'ajaxlib' => true,
'dialoglib' => true,
'tablewidget' => true,
'phrases' => array(
'global' => array(
'lehrauftraegeAnnehmen',
'dokumentePDF',
'PDFLehrauftraegeFH',
'PDFLehrauftraegeLehrgaenge'
),
'ui' => array(
'anzeigen',
'alleAnzeigen',
'nurBestellteAnzeigen',
'nurErteilteAnzeigen',
'nurAngenommeneAnzeigen',
'nurStornierteAnzeigen',
'hilfeZuDieserSeite',
'alleAuswaehlen',
'alleAbwaehlen',
'ausgewaehlteZeilen',
'hilfe',
'tabelleneinstellungen',
'keineDatenVorhanden',
'spaltenEinstellen',
'bestelltVon',
'erteiltVon',
'angenommenVon',
'storniertVon',
'lehrauftragInBearbeitung',
'wartetAufErteilung',
'wartetAufErneuteErteilung',
'letzterStatusBestellt',
'letzterStatusErteilt',
'letzterStatusAngenommen',
'vertragWurdeStorniert',
),
'password' => array('password'),
'dms' => array('informationsblattExterneLehrende'),
'table' => array(
'spaltenEinAusblenden',
'spaltenEinAusblendenMitKlickOeffnen',
'spaltenEinAusblendenAufEinstellungenKlicken',
'spaltenEinAusblendenMitKlickAktivieren',
'spaltenEinAusblendenMitKlickSchliessen',
'spaltenbreiteVeraendern',
'spaltenbreiteVeraendernText',
'spaltenbreiteVeraendernInfotext',
'zeilenAuswaehlen',
'zeilenAuswaehlenEinzeln',
'zeilenAuswaehlenBereich',
'zeilenAuswaehlenAlle'
),
'lehre' => array(
'lehrauftraegeAnnehmen',
'lehrauftraegeAnnehmenText',
'lehrauftraegeAnnehmenKlickStatusicon',
'lehrauftraegeAnnehmenLehrauftraegeWaehlen',
'lehrauftraegeAnnehmenMitKlickAnnehmen',
'lehrauftraegeNichtAuswaehlbar',
'lehrauftraegeNichtAuswaehlbarTextBeiAnnahme',
'filterAlleBeiAnnahme',
'filterErteiltBeiAnnahme',
'filterAngenommen'
)
$includesArray = array(
'title' => 'Lehrauftrag annehmen',
'jquery3' => true,
'jqueryui1' => true,
'jquerycheckboxes1' => true,
'bootstrap5' => true,
'fontawesome6' => true,
'sbadmintemplate' => false,
'tabulator5' => true,
'tabulator5JQuery' => true,
'cis'=>true,
'momentjs2' => true,
'ajaxlib' => true,
'dialoglib' => true,
'tablewidget' => true,
'phrases' => array(
'global' => array(
'lehrauftraegeAnnehmen',
'dokumentePDF',
'PDFLehrauftraegeFH',
'PDFLehrauftraegeLehrgaenge'
),
'customJSs' => array(
'public/js/bootstrapper.js',
'public/js/lehre/lehrauftrag/acceptLehrauftrag.js')
'ui' => array(
'anzeigen',
'alleAnzeigen',
'nurBestellteAnzeigen',
'nurErteilteAnzeigen',
'nurAngenommeneAnzeigen',
'nurStornierteAnzeigen',
'hilfeZuDieserSeite',
'alleAuswaehlen',
'alleAbwaehlen',
'ausgewaehlteZeilen',
'hilfe',
'tabelleneinstellungen',
'keineDatenVorhanden',
'spaltenEinstellen',
'bestelltVon',
'erteiltVon',
'angenommenVon',
'storniertVon',
'lehrauftragInBearbeitung',
'wartetAufErteilung',
'wartetAufErneuteErteilung',
'letzterStatusBestellt',
'letzterStatusErteilt',
'letzterStatusAngenommen',
'vertragWurdeStorniert',
),
'password' => array('password'),
'dms' => array('informationsblattExterneLehrende'),
'table' => array(
'spaltenEinAusblenden',
'spaltenEinAusblendenMitKlickOeffnen',
'spaltenEinAusblendenAufEinstellungenKlicken',
'spaltenEinAusblendenMitKlickAktivieren',
'spaltenEinAusblendenMitKlickSchliessen',
'spaltenbreiteVeraendern',
'spaltenbreiteVeraendernText',
'spaltenbreiteVeraendernInfotext',
'zeilenAuswaehlen',
'zeilenAuswaehlenEinzeln',
'zeilenAuswaehlenBereich',
'zeilenAuswaehlenAlle'
),
'lehre' => array(
'lehrauftraegeAnnehmen',
'lehrauftraegeAnnehmenText',
'lehrauftraegeAnnehmenKlickStatusicon',
'lehrauftraegeAnnehmenLehrauftraegeWaehlen',
'lehrauftraegeAnnehmenMitKlickAnnehmen',
'lehrauftraegeNichtAuswaehlbar',
'lehrauftraegeNichtAuswaehlbarTextBeiAnnahme',
'filterAlleBeiAnnahme',
'filterErteiltBeiAnnahme',
'filterAngenommen'
)
),
'customJSs' => array(
'public/js/bootstrapper.js',
'public/js/lehre/lehrauftrag/acceptLehrauftrag.js'
)
);
if (defined("CIS4")) {
$this->load->view(
'templates/CISVUE-Header',
$includesArray
);
} else {
$this->load->view(
'templates/FHC-Header',
$includesArray
);
}
?>
@@ -230,5 +242,17 @@ $this->load->view(
</div><!-- end page-wrapper -->
<br>
<?php $this->load->view('templates/FHC-Footer'); ?>
<?php
if (defined("CIS4")) {
$this->load->view(
'templates/CISVUE-Footer',
$includesArray
);
} else {
$this->load->view(
'templates/FHC-Footer',
$includesArray
);
}
?>
@@ -1,98 +1,101 @@
<?php
$includesArray = array(
'title' => 'Lehrauftrag erteilen',
'jquery3' => true,
'jqueryui1' => true,
'jquerycheckboxes1' => true,
'bootstrap3' => true,
'fontawesome6' => true,
'sbadmintemplate3' => true,
'tabulator5' => true,
'tabulator5JQuery' => true,
'momentjs2' => true,
'ajaxlib' => true,
'dialoglib' => true,
'tablewidget' => true,
'navigationwidget' => true,
'phrases' => array(
'global' => array(
'lehrauftraegeErteilen',
'mehrHilfe',
'weitereInformationenUnter'
),
'ui' => array(
'anzeigen',
'alleAnzeigen',
'nurNeueAnzeigen',
'nurBestellteAnzeigen',
'nurErteilteAnzeigen',
'nurAngenommeneAnzeigen',
'nurGeaenderteAnzeigen',
'nurDummiesAnzeigen',
'hilfeZuDieserSeite',
'alleAuswaehlen',
'alleAbwaehlen',
'ausgewaehlteZeilen',
'hilfe',
'tabelleneinstellungen',
'keineDatenVorhanden',
'spaltenEinstellen',
'bestelltVon',
'erteiltVon',
'angenommenVon',
'stundenStundensatzGeaendert',
'neuerLehrauftragOhneLektorVerplant',
'wartetAufBestellung',
'wartetAufErneuteBestellung',
'neuerLehrauftragWartetAufBestellung',
'letzterStatusBestellt',
'letzterStatusErteilt',
'letzterStatusAngenommen',
),
'table' => array(
'spaltenEinAusblenden',
'spaltenEinAusblendenMitKlickOeffnen',
'spaltenEinAusblendenAufEinstellungenKlicken',
'spaltenEinAusblendenMitKlickAktivieren',
'spaltenEinAusblendenMitKlickSchliessen',
'spaltenbreiteVeraendern',
'spaltenbreiteVeraendernText',
'spaltenbreiteVeraendernInfotext',
'zeilenAuswaehlen',
'zeilenAuswaehlenEinzeln',
'zeilenAuswaehlenBereich',
'zeilenAuswaehlenAlle'
),
'lehre' => array(
'lehrauftragStandardBestellprozess',
'lehrauftragStandardBestellprozessBestellen',
'lehrauftragStandardBestellprozessErteilen',
'lehrauftragStandardBestellprozessAnnehmen',
'lehrauftraegeErteilen',
'lehrauftraegeErteilenText',
'lehrauftraegeErteilenKlickStatusicon',
'lehrauftraegeErteilenLehrauftraegeWaehlen',
'lehrauftraegeErteilenMitKlickErteilen',
'geaenderteLehrauftraege',
'geaenderteLehrauftraegeTextBeiErteilung',
'lehrauftraegeNichtAuswaehlbar',
'lehrauftraegeNichtAuswaehlbarTextBeiErteilung',
'filterAlle',
'filterNeu',
'filterBestellt',
'filterErteilt',
'filterAngenommen',
'filterGeaendert',
'filterDummies'
)
),
'customJSs' => array(
'public/js/bootstrapper.js',
'public/js/lehre/lehrauftrag/approveLehrauftrag.js'
)
);
$this->load->view(
'templates/FHC-Header',
array(
'title' => 'Lehrauftrag erteilen',
'jquery3' => true,
'jqueryui1' => true,
'jquerycheckboxes1' => true,
'bootstrap3' => true,
'fontawesome6' => true,
'sbadmintemplate3' => true,
'tabulator5' => true,
'tabulator5JQuery' => true,
'momentjs2' => true,
'ajaxlib' => true,
'dialoglib' => true,
'tablewidget' => true,
'navigationwidget' => true,
'phrases' => array(
'global' => array(
'lehrauftraegeErteilen',
'mehrHilfe',
'weitereInformationenUnter'
),
'ui' => array(
'anzeigen',
'alleAnzeigen',
'nurNeueAnzeigen',
'nurBestellteAnzeigen',
'nurErteilteAnzeigen',
'nurAngenommeneAnzeigen',
'nurGeaenderteAnzeigen',
'nurDummiesAnzeigen',
'hilfeZuDieserSeite',
'alleAuswaehlen',
'alleAbwaehlen',
'ausgewaehlteZeilen',
'hilfe',
'tabelleneinstellungen',
'keineDatenVorhanden',
'spaltenEinstellen',
'bestelltVon',
'erteiltVon',
'angenommenVon',
'stundenStundensatzGeaendert',
'neuerLehrauftragOhneLektorVerplant',
'wartetAufBestellung',
'wartetAufErneuteBestellung',
'neuerLehrauftragWartetAufBestellung',
'letzterStatusBestellt',
'letzterStatusErteilt',
'letzterStatusAngenommen',
),
'table' => array(
'spaltenEinAusblenden',
'spaltenEinAusblendenMitKlickOeffnen',
'spaltenEinAusblendenAufEinstellungenKlicken',
'spaltenEinAusblendenMitKlickAktivieren',
'spaltenEinAusblendenMitKlickSchliessen',
'spaltenbreiteVeraendern',
'spaltenbreiteVeraendernText',
'spaltenbreiteVeraendernInfotext',
'zeilenAuswaehlen',
'zeilenAuswaehlenEinzeln',
'zeilenAuswaehlenBereich',
'zeilenAuswaehlenAlle'
),
'lehre' => array(
'lehrauftragStandardBestellprozess',
'lehrauftragStandardBestellprozessBestellen',
'lehrauftragStandardBestellprozessErteilen',
'lehrauftragStandardBestellprozessAnnehmen',
'lehrauftraegeErteilen',
'lehrauftraegeErteilenText',
'lehrauftraegeErteilenKlickStatusicon',
'lehrauftraegeErteilenLehrauftraegeWaehlen',
'lehrauftraegeErteilenMitKlickErteilen',
'geaenderteLehrauftraege',
'geaenderteLehrauftraegeTextBeiErteilung',
'lehrauftraegeNichtAuswaehlbar',
'lehrauftraegeNichtAuswaehlbarTextBeiErteilung',
'filterAlle',
'filterNeu',
'filterBestellt',
'filterErteilt',
'filterAngenommen',
'filterGeaendert',
'filterDummies'
)
),
'customJSs' => array(
'public/js/bootstrapper.js',
'public/js/lehre/lehrauftrag/approveLehrauftrag.js'
)
)
'templates/FHC-Header',
$includesArray
);
?>
@@ -208,5 +211,13 @@ $this->load->view(
</div><!-- end page-wrapper -->
<br>
<?php $this->load->view('templates/FHC-Footer'); ?>
<?php
$this->load->view(
'templates/FHC-Footer',
$includesArray
);
?>
@@ -1,98 +1,101 @@
<?php
$includesArray = array(
'title' => 'Lehrauftrag bestellen',
'jquery3' => true,
'jqueryui1' => true,
'jquerycheckboxes1' => true,
'bootstrap3' => true,
'fontawesome6' => true,
'sbadmintemplate3' => true,
'tabulator5' => true,
'tabulator5JQuery' => true,
'momentjs2' => true,
'ajaxlib' => true,
'dialoglib' => true,
'tablewidget' => true,
'navigationwidget' => true,
'phrases' => array(
'global' => array(
'lehrauftraegeBestellen',
'mehrHilfe',
'weitereInformationenUnter'
),
'ui' => array(
'anzeigen',
'alleAnzeigen',
'nurNeueAnzeigen',
'nurBestellteAnzeigen',
'nurErteilteAnzeigen',
'nurAngenommeneAnzeigen',
'nurGeaenderteAnzeigen',
'nurDummiesAnzeigen',
'hilfeZuDieserSeite',
'alleAuswaehlen',
'alleAbwaehlen',
'ausgewaehlteZeilen',
'hilfe',
'tabelleneinstellungen',
'keineDatenVorhanden',
'spaltenEinstellen',
'bestelltVon',
'erteiltVon',
'angenommenVon',
'neuerLehrauftragOhneLektorVerplant',
'neuerLehrauftragWartetAufBestellung',
'letzterStatusBestellt',
'letzterStatusErteilt',
'letzterStatusAngenommen',
'nachAenderungStundensatzStunden',
'vorAenderungStundensatzStunden'
),
'table' => array(
'spaltenEinAusblenden',
'spaltenEinAusblendenMitKlickOeffnen',
'spaltenEinAusblendenAufEinstellungenKlicken',
'spaltenEinAusblendenMitKlickAktivieren',
'spaltenEinAusblendenMitKlickSchliessen',
'spaltenbreiteVeraendern',
'spaltenbreiteVeraendernText',
'spaltenbreiteVeraendernInfotext',
'zeilenAuswaehlen',
'zeilenAuswaehlenEinzeln',
'zeilenAuswaehlenBereich',
'zeilenAuswaehlenAlle'
),
'lehre' => array(
'lehrauftragStandardBestellprozess',
'lehrauftragStandardBestellprozessBestellen',
'lehrauftragStandardBestellprozessErteilen',
'lehrauftragStandardBestellprozessAnnehmen',
'lehrauftraegeBestellen',
'lehrauftraegeBestellenText',
'lehrauftraegeBestellenKlickStatusicon',
'lehrauftraegeBestellenLehrauftraegeWaehlen',
'lehrauftraegeBestellenMitKlickBestellen',
'lehrauftraegeBestellenVertragWirdAngelegt',
'geaenderteLehrauftraege',
'geaenderteLehrauftraegeText',
'lehrauftraegeNichtAuswaehlbar',
'lehrauftraegeNichtAuswaehlbarText',
'filterAlle',
'filterNeu',
'filterBestellt',
'filterErteilt',
'filterAngenommen',
'filterGeaendert',
'filterDummies'
)
),
'customJSs' => array(
'public/js/bootstrapper.js',
'public/js/lehre/lehrauftrag/orderLehrauftrag.js'
)
);
$this->load->view(
'templates/FHC-Header',
array(
'title' => 'Lehrauftrag bestellen',
'jquery3' => true,
'jqueryui1' => true,
'jquerycheckboxes1' => true,
'bootstrap3' => true,
'fontawesome6' => true,
'sbadmintemplate3' => true,
'tabulator5' => true,
'tabulator5JQuery' => true,
'momentjs2' => true,
'ajaxlib' => true,
'dialoglib' => true,
'tablewidget' => true,
'navigationwidget' => true,
'phrases' => array(
'global' => array(
'lehrauftraegeBestellen',
'mehrHilfe',
'weitereInformationenUnter'
),
'ui' => array(
'anzeigen',
'alleAnzeigen',
'nurNeueAnzeigen',
'nurBestellteAnzeigen',
'nurErteilteAnzeigen',
'nurAngenommeneAnzeigen',
'nurGeaenderteAnzeigen',
'nurDummiesAnzeigen',
'hilfeZuDieserSeite',
'alleAuswaehlen',
'alleAbwaehlen',
'ausgewaehlteZeilen',
'hilfe',
'tabelleneinstellungen',
'keineDatenVorhanden',
'spaltenEinstellen',
'bestelltVon',
'erteiltVon',
'angenommenVon',
'neuerLehrauftragOhneLektorVerplant',
'neuerLehrauftragWartetAufBestellung',
'letzterStatusBestellt',
'letzterStatusErteilt',
'letzterStatusAngenommen',
'nachAenderungStundensatzStunden',
'vorAenderungStundensatzStunden'
),
'table' => array(
'spaltenEinAusblenden',
'spaltenEinAusblendenMitKlickOeffnen',
'spaltenEinAusblendenAufEinstellungenKlicken',
'spaltenEinAusblendenMitKlickAktivieren',
'spaltenEinAusblendenMitKlickSchliessen',
'spaltenbreiteVeraendern',
'spaltenbreiteVeraendernText',
'spaltenbreiteVeraendernInfotext',
'zeilenAuswaehlen',
'zeilenAuswaehlenEinzeln',
'zeilenAuswaehlenBereich',
'zeilenAuswaehlenAlle'
),
'lehre' => array(
'lehrauftragStandardBestellprozess',
'lehrauftragStandardBestellprozessBestellen',
'lehrauftragStandardBestellprozessErteilen',
'lehrauftragStandardBestellprozessAnnehmen',
'lehrauftraegeBestellen',
'lehrauftraegeBestellenText',
'lehrauftraegeBestellenKlickStatusicon',
'lehrauftraegeBestellenLehrauftraegeWaehlen',
'lehrauftraegeBestellenMitKlickBestellen',
'lehrauftraegeBestellenVertragWirdAngelegt',
'geaenderteLehrauftraege',
'geaenderteLehrauftraegeText',
'lehrauftraegeNichtAuswaehlbar',
'lehrauftraegeNichtAuswaehlbarText',
'filterAlle',
'filterNeu',
'filterBestellt',
'filterErteilt',
'filterAngenommen',
'filterGeaendert',
'filterDummies'
)
),
'customJSs' => array(
'public/js/bootstrapper.js',
'public/js/lehre/lehrauftrag/orderLehrauftrag.js'
)
)
'templates/FHC-Header',
$includesArray
);
?>
@@ -209,5 +212,11 @@ $this->load->view(
</div><!-- end page-wrapper -->
<br>
<?php $this->load->view('templates/FHC-Footer'); ?>
<?php
$this->load->view(
'templates/FHC-Footer',
$includesArray
);
?>
+36 -25
View File
@@ -9,22 +9,8 @@ $sitesettings = array(
'ajaxlib' => true,
'sbadmintemplate3' => true,
'phrases' => array(
'abschlusspruefung' => array(
'freigegebenAm',
'pruefungGespeichert',
'pruefungSpeichernFehler',
'abschlussbeurteilungLeer',
'beginnzeitLeer',
'beginnzeitFormatError',
'endezeitLeer',
'endezeitFormatError',
'endezeitBeforeError',
'verfNotice'
),
'ui' => array(
'stunde',
'minute'
)
'abschlusspruefung',
'ui'
),
'customCSSs' => array(
'public/css/sbadmin2/admintemplate_contentonly.css',
@@ -37,10 +23,18 @@ $sitesettings = array(
)
);
$this->load->view(
'templates/FHC-Header',
$sitesettings
);
if(defined('CIS4')){
$this->load->view(
'templates/CISVUE-Header',
$sitesettings
);
}else{
$this->load->view(
'templates/FHC-Header',
$sitesettings
);
}
?>
<div id="wrapper">
<div id="page-wrapper">
@@ -161,6 +155,14 @@ $this->load->view(
<?php echo ($abschlusspruefung->studiengangstyp == 'Bachelor' ? $this->p->t('abschlusspruefung', 'pruefungsgegenstandBachelor') : $this->p->t('abschlusspruefung', 'pruefungsgegenstandMaster')) ?>
</td>
</tr>
<tr>
<td>
<?php echo $this->p->t('abschlusspruefung', 'spracheDerArbeit') ?>&nbsp;<?php echo $arbeit_name ?>
</td>
<td colspan="5">
<?php echo $abschlusspruefung->abschlussarbeit_sprache ?? '' ?>
</td>
</tr>
<tr>
<td colspan="6">
<?php echo ucfirst($this->p->t('global', 'notizen')); ?>
@@ -201,7 +203,9 @@ $this->load->view(
<div class="col-lg-12 text-right">
<p>
<?php $freigegeben = isset($abschlusspruefung->freigabedatum); ?>
<button id="saveProtocolBtn" class="btn btn-default"<?php echo $freigegeben ? " disabled" : "" ?>><?php echo $this->p->t('ui', 'speichern') ?></button>
<button id="saveProtocolBtn" class="btn btn-default"<?php echo $freigegeben ? ' disabled title="'.$this->p->t('abschlusspruefung', 'bereitsFreigegeben').'"' : '' ?>>
<?php echo $this->p->t('ui', 'speichern') ?>
</button>
</p>
</div>
</div>
@@ -236,7 +240,14 @@ $this->load->view(
</div>
</div>
<?php
$this->load->view(
'templates/FHC-Footer',
$sitesettings
);
if (defined('CIS4')) {
$this->load->view(
'templates/CISVUE-Footer',
$sitesettings
);
} else {
$this->load->view(
'templates/FHC-Footer',
$sitesettings
);
}
+5 -1
View File
@@ -132,7 +132,11 @@
if ($cis === true) generateCSSsInclude(defined('CIS4') ? 'public/css/cis4.css' : 'public/css/cis_bs5.css');
//Tags
if ($tags === true) generateCSSsInclude('public/css/tags.css');
if ($tags === true)
{
generateCSSsInclude('public/css/tags.css');
generateCSSsIncludeIfExtensionCssExists('tags.css');
}
$extapphelper = ExtendableAppsHelper::getInstance();
$extapphelper->init($customCSSs, $customJSs, $customJSModules);
+1 -1
View File
@@ -1,4 +1,4 @@
<div class="navbar-default sidebar" role="navigation">
<div class="navbar-default sidebar top-0" role="navigation">
<div class="sidebar-nav navbar-collapse">
<ul class="nav" id="side-menu"></ul>
</div>
+47
View File
@@ -0,0 +1,47 @@
/* 1. Stick the Header */
#notentable .tabulator-header .tabulator-col.sticky-col {
position: sticky;
left: 0;
z-index: 10; /* Must be higher than other headers */
background-color: #fff; /* Opaque background is required */
border-right: 2px solid #ddd; /* Optional: Separator line */
}
/* 2. Stick the Data Cells */
#notentable .tabulator-tableholder .tabulator-row .tabulator-cell.sticky-col {
position: sticky;
left: 0;
z-index: 10; /* Ensure it floats above other cells */
background-color: #fff; /* Match your row background color */
border-right: 2px solid #ddd; /* Optional: Separator line */
}
/* 3. Fix for Hover Effects (Optional) */
/* If you use hover rows, you need to ensure the sticky cell matches the hover color */
#notentable .tabulator-row:hover .tabulator-cell.sticky-col {
background-color: #ccc; /* Match your existing hover color */
}
/* styling for points input column for notenvorschläge in benotungstool*/
#notentable .tabulator-tableholder .tabulator-editable[tabulator-field="punkte"] {
position: relative;
background-color: rgba(255, 255, 157, 0.73);
}
/* styling for editable dropdown column of notenvorschläge in benotungstool*/
#notentable .tabulator-tableholder .tabulator-editable[tabulator-field="note_vorschlag"] {
position: relative;
background-color: rgba(255, 255, 157, 0.73);
cursor: pointer;
}
#notentable .tabulator-tableholder .tabulator-editable[tabulator-field="note_vorschlag"]::after {
content: "▾";
position: absolute;
right: 6px;
color: rgba(176, 176, 106, 0.73);;
font-size: x-large;
bottom: 6px;
pointer-events: none;
}
+14
View File
@@ -97,3 +97,17 @@
display: none;
}
.fhc-calendar-empty-slot-plus {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
font-size: 18px;
opacity: 0;
pointer-events: none;
user-select: none;
}
.part-body:hover .fhc-calendar-empty-slot-plus {
opacity: 1;
}
+39
View File
@@ -0,0 +1,39 @@
/* Repositioning clear icon Datepicker for not being outside the textfield */
/* Wrapper */
.dp__input_wrap {
position: relative;
}
/* calender-Icon left */
.dp__input_icon {
position: absolute;
left: 10px;
right: auto;
top: 50%;
transform: translateY(-50%);
}
/* Clear-Icon right */
.dp__clear_icon {
position: absolute;
right: 10px;
left: auto;
top: 50%;
transform: translateY(-50%);
}
/* padding for Icons */
.dp__input {
padding-left: 36px !important;
padding-right: 36px !important;
}
.info-feedback {
display: block;
font-size: 0.875em;
color: #0d6efd; /* Bootstrap primary */
}
.is-info {
border-color: #0d6efd;
}
+5
View File
@@ -2,6 +2,7 @@
@import './SvgIcons.css';
@import './components/searchbar/searchbar.css';
@import './components/verticalsplit.css';
@import './components/horizontalsplit.css';
@import './components/FilterComponent.css';
@import './components/Tabs.css';
@import './components/Notiz.css';
@@ -277,3 +278,7 @@ html.fs_huge {
}
*/
/* slim ende */
.fhc-xxl-modal {
min-width: 80vw;
}
+10
View File
@@ -6,3 +6,13 @@
color: var(--fhc-myLv-disabled) !important;
cursor: default;
}
/* adjustment to have bs5 dropdownmenus rendered properly over a tabulator table */
.mylv-semester-table .tabulator-cell {
overflow: unset;
}
.mylv-semester-table .tabulator-cell .action-col {
/*min-height: 2.5rem;*/
align-items: flex-start; /* so wrapped rows don't stretch vertically */
}
+75
View File
@@ -0,0 +1,75 @@
:root {
--fhc-horizontalsplit-hsplitter-bg-color: var(--fhc-background, #eee);
--fhc-horizontalsplit-hsplitter-border-color: var(--fhc-border, #eee);
--fhc-horizontalsplit-hsplitter-splitactions-color: var(--fhc-dark, #000);
}
.horizontalsplit-container {
display: flex;
flex-direction: row;
overflow: hidden;
max-height: 100%;
padding: 0px;
}
.horizontalsplitted {
overflow: auto;
flex-shrink: 0;
}
.horizontalsplitter {
flex-shrink: 0;
width: 16px;
cursor: col-resize;
user-select: none;
display: flex;
align-items: center;
justify-content: center;
}
.horizontalsplitter.left {
border-left: solid 3px var(--fhc-horizontalsplit-hsplitter-border-color);
margin-right: 3px;
}
.horizontalsplitter.right {
border-right: solid 3px var(--fhc-horizontalsplit-hsplitter-border-color);
margin-left: 3px;
}
.splitactions.horizontal {
background-color: var(--fhc-horizontalsplit-hsplitter-bg-color);
color: var(--fhc-horizontalsplit-hsplitter-splitactions-color);
display: flex;
flex-direction: column;
padding: 5px 0 5px 0;
}
.splitactions.horizontal.left {
border-radius: 0 40% 40% 0;
}
.splitactions.horizontal.right {
border-radius: 40% 0 0 40%;
}
.splitactions.horizontal .splitaction {
width: 14px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
}
.splitactions.horizontal .splitaction.resize {
cursor: col-resize;
}
#content {
padding-top: 0 !important;
padding-bottom: 0 !important;
}
#content > div:first-child {
margin-top: 30px;
}
+6 -1
View File
@@ -69,12 +69,17 @@
.tag_limette {
background-color: #D3FFCE;
color: black;
}
.tag_done {
text-decoration: line-through;
}
.tag_auto {
border: 1px dashed white;
}
.display_all {
background-color: darkgrey !important;
border: none;
@@ -111,4 +116,4 @@
.copy-btn {
float: right;
margin-top: 3px;
}
}
+15
View File
@@ -21,5 +21,20 @@ export default {
method: 'get',
url: `/api/frontend/v1/LvMenu/getLvMenu/${lvid}/${studiensemester_kurzbz}`
};
},
getMultipleLvMenu(lvas, studiensemester_kurzbz) {
// format params for backend bulk function
const lvMenuOptionList = lvas.map(lva => {
return {
lvid: lva.lehrveranstaltung_id,
studiensemester_kurzbz
}
})
return {
method: 'post',
url: `/api/frontend/v1/LvMenu/getMultipleLvMenu`,
params: { lvMenuOptionList }
};
}
};
@@ -0,0 +1,44 @@
export default {
getReservableMap(ort_kurzbz, start_date, end_date) {
return {
method: 'post',
url: `/api/frontend/v1/calendar/RoomPlan/getReservableMap/${ort_kurzbz}`,
params: { start_date, end_date }
};
},
getRoomCreationInfo() {
return {
method: 'get',
url: '/api/frontend/v1/calendar/RoomPlan/getRoomCreationInfo'
};
},
getGruppen(query) {
return {
method: 'get',
url: `/api/frontend/v1/calendar/RoomPlan/getGruppen?query=${encodeURIComponent(query)}`
};
},
getLektor(query) {
return {
method: 'get',
url: `/api/frontend/v1/calendar/RoomPlan/getLektor?query=${encodeURIComponent(query)}`
};
},
addRoomReservation(formData) {
return {
method: 'post',
url: '/api/frontend/v1/calendar/RoomPlan/addRoomReservation',
params: formData
};
},
deleteRoomReservation(reservierung_id) {
return {
method: 'post',
url: '/api/frontend/v1/calendar/RoomPlan/deleteRoomReservation',
params: {
reservierung_id: reservierung_id
}
};
}
}
+70
View File
@@ -0,0 +1,70 @@
/**
* 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 {
getTimelocksUser(uid) {
return {
method: 'get',
url: '/api/frontend/v1/Zeitsperren/getZeitsperrenUser/' + uid
};
},
getTypenZeitsperren(){
return {
method: 'get',
url: '/api/frontend/v1/Zeitsperren/getTypenZeitsperren/'
};
},
getTypenErreichbarkeit(){
return {
method: 'get',
url: '/api/frontend/v1/Zeitsperren/getTypenErreichbarkeit/'
};
},
getStunden(){
return {
method: 'get',
url: '/api/frontend/v1/Zeitsperren/getStunden/'
};
},
addZeitsperre(uid, params) {
return {
method: 'post',
url: '/api/frontend/v1/Zeitsperren/add/' + uid,
params
};
},
editZeitsperre(zeitsperre_id, params) {
return {
method: 'post',
url: '/api/frontend/v1/Zeitsperren/update/' + zeitsperre_id,
params
};
},
loadZeitsperre(zeitsperre_id) {
return {
method: 'get',
url: '/api/frontend/v1/Zeitsperren/loadZeitsperre/' + zeitsperre_id
};
},
deleteZeitsperre(zeitsperre_id) {
return {
method: 'post',
url: '/api/frontend/v1/Zeitsperren/delete/' + zeitsperre_id
};
}
};
+6
View File
@@ -34,4 +34,10 @@ export default {
url: 'api/frontend/v1/detailheader/detailheader/getLeitungOrg/' + oekurzbz,
};
},
getSemesterStati(prestudent_id){
return {
method: 'get',
url: 'api/frontend/v1/detailheader/detailheader/getSemesterStati/' + prestudent_id,
};
},
}
+15 -1
View File
@@ -41,5 +41,19 @@ export default {
method: 'get',
url: `/api/frontend/v1/Lehre/semesterAverageGrade/${semester}`
}
},
getZugewieseneLv(uid, sem_kurzbz){
return {
method: 'get',
url: '/api/frontend/v1/Lehre/getZugewieseneLv',
params: { uid, sem_kurzbz}
};
},
getLeForLv(lv_id, sem_kurzbz) {
return {
method: 'get',
url: '/api/frontend/v1/Lehre/getLeForLv',
params: { lv_id, sem_kurzbz }
};
}
};
};
+87
View File
@@ -0,0 +1,87 @@
/**
* 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 {
getCisConfig(){
return {
method: 'get',
url: '/api/frontend/v1/Noten/getCisConfig'
};
},
getStudentenNoten(lv_id, sem_kurzbz) {
return {
method: 'get',
url: '/api/frontend/v1/Noten/getStudentenNoten',
params: { lv_id, sem_kurzbz }
};
},
getNoten(){
return {
method: 'get',
url: '/api/frontend/v1/Noten/getNoten'
};
},
saveStudentenNoten(password, noten, lv_id, sem_kurzbz) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveStudentenNoten',
params: { password, noten, lv_id, sem_kurzbz }
};
},
saveNotenvorschlag(lv_id, sem_kurzbz, student_uid, note, punkte = null) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveNotenvorschlag',
params: { lv_id, sem_kurzbz, student_uid, note, punkte }
};
},
saveStudentPruefung(student_uid, note, punkte, datum, lva_id, lehreinheit_id, sem_kurzbz, typ){
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveStudentPruefung',
params: { student_uid, note, punkte, datum, lva_id, lehreinheit_id, sem_kurzbz, typ }
};
},
createPruefungen(uids, datum, lva_id, sem_kurzbz){
return {
method: 'post',
url: '/api/frontend/v1/Noten/createPruefungen',
params: { uids, datum, lva_id, sem_kurzbz }
};
},
saveNotenvorschlagBulk(lv_id, sem_kurzbz, noten) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveNotenvorschlagBulk',
params: { lv_id, sem_kurzbz, noten }
};
},
saveStudentPruefungBulk(lv_id, sem_kurzbz, pruefungen) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/savePruefungenBulk',
params: { lv_id, sem_kurzbz, pruefungen }
};
},
getNoteByPunkte(punkte, lv_id, sem_kurzbz) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/getNoteByPunkte',
params: { punkte, lv_id, sem_kurzbz }
};
}
}
@@ -0,0 +1,36 @@
export default {
getPaAbgaben(studiengang_kz, abgabetyp_kurzbz, abgabedatum, personSearchString) {
return {
method: 'get',
url: '/api/frontend/v1/education/PaabgabeUebersicht/getPaAbgaben',
params: {
studiengang_kz: studiengang_kz, abgabetyp_kurzbz: abgabetyp_kurzbz, abgabedatum: abgabedatum, personSearchString: personSearchString
}
};
},
getStudiengaenge() {
return {
method: 'get',
url: '/api/frontend/v1/education/PaabgabeUebersicht/getStudiengaenge'
};
},
getTermine(studiengang_kz, abgabetyp_kurzbz) {
return {
method: 'get',
url: '/api/frontend/v1/education/PaabgabeUebersicht/getTermine',
params: { studiengang_kz: studiengang_kz, abgabetyp_kurzbz: abgabetyp_kurzbz }
};
},
getPaAbgabetypen() {
return {
method: 'get',
url: '/api/frontend/v1/education/PaabgabeUebersicht/getPaAbgabetypen'
};
},
getViewData() {
return {
method: 'get',
url: '/api/frontend/v1/education/PaabgabeUebersicht/viewData'
};
}
};
+7 -1
View File
@@ -29,5 +29,11 @@ export default {
url: 'api/frontend/v1/organisation/studiensemester/getAll',
params: { order, start }
};
}
},
getStudiensemester() {
return {
method: 'get',
url: '/api/frontend/v1/Studiensemester/getStudiensemester'
};
},
};
+16
View File
@@ -51,4 +51,20 @@ export default {
params: data
};
},
getAllTagsPrestudent(prestudent_id){
return {
method: 'get',
url: 'api/frontend/v1/stv/Tags/getAllTags',
params: prestudent_id
};
},
rebuildTagsforTypeId(data){
return {
method: 'post',
url: 'api/frontend/v1/stv/Tags/rebuildTagsForTypeId/',
params: data
};
}
};
-3
View File
@@ -35,9 +35,6 @@ export default {
getMitarbeiter(searchString){
return this.$fhcApi.get('api/frontend/v1/stv/abschlusspruefung/getMitarbeiter/' + searchString);
},
getPruefer(searchString){
return this.$fhcApi.get('api/frontend/v1/stv/abschlusspruefung/getPruefer/' + searchString);
},
getNoten(){
return this.$fhcApi.get('api/frontend/v1/stv/abschlusspruefung/getNoten/');
},
+40 -10
View File
@@ -5,7 +5,7 @@ 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 Mylv from "../../components/Cis/Mylv/MyLv.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";
@@ -19,9 +19,13 @@ import DeadlineOverview from "../../components/Cis/Abgabetool/DeadlineOverview.j
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 PaabgabeUebersicht from "../../components/Cis/ProjektabgabeUebersicht/ProjektabgabeUebersicht.js";
import Benotungstool from "../../components/Cis/Benotungstool/Benotungstool.js";
import Zeitsperren from "../../components/Cis/Zeitsperren/Zeitsperren.js";
import ApiRouteInfo from '../../api/factory/routeinfo.js';
import {capitalize} from "../../helpers/StringHelpers.js";
import ApiAuthinfo from "../../api/factory/authinfo.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;
@@ -77,6 +81,18 @@ const router = VueRouter.createRouter({
component: Raumsuche,
props: true
},
{
path: `/Cis/ProjektabgabeUebersicht`,
name: 'PaabgabeUebersicht',
component: PaabgabeUebersicht,
props: true
},
{
path: `/Cis/Benotungstool/:lv_id/:sem_kurzbz`,
name: 'Benotungstool',
component: Benotungstool,
props: true
},
// Redirect old links to new format
{
path: "/CisVue/Cms/getRoomInformation/:ort_kurzbz",
@@ -151,7 +167,7 @@ const router = VueRouter.createRouter({
{
path: `/Cis/MyLv/:studiensemester?`,
name: 'MyLv',
component: MylvStudent,
component: Mylv,
props: true,
},
{
@@ -163,7 +179,7 @@ const router = VueRouter.createRouter({
// Redirect old links to new format
{
// only trigger on first param being numeric to avoid paths like "LvPlan/Month" entering here
path: "/Cis/LvPlan/:lv_id(\\d+)",
path: "/Cis/LvPlan/:lv_id(\\d+)",
name: "LvPlanOld",
component: LvPlan,
redirect(to) {
@@ -245,6 +261,12 @@ const router = VueRouter.createRouter({
};
},
},
{
path: `/Cis/Zeitsperren`,
name: 'Zeitsperren',
component: Zeitsperren,
props: true
},
]
})
@@ -253,11 +275,15 @@ const app = Vue.createApp({
data: () => ({
appSideMenuEntries: {},
windowWidth: 0,
isStudent: null,
isMitarbeiter: null,
}),
provide() {
return { // provide injectable & watchable language property
language: Vue.computed(() => this.$p.user_language),
isMobile: Vue.computed(() => this.windowWidth < 767),
isStudent: Vue.computed(() => this.isStudent),
isMitarbeiter: Vue.computed(() => this.isMitarbeiter)
}
},
methods: {
@@ -271,7 +297,7 @@ const app = Vue.createApp({
if(target?.id == 'skiplink') return
if (target && this.isInternalRoute(target.href)) {
const url = new URL(target.href)
const path = url.pathname
const base = this.$router.options.history.base
const route = path.replace(base, '') || '/'
@@ -279,27 +305,31 @@ const app = Vue.createApp({
// let click event propagate normally if we dont route internally
const res = this.$router.resolve(route)
if(!res?.matched?.length || res.name === 'Fallback') return
event.preventDefault(); // Prevent browser navigation
if(this.isMobile) { // toggle the menu
const navMain = document.getElementById('nav-main');
// fix unwanted toggle from off to on for some links on mobile
if(navMain.classList.contains('show')){
document.getElementById('nav-main-btn').click();
}
}
}
this.$router.push(route);
}
},
handleWindowResize() {
this.windowWidth = window.innerWidth;
},
},
created() {
async created() {
this.windowWidth = window.innerWidth;
await this.$api.call(ApiAuthinfo.getAuthInfo()).then((res) => {
this.isMitarbeiter = res.data.isMitarbeiter;
this.isStudent = res.data.isStudent;
});
},
async mounted() {
document.addEventListener('click', this.handleClick);
+1 -1
View File
@@ -148,4 +148,4 @@ export default {
</div>
</div>
`
}
}
+14 -3
View File
@@ -44,7 +44,10 @@ export default {
return () => true;
}),
hasDragoverFunc: Vue.computed(() => this.onDragover),
mode: Vue.computed(() => this.mode)
mode: Vue.computed(() => this.mode),
reservierbarMap: Vue.computed(() => this.reservierbarMap),
isReservierbar: Vue.computed(() => this.isReservierbar),
createContext: Vue.computed(() => this.createContext)
};
},
props: {
@@ -97,7 +100,13 @@ export default {
draggableEvents: [Boolean, Array, Function],
dropableEvents: [Boolean, Array, Function],
onDragover: Function,
onDrop: Function
onDrop: Function,
isReservierbar: Boolean,
createContext: Object,
reservierbarMap: {
type: Object,
default: () => ({})
},
},
emits: [
"click:next",
@@ -105,11 +114,13 @@ export default {
"click:mode",
"click:event",
"click:day",
"click:slot",
"click:week",
"update:date",
"update:mode",
"update:range",
"drop"
"drop",
"create-event"
],
data() {
return {
+38 -2
View File
@@ -2,6 +2,7 @@ import GridLine from './Grid/Line.js';
import GridLineEvent from './Grid/Line/Event.js';
import CalDnd from '../../../directives/Calendar/DragAndDrop.js';
import CalClick from '../../../directives/Calendar/Click.js';
export default {
name: "CalendarGrid",
@@ -10,12 +11,16 @@ export default {
GridLineEvent
},
directives: {
CalDnd
CalDnd,
CalClick
},
inject: {
originalEvents: "events",
originalBackgrounds: "backgrounds",
dropAllowed: "dropAllowed"
dropAllowed: "dropAllowed",
timezone: "timezone",
reservierbar: "isReservierbar",
reservierbarMap: "reservierbarMap",
},
provide() {
return {
@@ -308,8 +313,25 @@ export default {
} else {
this.$refs.scroller.scrollTo(0, 0);
}
},
isFreeSlot(date, part, dayEvents) {
const pastEnd = luxon.DateTime.now().setZone(this.timezone);
const start = date.plus(part.start || part);
const end = date.plus(part.end || part.plus({ hours: 1 }));
if (start < pastEnd)
return false;
if (!dayEvents || !dayEvents.length)
return true;
return !dayEvents.some(ev => ev.start < end && ev.end > start);
}
},
created() {
this.$p.loadCategory(["LvPlan"]);
},
beforeUnmount() {
this.disableAutoScroll();
},
@@ -400,6 +422,20 @@ export default {
:style="'grid-' + axisCol + ':' + (1+index) + ';grid-' + axisRow + ':ps_' + i + '/pe_' + i"
>
<slot name="part-body" v-bind="{ index, part }" />
<div
v-if="isFreeSlot(date, part, eventsNormal[index]) && reservierbar"
class="fhc-calendar-empty-slot"
style="position:absolute; inset:0; z-index:1"
v-cal-click:slot="{ date, part }"
:title="this.reservierbarMap?.[date.toISODate()] ? $p.t('LvPlan/add_reservation') : $p.t('LvPlan/reservation_not_allowed')"
>
<div class="fhc-calendar-empty-slot-plus">
<i v-if="this.reservierbarMap?.[date.toISODate()]" class="fa-solid fa-plus"></i>
<i v-else class="fa-solid fa-ban" style="color: red;"></i>
</div>
</div>
<div
v-if="snapToGrid && dragging"
style="position:absolute;inset:0;z-index:1"
+39 -3
View File
@@ -28,7 +28,15 @@ export default {
getPromiseFunc: {
type: Function,
required: true
}
},
reservierbar: {
type: Boolean,
default: false
},
createContext: {
type: Object,
default: () => ({})
},
},
provide() {
return {
@@ -43,11 +51,26 @@ export default {
emits: [
"update:date",
"update:mode",
"update:range"
"update:range",
"create-event",
"delete-event",
'update:reservierbarMap'
],
data() {
return {
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
isReservierbar: Vue.computed(() => {
if (!this.reservierbar)
return false;
if (!this.reservierbarMap)
return false;
if (typeof this.reservierbarMap === 'object')
return Object.keys(this.reservierbarMap).length > 0;
return false;
}),
modeOptions: {
day: {
emptyMessage: Vue.computed(() => this.$p.t('lehre/noLvFound')),
@@ -125,11 +148,14 @@ export default {
);
this.compactibleEventTypes = compactibleEventTypesResponse.data;
},
closeModal() {
this.$refs.calendar.hideEventModal();
},
},
setup(props, context) {
const rangeInterval = Vue.ref(null);
const { events, lv, reset } = useEventLoader(rangeInterval, props.getPromiseFunc);
const { events, lv, reservierbarMap, reset } = useEventLoader(rangeInterval, props.getPromiseFunc);
Vue.watch(lv, newValue => {
context.emit('update:lv', newValue);
@@ -137,10 +163,15 @@ export default {
const { renderers } = useRenderers();
Vue.watch(reservierbarMap, newVal => {
context.emit('update:reservierbarMap', newVal);
});
return {
rangeInterval,
events,
lv,
reservierbarMap,
reset,
renderers
};
@@ -162,6 +193,9 @@ export default {
:events="events || []"
:backgrounds="backgrounds"
:time-grid="teachingunits"
:reservierbar-map="reservierbarMap"
:isReservierbar="isReservierbar"
:create-context="createContext"
show-btns
@update:date="(newDate, newMode) => $emit('update:date', newDate, newMode)"
@update:mode="(newMode, newDate) => $emit('update:mode', newMode, newDate)"
@@ -184,6 +218,7 @@ export default {
v-if="mode == 'event'"
:is="renderers[event.type]?.modalContent"
:event="event"
@create-event="(event) => $emit('create-event', event)"
></component>
<component
v-else-if="mode == 'eventheader'"
@@ -194,6 +229,7 @@ export default {
v-else
:is="renderers[event.type]?.calendarEvent"
:event="event"
@delete-event="(event) => $emit('delete-event', event)"
:timeSlotDisplayBehavior="
$props.mode.toLowerCase() === 'list' ? 'always' : 'default'
"
+34 -1
View File
@@ -16,7 +16,19 @@ export default {
inject: {
timeGrid: "timeGrid",
originalEvents: "events",
timezone: "timezone"
timezone: "timezone",
reservierbar: {
from: "isReservierbar",
default: false
},
reservierbarMap: {
type: Object,
default: () => ({})
},
createContext: {
from: 'createContext',
default: () => {}
},
},
props: {
day: {
@@ -103,6 +115,27 @@ export default {
});
}
}
else if (evt.detail.source == 'slot')
{
if (!this.reservierbar)
return;
const { date, part } = evt.detail.value || {};
if (!date)
return;
let reservierbar = this.reservierbarMap?.[date.toISODate()] === true;
if (!reservierbar)
return;
this.$emit('requestModalOpen', {
event: {
type: this.createContext?.scope ?? 'slot',
start: date.plus(part.start || part),
end: date.plus(part.end || part.plus({ hours: 1 })),
createContext: this.createContext
}
});
}
}
},
setup() {
+35 -8
View File
@@ -7,6 +7,14 @@ export default {
BaseSlider,
WeekView
},
inject: {
reservierbar: "isReservierbar",
reservierbarMap: "reservierbarMap",
createContext: {
from: 'createContext',
default: () => {}
},
},
props: {
currentDate: {
type: luxon.DateTime,
@@ -83,14 +91,33 @@ export default {
},
handleClickDefaults(evt) {
switch (evt.detail.source) {
case 'day':
// default: Set current-date
this.$emit('update:currentDate', evt.detail.value);
break;
case 'event':
// default: Request Modal
this.$emit('requestModalOpen', { event: evt.detail.value });
break;
case 'day':
// default: Set current-date
this.$emit('update:currentDate', evt.detail.value);
break;
case 'event':
// default: Request Modal
this.$emit('requestModalOpen', { event: evt.detail.value });
break;
case 'slot':
{
const { date, part } = evt.detail.value || {};
if (!date)
return;
let reservierbar = this.reservierbarMap?.[date.toISODate()] === true;
if (!reservierbar)
return;
this.$emit('requestModalOpen', {
event: {
type: this.createContext?.scope ?? 'slot',
start: date.plus(part.start || part),
end: date.plus(part.end || part.plus({ hours: 1 })),
createContext: this.createContext
}
});
break;
}
}
}
},
@@ -522,41 +522,20 @@ export const AbgabetoolAssistenz = {
const table = this.$refs.abgabeTable.tabulator
this.tableBuiltResolve()
table.on("columnMoved", () => {
this.saveState(table);
});
table.on("columnResized", () => {
this.saveState(table);
});
table.on("columnVisibilityChanged", () => {
this.saveState(table);
});
table.on("filterChanged", () => {
this.saveState(table);
});
table.on("headerFilterChanged", () => {
this.saveState(table);
});
table.on("dataSorted", () => {
this.saveState(table);
});
table.on("columnSorted", () => {
this.saveState(table);
});
table.on("sortersChanged", () => {
this.saveState(table);
});
const saved = this.loadState();
// setup change eventlisteners
const events = [
"columnMoved", "columnResized", "columnVisibilityChanged",
"filterChanged", "headerFilterChanged", "dataSorted",
"columnSorted", "sortersChanged"
];
events.forEach(eventName => {
table.on(eventName, () => this.saveState(table));
});
table.on("renderComplete", () => {
if(!this.stateRestored) {
@@ -999,7 +978,6 @@ export const AbgabetoolAssistenz = {
this.tableData = this.mapProjekteToTableData(this.projektarbeiten)
await this.tableBuiltPromise
this.$refs.abgabeTable.tabulator.setData(this.tableData);
},
loadProjektarbeiten(all = false, callback) {
File diff suppressed because it is too large Load Diff
+1
View File
@@ -1,5 +1,6 @@
export default {
name: 'LvMenu',
props:{
menu:{
type:Array,
@@ -4,7 +4,7 @@ import LvMenu from "./LvMenu.js";
import ApiAddons from '../../../api/factory/addons.js';
export default {
name: 'LvUebersicht',
props:{
event:{
type:Object,
@@ -1,25 +1,37 @@
import MylvSemester from "./Semester.js";
import Phrasen from "../../../mixins/Phrasen.js";
import MylvSemesterCards from "./Semester.js";
import MylvTable from "./Table.js";
import ApiAddons from "../../../api/factory/addons.js"
// TODO(chris): phrase: global/studiensemester_auswaehlen
// TODO(chris): phrase: next & prev +aria-label
export default {
name: 'MyLv',
components: {
MylvSemester
MylvSemesterCards,
MylvTable
},
mixins: [
Phrasen
],
data: () => {
return {
firstLoad: true,
studiensemester: null,
lvs: {},
currentSemester: null
currentSemester: null,
mode: localStorage.getItem('myLvaDefaultMode') ?? 'cards'
};
},
provide() {
return {
type: Vue.computed(() => this.type),
}
},
inject: ['isStudent', 'isMitarbeiter'],
computed: {
type() {
if(this.isStudent) return 'student'
if(this.isMitarbeiter) return 'employee'
return null
},
ready() {
return this.studiensemester !== null && (!this.firstLoad || this.current.lvs !== null);
},
@@ -34,7 +46,22 @@ export default {
axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Cis/Mylv/Lvs/' + this.currentSemester).then(res => {
this.lvs[this.currentSemester].lvs = res.data.retval || [];
this.firstLoad = false;
});
this.lvs[this.currentSemester].lvs.forEach(lv=>{
this.$api.call(ApiAddons.getLvMenu(lv.lehrveranstaltung_id, this.currentSemester)).then(res => {
if(res.data) {
const lvProp = this.lvs[this.currentSemester].lvs.find(lv2 => lv2.lehrveranstaltung_id == lv.lehrveranstaltung_id)
lvProp.menu = res.data
}
})
})
})
}
return this.lvs[this.currentSemester];
},
@@ -67,6 +94,10 @@ export default {
}
},
methods: {
clickMode(evt, mode) {
localStorage.setItem('myLvaDefaultMode', mode)
this.mode = mode
},
prevSem() {
this.$refs.studiensemester.selectedIndex--;
this.$refs.studiensemester.dispatchEvent(new Event('change', { bubbles: true }));
@@ -99,7 +130,7 @@ export default {
<div>
<h2>{{$p.t('lehre/myLV')}}</h2>
<hr>
<div class="mylv-student" v-if="ready">
<div class="mylv" v-if="ready">
<div v-if="currentSemester" class="row justify-content-center mb-3">
<div class="col-auto d-none">
<label class="col-form-label">{{$p.t('lehre/studiensemester')}}</label>
@@ -117,14 +148,35 @@ export default {
</button>
</div>
</div>
<div class=" col-auto my-lva-modes">
<div class="d-flex gap-1 justify-content-end" role="group">
<button
type="button"
class="btn btn-outline-secondary"
:class="{active: mode === 'cards'}"
@click="clickMode($event, 'cards')"
>
<i class="fa fa-grip"></i>
</button>
<button
type="button"
class="btn btn-outline-secondary"
:class="{active: mode === 'table'}"
@click="clickMode($event, 'table')"
>
<i class="fa fa-table"></i>
</button>
</div>
</div>
</div>
<div class="alert alert-danger" role="alert" v-else>
{{$p.t('lehre/noLvFound')}}
</div>
<mylv-semester v-bind="current"/>
<mylv-semester-cards v-if="mode == 'cards'" v-bind="current"/>
<mylv-table v-else-if="mode == 'table'" v-bind="current"/>
</div>
<div class="mylv-student text-center" v-else>
<div class="mylv text-center" v-else>
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>
</div>`
};
};
@@ -1,6 +1,7 @@
import FhcCalendar from "../../Calendar/LvPlan.js";
import ApiLvPlan from '../../../api/factory/lvPlan.js';
import ApiRoomPlan from '../../../api/factory/calendar/roomPlan.js';
export const DEFAULT_MODE_RAUMINFO_MOBILE = 'List';
export const DEFAULT_MODE_RAUMINFO_DESKTOP = 'Week';
@@ -24,6 +25,36 @@ export default {
return this.propsViewData?.mode || defaultMode;
}
},
data() {
return {
filteredGroups: [],
abortController: null,
createContext: {
scope: 'slot_room',
show_all_fields: false,
room_create_information: {
semester: [1, 2, 3, 4, 5, 6, 7, 8],
verband: ['A', 'B', 'C', 'D', 'E', 'F', 'V'],
gruppe: [1, 2, 3, 4],
studiengaenge: [],
searchGroup: this.searchGroup,
searchLektor: this.searchLektor,
},
}
}
},
created() {
this.$api.call(ApiRoomPlan.getRoomCreationInfo())
.then(result => result.data)
.then(result => {
if (result.berechtigt)
{
this.createContext.room_create_information.studiengaenge = result.studiengaenge
}
this.createContext.show_all_fields = result.berechtigt;
});
},
methods:{
handleChangeDate(day, newMode) {
return this.handleChangeMode(newMode, day);
@@ -41,8 +72,81 @@ export default {
}
});
},
async handleCreateEvent(event)
{
event.ort_kurzbz = this.propsViewData.ort_kurzbz;
this.$api.call(ApiRoomPlan.addRoomReservation(event));
this.$refs.calendar.resetEventLoader();
this.$refs.calendar.closeModal();
},
async handleDeleteEvent(event)
{
if (event.type !== 'reservierung')
return;
if (luxon.DateTime.fromISO(`${event.datum}T${event.beginn}`) < luxon.DateTime.now())
return;
this.$api.call(ApiRoomPlan.deleteRoomReservation(event.reservierung_id));
this.$refs.calendar.reset();
},
async searchGroup(event)
{
const query = event.query.trim();
if (query.length < 2)
return [];
if (this.abortController)
this.abortController.abort();
this.abortController = new AbortController();
const signal = this.abortController.signal;
return this.$api.call(ApiRoomPlan.getGruppen(query), { signal })
.then(result => {
return result.data.map(gruppe => ({
label: gruppe.bezeichnung
? `${gruppe.gruppe_kurzbz.trim()} (${gruppe.bezeichnung})`
: gruppe.gruppe_kurzbz.trim(),
gid: gruppe.gid,
gruppe_kurzbz: gruppe.gruppe_kurzbz.trim(),
lehrverband: gruppe.lehrverband,
})
);
})
.catch((e)=> {
this.$fhcAlert.handleSystemError(e)
return []
})
},
async searchLektor(event)
{
const query = event.query.trim();
if (query.length < 2)
return [];
if (this.abortController)
this.abortController.abort();
this.abortController = new AbortController();
const signal = this.abortController.signal;
return this.$api.call(ApiRoomPlan.getLektor(query), { signal })
.then(result => {
return result.data.map(lektor => ({
label: `${lektor.nachname} ${lektor.vorname} (${lektor.uid})`,
uid: lektor.uid
})
)})
.catch(this.$fhcAlert.handleSystemError)
},
getPromiseFunc(start, end) {
return [
this.$api.call(ApiRoomPlan.getReservableMap(this.propsViewData.ort_kurzbz, start.toISODate(), end.toISODate())),
this.$api.call(ApiLvPlan.getRoomInfo(this.propsViewData.ort_kurzbz, start.toISODate(), end.toISODate())),
this.$api.call(ApiLvPlan.getOrtReservierungen(this.propsViewData.ort_kurzbz, start.toISODate(), end.toISODate()))
];
@@ -57,8 +161,12 @@ export default {
:get-promise-func="getPromiseFunc"
:date="currentDay"
:mode="currentMode"
:reservierbar="true"
:create-context="createContext"
@update:date="handleChangeDate"
@update:mode="handleChangeMode"
@create-event="handleCreateEvent"
@delete-event="handleDeleteEvent"
class="responsive-calendar"
></fhc-calendar>
</div>`
+2 -1
View File
@@ -1,6 +1,7 @@
import MylvSemesterStudiengang from "./Semester/Studiengang.js";
export default {
name: 'Semester',
components: {
MylvSemesterStudiengang
},
@@ -43,4 +44,4 @@ export default {
<div class="mylv-semester text-center" v-else>
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>`
};
};
@@ -2,7 +2,10 @@ import MylvSemesterStudiengangLv from "./Studiengang/Lv.js";
import MylvSemesterStudiengangAverageGrade from "./Studiengang/AverageGrade.js";
import Phrasen from "../../../../mixins/Phrasen.js";
import ApiAuthinfo from '../../../../api/factory/authinfo.js';
export default {
name: 'Studiengang',
components: {
MylvSemesterStudiengangLv,
MylvSemesterStudiengangAverageGrade
@@ -18,6 +21,11 @@ export default {
lvs: Array,
sg_bezeichnung_eng: String
},
data() {
return {
isAverageGradeDisplayed: false,
}
},
computed: {
lehrveranstaltungen() {
return [... new Map(
@@ -33,9 +41,17 @@ export default {
note(lv) {
return lv.benotung ? lv.znote || lv.lvnote || null : null;
},
async checkIfAverageGradeIsDisplayed() {
const authInfoResponse = await this.$api.call(ApiAuthinfo.getAuthInfo());
const authInfo = authInfoResponse.data;
this.isAverageGradeDisplayed = !!authInfo.isStudent;
},
},
template: `<div class="card mb-3">
created() {
this.checkIfAverageGradeIsDisplayed();
},
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>
@@ -45,7 +61,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 :semesterInfo="$props.semesterInfo" />
<mylv-semester-studiengang-average-grade v-if="isAverageGradeDisplayed" :semesterInfo="$props.semesterInfo" />
</div>
</div>`
};
};
@@ -1,7 +1,4 @@
import LvPruefungen from "./Lv/Pruefungen.js";
import LvInfo from "./Lv/Info.js";
import Phrasen from "../../../../../mixins/Phrasen.js";
import LvUebersicht from "../../LvUebersicht.js";
import ApiLehre from '../../../../../api/factory/lehre.js';
import ApiAddons from '../../../../../api/factory/addons.js';
@@ -9,15 +6,11 @@ import ApiAddons from '../../../../../api/factory/addons.js';
// TODO(chris): L10n
export default {
components:{
LvUebersicht,
},
mixins: [
Phrasen
],
inject: ['studien_semester'],
name: 'Lv',
inject: ['studien_semester', 'type'],
props: {
lehrveranstaltung_id: Number,
lehrveranstaltung_id: [Number, String],
semesterstunden: [Number, String],
bezeichnung: String,
bezeichnung_eng: String,
module: String,
@@ -35,7 +28,8 @@ export default {
ects: String,
incoming: Number,
positiv: Boolean,
note_index: String
note_index: String,
menu: [Array, String]
},
provide() {
return {
@@ -46,7 +40,6 @@ export default {
return {
pruefungenData: null,
info: null,
menu: null,
preselectedMenuItem: null,
}
},
@@ -70,12 +63,6 @@ export default {
emptyMenu(){
return !this.menu || !Array.isArray(this.menu) || Array.isArray(this.menu) && this.menu.length == 0;
},
bodyStyle() {return {};
/*const bodyStyle = {};
if (this.farbe)
bodyStyle['background-color'] = '#' + this.farbe;
return bodyStyle;*/
},
grade() {
const languageIndex = this.$p.user_language.value === 'English' ? 1 : 0
// no more showing of grade LV, if grade Zeugnis is not existing yet
@@ -89,7 +76,6 @@ export default {
},
},
methods: {
fetchMenu(lehrveranstaltung_id = this.lehrveranstaltung_id, studien_semester = this.studien_semester) {
return this.$api
.call(ApiAddons.getLvMenu(lehrveranstaltung_id, studien_semester))
@@ -101,28 +87,18 @@ export default {
this.menu = [];
});
},
c4_target(menuItem) {
if (menuItem.c4_moodle_links?.length > 0) return null;
return menuItem.c4_target ?? null;
},
c4_link(menuItem) {
if (!menuItem) return null;
if (Array.isArray(menuItem.c4_moodle_links) && menuItem.c4_moodle_links.length) {
return '#';
}
else {
} else {
return menuItem.c4_link ?? null;
}
},
openLvOption(menuItem){
if (menuItem.id == "core_menu_mailanstudierende"){
window.location.href = menuItem.c4_link;
} else if (menuItem.id == "core_menu_digitale_anwesenheitslisten") {
window.location.href = menuItem.c4_link;
} else{
this.preselectedMenuItem = menuItem;
Vue.nextTick(() => {
this.$refs.lvUebersicht.show();
});
}
},
openPruefungen() {
if (!this.LvHasPruefungenInformation) return;
@@ -130,75 +106,65 @@ export default {
pruefungenData: this.pruefungenData,
bezeichnung: this.bezeichnung
});
},
openInfos() {
if (!this.info) {
this.info = true;
// TODO(chris): load all this params on ajax?
LvInfo.popup({
lehrveranstaltung_id: this.lehrveranstaltung_id,
bezeichnung: this.bezeichnung,
bezeichnung_eng: this.bezeichnung_eng,
studiengang_kuerzel: this.studiengang_kuerzel,
semester: this.semester,
studien_semester: this.studien_semester,
orgform_kurzbz: this.orgform_kurzbz,
sprache: this.sprache,
ects: this.ects,
incoming: this.incoming
}).then(() => this.info = false).catch(() => this.info = false);
}
}
},
watch:{
studien_semester(newValue){
this.fetchMenu(this.lehrveranstaltung_id, newValue);
}
},
created() {
this.$api
.call(ApiLehre.getStudentPruefungen(this.lehrveranstaltung_id))
.then(res => res.data)
.then(pruefungen => {
this.pruefungenData = pruefungen;
});
if(this.type == 'student') {
this.$api
.call(ApiLehre.getStudentPruefungen(this.lehrveranstaltung_id))
.then(res => res.data)
.then(pruefungen => {
this.pruefungenData = pruefungen;
});
}
},
mounted() {
this.fetchMenu(this.lehrveranstaltung_id, this.studien_semester);
},
template: /*html*/`<div class="mylv-semester-studiengang-lv card">
<lv-uebersicht ref="lvUebersicht" :preselectedMenu="preselectedMenuItem" :event="{
lehrveranstaltung_id: lehrveranstaltung_id,
studiensemester_kurzbz:studien_semester,
lehrfach_bez:studien_semester,
stg_kurzbzlang:studien_semester,
}"/>
template: /*html*/`
<div class="mylv-semester-studiengang-lv card">
<div class="p-2" :class="is_organisatorische_einheit?'':'card-header'">
<!-- {{module}} if the module of the lv is important then query the module from the api endpoint for LV-->
<h6 class="fw-bold" v-if="is_organisatorische_einheit" >{{ $p.t('lehre/organisationseinheit') }}:</h6>
<h6 class="mb-0">{{$p.user_language.value === 'English' ? bezeichnung_eng : bezeichnung}}</h6>
</div>
<div v-if="!emptyMenu" class="card-body " :style="bodyStyle">
<div v-if="!emptyMenu" class="card-body ">
<template v-if="menu">
<ul class="list-group border-top-0 border-bottom-0 rounded-0">
<li :type="menuItem.c4_link ? 'button' : null" v-for="menuItem in menu" class="list-group-item border-0 " >
<div class="d-flex flex-row" :data-bs-toggle="menuItem.c4_moodle_links?.length ? 'dropdown' : null">
<li :type="menuItem.c4_link ? 'button' : null"
v-for="(menuItem, index) in menu" :key="index" class="list-group-item border-0 " >
<div class="d-flex flex-row">
<div class="mx-4">
<i :class="[menuItem.c4_icon2 ? menuItem.c4_icon2 : 'fa-solid fa-pen-to-square', !menuItem.c4_link ? 'unavailable' : null ]"></i>
</div>
<a
<a :id="menuItem.name"
class="fhc-body text-decoration-none text-truncate"
:id="'moodle_links_'+lehrveranstaltung_id"
:class="{ 'unavailable':!menuItem.c4_link, 'dropdown-toggle':menuItem.c4_moodle_links?.length }"
:class="{ 'unavailable':!menuItem.c4_link }"
:target="menuItem.c4_target"
:href="c4_link(menuItem) ? c4_link(menuItem) : null">
{{ menuItem.phrase ? $p.t(menuItem.phrase) : menuItem.name}}
</a>
<div v-if="(menuItem.c4_moodle_links?.length || menuItem.c4_linkList?.length) && menuItem.c4_link" class="dropdown">
<button
class="btn btn-sm dropdown-toggle dropdown-toggle-split border-0"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul v-if="menuItem.c4_moodle_links?.length" class="dropdown-menu dropdown-menu p-0">
<li v-for="item in menuItem.c4_moodle_links" :key="item.url">
<a class="dropdown-item border-bottom" :href="item.url" target="#">{{ item.lehrform }}</a>
</li>
</ul>
<ul v-else class="dropdown-menu dropdown-menu p-0">
<li v-for="([text, link], i) in menuItem.c4_linkList" :key="i">
<a class="dropdown-item border-bottom" :href="link" target="#">{{ text }}</a>
</li>
</ul>
</div>
<ul v-if="menuItem.c4_moodle_links?.length" class="dropdown-menu p-0" :aria-labelledby="'moodle_links_'+lehrveranstaltung_id">
<li v-for="item in menuItem.c4_moodle_links"><a class="dropdown-item border-bottom" :href="item.url">{{item.lehrform}}</a></li>
</ul>
</div>
</li>
</ul>
</template>
@@ -208,16 +174,31 @@ export default {
</div>
</template>
</div>
<div v-if="!emptyMenu" class="card-footer">
<div
@click.prevent="openPruefungen()"
:type="LvHasPruefungenInformation ? 'button' : ''"
class="d-flex flex-row align-items-center gap-1"
>
<i class="fa fa-check text-success" v-if="positiv"></i>
<span :style="'color:'+gradeColor">{{ grade || $p.t('lehre/noGrades') }}</span>
<i v-if="LvHasPruefungenInformation" class="fa fa-circle-info ms-1"></i>
<div v-if="!emptyMenu && type == 'student'" class="card-footer">
<div class="row">
<!-- template for the LV if there are multiple pruefungen -->
<template v-if="LvHasPruefungenInformation">
<a href="#" class="col-auto text-start text-decoration-none" @click.prevent="openPruefungen">
<i class="fa fa-check text-success" v-if="positiv"></i>
<span class="ps-1" :style="'color:'+gradeColor">{{ grade || $p.t('lehre/noGrades') }}</span>
<i class="fa fa-circle-info ms-1"></i>
</a>
</template>
<!-- template for the LV with no pruefungen -->
<template v-else>
<span class="col-auto text-start text-decoration-none" >
<i class="fa fa-check text-success" v-if="positiv"></i>
<span class="ps-1" :style="'color:'+gradeColor">{{ grade || $p.t('lehre/noGrades') }}</span>
</span>
</template>
</div>
</div>
<div v-else-if="!emptyMenu && type == 'employee'" class="card-footer">
<div class="row">
<div class="col-auto">
<span class="ps-1">{{ $p.t('lehre/semesterstunden') }}: {{ semesterstunden }}</span>
</div>
</div>
</div>
</div>`
};
};
+345
View File
@@ -0,0 +1,345 @@
import {CoreFilterCmpt} from "../../../components/filter/Filter.js";
export default {
name: 'MylvTable',
components: {
CoreFilterCmpt
},
props: {
semester: [String],
lvs: Array,
},
data() {
return {
phrasenPromise: null,
phrasenResolved: false,
tabulatorUuid: null,
tableBuiltResolve: null,
tableBuiltPromise: null,
mylvTableOptions: {
height: Vue.ref(400),
index: 'lehrveranstaltung_id',
layout: 'fitDataStretch',
placeholder: this.$p.t('global/noDataAvailable'),
columns: [
{title: Vue.computed(() => this.$capitalize(this.$p.t('lehre/studiengang'))), field: 'sg_bezeichnung', widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('global/bezeichnung'))), field: 'bezeichnung', widthGrow: 2},
{title: Vue.computed(() => this.$capitalize(this.$p.t('lehre/orgform'))), field: 'orgform_kurzbz', widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('lehre/kurzbz'))), field: 'studiengang_kuerzel', widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('lehre/semesterstunden'))), field: 'semesterstunden',
bottomCalc: this.semesterstundenCalc, widthGrow: 1, visible: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('global/actions'))), headerSort: false,
field: 'menu', formatter: this.actionFormatter, widthGrow: 1, tooltip: this.spoofingFunc}
],
persistence: false,
persistenceID: "mylv_2026_04_17"
},
mylvTableEventHandlers: [
]
}
},
computed: {
ready() { return this.lvs !== null; },
},
methods: {
semesterstundenCalc(values, data) {
let sum = 0
values.forEach(val => {
sum += Number(val)
})
return sum
},
spoofingFunc() {
// intentionally send empty tooltip info so tabulator tooltip doesnt get rendered but hover event propagates
// to individual button tooltips
return ''
},
c4_link(menuItem) {
if (!menuItem) return null;
if (Array.isArray(menuItem.c4_moodle_links) && menuItem.c4_moodle_links.length) {
return '#';
} else {
return menuItem.c4_link ?? null;
}
},
handleUuidDefined(uuid) {
this.tabulatorUuid = uuid
},
tableResolve(resolve) {
this.tableBuiltResolve = resolve
},
actionFormatter(cell) {
let container = document.createElement('div');
container.className = "d-flex gap-2";
const data = cell.getData()
if(data.menu && data.menu.length) {
container.className = "d-flex flex-wrap gap-2"
data.menu.forEach((lvLink) => {
// render dropdown if we have a link and some some linklist
const hasDropdown = (lvLink.c4_moodle_links?.length || lvLink.c4_linkList?.length) && lvLink.c4_link;
if (hasDropdown) {
// button group
const group = document.createElement('div');
group.className = 'btn-group';
// main action button
const button= this.createActionButton(lvLink)
// toggle button
const toggle = document.createElement('button');
toggle.className = 'btn btn-sm dropdown-toggle dropdown-toggle-split border-0';
toggle.type = 'button';
toggle.dataset.bsToggle = 'dropdown'; // uses absolute position which gets clipped by tabulator
toggle.ariaExpanded = 'false';
toggle.innerHTML = '<span class="visually-hidden">Toggle Dropdown</span>';
// dropdown menu
const dropMenu = document.createElement('ul');
dropMenu.className = 'dropdown-menu dropdown-menu p-0';
// moodle links have priority to be dropdown items but both can be!
const items = lvLink.c4_moodle_links?.length
? lvLink.c4_moodle_links.map(item => ({ text: item.lehrform, href: item.url }))
: lvLink.c4_linkList.map(([text, link]) => ({ text, href: link }));
items.forEach(({ text, href }) => {
const li = document.createElement('li');
const a = document.createElement('a');
a.className = 'dropdown-item border-bottom';
a.href = href;
a.target = '#';
a.textContent = text;
li.appendChild(a);
dropMenu.appendChild(li);
});
group.appendChild(button);
group.appendChild(toggle);
group.appendChild(dropMenu);
container.appendChild(group);
} else {
container.appendChild(this.createActionButton(lvLink));
}
})
}
return container;
},
createActionButton(lvLink){
const button = document.createElement('a');
button.className = 'fhc-body text-decoration-none text-truncate';
if (!lvLink.c4_link) button.classList.add('unavailable');
button.id = `${lvLink.name}_${lvLink.lehrveranstaltung_id}`;
const icon = lvLink.c4_icon2 ?? 'fa-solid fa-pen-to-square';
const label = lvLink.phrase ? this.$p.t(lvLink.phrase) : lvLink.name;
button.title = label;
button.innerHTML = `<i class="${icon}"></i><span style="margin-left:2px;">${label}</span>`;
button.addEventListener('click', (event) => {
event.preventDefault();
const url = this.c4_link(lvLink);
if (url) {
const target = lvLink.c4_target || '_blank';
if (target === '_blank') {
window.open(url, '_blank', 'noopener,noreferrer');
} else {
window.location.href = url;
}
} else {
console.warn("Link is unavailable for:", lvLink.name);
}
});
return button
},
loadState() {
return JSON.parse(localStorage.getItem(this.mylvTableOptions.persistenceID) || "null");
},
saveState(table) {
// avoid storing state after first restore part happened
if(!this.stateRestored) return
const rawLayout = table.getColumnLayout();
const state = {
columns: rawLayout.map(col => ({
field: col.field,
visible: col.visible,
width: col.width,
})),
sort: table.getSorters().map(s => ({
field: s.field,
dir: s.dir,
})),
filters: table.getFilters(),
headerFilters: table.getHeaderFilters()
};
localStorage.setItem(this.mylvTableOptions.persistenceID, JSON.stringify(state));
},
handleTableBuilt() {
const table = this.$refs.mylvTable.tabulator
this.tableBuiltResolve()
table.on("columnMoved", () => {
this.saveState(table);
});
table.on("columnResized", () => {
this.saveState(table);
});
table.on("columnVisibilityChanged", () => {
this.saveState(table);
});
table.on("filterChanged", () => {
this.saveState(table);
});
table.on("headerFilterChanged", () => {
this.saveState(table);
});
table.on("dataSorted", () => {
this.saveState(table);
});
table.on("columnSorted", () => {
this.saveState(table);
});
table.on("sortersChanged", () => {
this.saveState(table);
});
const saved = this.loadState();
table.on("renderComplete", () => {
if(!this.stateRestored) {
if (saved?.columns && !this.colLayoutRestored) {
const layout = saved.columns.map(col => ({
field: col.field,
width: col.width,
visible: col.visible,
// add more if needed, but keep it simple
}));
table.setColumnLayout(layout);
this.colLayoutRestored = true;
}
if (saved?.filters && !this.filtersRestored) {
this.filtersRestored = true // instantly avoid retriggers
table.setFilter(saved.filters);
}
if (saved?.headerFilters && !this.headerFiltersRestored) {
this.headerFiltersRestored = true // instantly avoid retriggers
for (let hf of saved.headerFilters) {
table.setHeaderFilterValue(hf.field, hf.value);
}
}
if (saved?.sort?.length && !this.sortRestored) {
this.sortRestored = true;
setTimeout(() => {
const sortList = saved.sort.map(s => {
const col = table.columnManager.findColumn(s.field);
if (!col) {
return null;
}
return { column: col, dir: s.dir };
}).filter(Boolean);
table.setSort(sortList);
}, 100);
}
this.stateRestored = true
}
});
},
async setupData() {
this.$refs.mylvTable.tabulator.setData(this.lvs);
},
async setupMounted() {
this.tableBuiltPromise = new Promise(this.tableResolve)
await this.tableBuiltPromise
this.setupData()
const tableID = this.tabulatorUuid ? ('-' + this.tabulatorUuid) : ''
const tableDataSet = document.getElementById('filterTableDataset' + tableID);
if(!tableDataSet) return
const rect = tableDataSet.getBoundingClientRect();
const h = window.visualViewport.height - rect.top - 50
if(this.$refs.mylvTable) {
this.$refs.mylvTable.$refs.table.style.setProperty('height', h+'px')
// necessary so the wrapping action row resolves to the full rowHeight required
// without the redraw here actions past the initial rowHeight would be clipped off
this.$refs.mylvTable.tabulator.redraw(true)
}
}
},
created() {
this.phrasenPromise = this.$p.loadCategory(['global', 'lehre', 'lvinfo'])
this.phrasenPromise.then(()=> {this.phrasenResolved = true})
},
mounted() {
this.setupMounted()
},
watch: {
lvs: {
async handler(newVal) {
await this.tableBuiltPromise;
if(!this.$refs.mylvTable?.tabulator) return
this.$refs.mylvTable.tabulator.setData(newVal);
const tableID = this.tabulatorUuid ? ('-' + this.tabulatorUuid) : ''
const tableDataSet = document.getElementById('filterTableDataset' + tableID);
if(!tableDataSet) return
const rect = tableDataSet.getBoundingClientRect();
const h = window.visualViewport.height - rect.top - 50
if(this.$refs.mylvTable) {
this.$refs.mylvTable.$refs.table.style.setProperty('height', h+'px')
}
},
deep: true
}
},
template: `
<div class="mylv-semester-table" v-if="ready">
<core-filter-cmpt
v-if="phrasenResolved"
@uuidDefined="handleUuidDefined"
:title="''"
ref="mylvTable"
:tabulator-options="mylvTableOptions"
:tabulator-events="mylvTableEventHandlers"
@tableBuilt="handleTableBuilt"
tableOnly
:sideMenu="false"
/>
</div>
<div v-if="tabulatorUuid === null" class="text-center d-flex justify-content-center align-items-center h-100" >
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>
`
};

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