Compare commits

..
Author SHA1 Message Date
Johann Hoffmann 90a3de0705 Merge branch 'feature-77588/AnwesenheitenSprint252' into demo 2026-10-08 16:25:30 +02:00
Johann Hoffmann 57d37e01a3 only make those anw_user_entries editable which come from LE's the user is allowed to change by isAdminOrTeachesLE function; also send the correct le_id of the different le_ids of detail component to backend, not the selected one of main view; simplify the entschuldigungsmanagement filter options and only have the "alle offenen Entschuldigungen unabhängig vom gewählten Zeitraum" button available which comes with a comprehensive tooltip in simple language what that button actually does since it can be confusing to interpret which timespan and which dataset is being delivered here. 💯💥 2026-10-08 16:22:28 +02:00
Johann Hoffmann 48c1787af9 Merge branch 'feature-77588/AnwesenheitenSprint252' into demo 2026-10-07 19:12:59 +02:00
Johann Hoffmann 0428c27bef fix delete dialog handling; new multiselect mode is now hidden behind a "Gesamtansicht" Button Switch; in Gesamtansicht remove Kontrolldatum select & Alle anzeigen and default to all LV-Teile selected; fix multiselect darkmode theme; align all ? tooltips to be the same component and enhanced that component with delayed sticky animation, then moved that to the core so we can ALL have fancy sticky tooltips; checked over existing phrasen for spelling mistakes; bunch more css adjustments; fixed a possible bug in getAnwForUid where only uid was verified and not person_id; renamed timeline tab and enabled it for students aswell -> configurable; 🤓; 2026-10-07 19:10:43 +02:00
Johann Hoffmann a5db0862d2 Merge branch 'feature-77588/AnwesenheitenSprint252' into demo 2026-10-01 12:23:01 +02:00
Johann Hoffmann b4fc8dfd45 status entry in legende for missing entry aka "-" 2026-10-01 12:03:34 +02:00
Johann Hoffmann 3470d6ee34 Merge branch 'feature-78489/CMS_Admin_Vue' into demo
# Conflicts:
#	system/phrasesupdate.php
2026-09-30 15:24:46 +02:00
Johann Hoffmann 229352e5e4 Contentcomponents.css want to go to git remote aswell 2026-09-30 15:16:39 +02:00
Johann Hoffmann 3dd9be5ea2 Merge branch 'master' into feature-78489/CMS_Admin_Vue
# Conflicts:
#	system/phrasesupdate.php
2026-09-30 15:13:51 +02:00
Johann Hoffmann 614274f3e1 darkmode stylecheck & adjustments across anw extension; ent management UX changes: datepicker labels, counts for filtered/total/offen/akzeptiert/abgelehnt + a button to just load all open excuse note applications no matter the timespan; added more clear visualization for multi stg students inside student uid col, studiengang col, orgform col and edit entschuldigung modal; fixed a bug in edit entschuldigung modal never showing to upload datum oopsie; 2026-09-30 15:07:45 +02:00
Johann Hoffmann c82a6e4c44 phrasens 2026-09-28 13:31:13 +02:00
Johann Hoffmann 791ae73627 Merge branch 'master' into feature-77588/AnwesenheitenSprint252 2026-09-28 12:35:40 +02:00
Johann Hoffmann 283865aa32 Merge remote-tracking branch 'origin/feature-76994/EntEditByStudentErrorFix' into feature-77588/AnwesenheitenSprint252
# Conflicts:
#	system/phrasesupdate.php
2026-09-28 11:26:11 +02:00
Johann Hoffmann 2bb59c9f66 fix a tabulator issue with dynamic columns and presets; legacy le selection now also uses the legacy loading procedure instead of the new one fetching all le of the lva, with the flag on only the assigned le to the looged in lektor; added phrasen; 2026-09-25 13:47:51 +02:00
Johann Hoffmann ba251c0aa7 Merge branch 'feature-78489/CMS_Admin_Vue' into demo
# Conflicts:
#	system/phrasesupdate.php
2026-09-24 10:21:36 +02:00
Johann Hoffmann 98ceeb862d not only log sophisticated upload errors into log table, also tell the user what went wrong with the upload as per codeigniter 3 upload class; 2026-09-23 15:49:03 +02:00
ma0048 95bd12941a Merge branch 'feature-54225/reihungstest_ablauf_fuer_quereinsteiger' into demo
# Conflicts:
#	cis/testtool/menu.php
#	system/dbupdate_3.4.php
2026-09-16 07:28:20 +02:00
Johann Hoffmann d1bd77efb9 enhanced tree filters: by template, date range, child count, content length, editor; hierarchy display mode for tree search results; removed clickstats to configurable tab behind admin berechtigung; added a cis4 preview iframe which builds content the same way cis4 would do, which resemble the cis4 stylesheets and the resolving of contentcomponents; block clickcounting for cis4 preview to not skew statistics; Children Tab is now a Hierarchy Tab which shows parents aswell; added drag&drop sort; Delete Tab shows existing references with db table name; content components got default cis4 styling; added tiefe parameter for recursive lookup of organisationseinheiten or dms groups; DMS query also checks user entitlement since there might be sensitive documents in reach if a high enough dms group would be chosen with a deep enough tiefe; version select has a shorthand mode for contents with large versioncount; 2026-09-08 11:42:39 +02:00
Johann Hoffmann 086d20c1c9 staged missing file 2026-09-02 09:44:28 +02:00
Johann Hoffmann 9f29a7798a introduced dynamic cms content blocks, which can be predefined by adding another vue component to contentcomponents and following the catalog.js/components.js registration convention, which exists to avoid enabling the use of vue components inside content which where never designed for isolated use. contentcomponents could also be reused by traditional vue apps/components if need be; added the most used/requested dynamic content building blocks: OePersonen.js - all persons of a chosen OE; PersonKontakt.js - same but by uid; DmsListe.js - file links of a dms category; DmsDokumente.js - same but by dms_id; scan.js finds the divs inside cms contents tagged with "data-fhc-content" and teleports the actual vue component into the DOM structure of the rendered content. If there are issues/problems with the defined & allowed properties the component is skipped to avoid breaking pages with faulty configs; WIP testing edge cases with such vue content blocks and refining the feature idea in general; 2026-09-01 15:31:55 +02:00
Johann Hoffmann 09ee72bd50 fixed error paths in Auth_Controller for content since it has no terminateWithError; tree reloads only nodes affected instead of complete refetch; simplified url & language options; added fhcAlert toasts to api calls; some bugfixes; still pondering how to properly utilize generic placeholders in contents; 2026-08-28 13:28:15 +02:00
Johann Hoffmann 6af2c7e11a improved content tabs behaviour all around; changed lock mechanism to 24 hour expiry if not manually unlocked; added rank by clicks (sourced from system.tbl_webservicelog) since timestamp filter feature in order to find out which contents are important; general UI improvements; a ton of phrasen; some code cleanup; WIP finetuning & revamping the content features themselves; 2026-08-27 17:57:39 +02:00
Johann Hoffmann 86a8350bdc fix phrasesupdate.php missing colon 2026-08-27 09:32:59 +02:00
Johann Hoffmann 943999cef9 adapt filter/tablePresets/tablePresetsModule to account for dynamic tables with a new presetColumns property. by default the preset holds all columns, if a scope is given it only holds those columns, their header filters and their sort; on apply the scope is merged into the current table columns so the dynamic ones keep their place; 2026-08-26 16:45:06 +02:00
Johann Hoffmann 6d9ce91302 Merge remote-tracking branch 'origin/master' into feature-77588/AnwesenheitenSprint252
# Conflicts:
#	system/phrasesupdate.php
2026-08-26 16:31:28 +02:00
Johann Hoffmann 1802f9d32c cms admin first draft, needs a lot of polishing still; 2026-08-26 11:18:26 +02:00
Johann Hoffmann 7e01142016 anw widget sql scripts for standalone linklist and once utilizing fhc-calendar list view; also added anw link in lehreinheiten renderer modalContent as new row 2026-07-21 18:30:24 +02:00
Johann Hoffmann 04d90f53d3 adapt stundeplanlib expandObjectInformation when called by getEventsEmployee with an event called extendStundenplanData, which allows extensions like digi anw to plant their own data object inside the stundenplan events in order to conditionally render something like currently in the lehreinheiten calendarEvent.js renderer -> parameterized link to anw extension in order to avoid duplicating functionality across widgets when there is already one doing it very well; 2026-07-20 16:40:57 +02:00
Johann Hoffmann 7fdfa1481e new phrases 2026-07-09 16:00:42 +02:00
Johann Hoffmann 285a2921e4 code cleanup & refactoring lektor frontend/backend; transactions on important concurrent operations like deleteAnwesenheitskontrolle & getNewQRCode; show all flag saved/fetch from local storage; hardcoded strings -> phrasen; 2026-07-03 14:23:14 +02:00
341 changed files with 17915 additions and 33624 deletions
-9
View File
@@ -23,15 +23,6 @@ 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(
+21
View File
@@ -0,0 +1,21 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
// Preferred organisational unit for a new content.
// If the user is entitled for this unit, it wins.
// If not, the first entitled unit wins.
$config['default_oe_kurzbz'] = 'etw';
// Lifetime of an edit lock in hours, counted from tbl_contentlog.start. Must be > 0.
$config['lock_ttl_hours'] = 24;
// Switches the click statistics off completely. With false the admin tab stays hidden and
// the endpoint refuses every request, whatever the rights of the caller are.
$config['clickstats_enabled'] = true;
// Default period of the click statistics in months, when the caller sends none.
$config['clickstats_months'] = 12;
// Length of the ranking in the click statistics tab.
$config['clickstats_limit'] = 100;
-64
View File
@@ -1,64 +0,0 @@
<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');
// Noten die keinen Prüfungsantritt verbrauchen. Fallback-PKs; massgeblich sind die per Bezeichnung
// aufgelösten Noten (NOTEN_OHNE_ANTRITT_BEZEICHNUNGEN), da die PKs je Installation abweichen.
$config['NOTEN_OHNE_ANTRITT'] = [9, 17]; // tbl_note pk
// 'Nicht beurteilt' ist eine Altlast: formal eine Note, inhaltlich keine Leistungsfeststellung.
// Wird weiterhin häufig vergeben und darf daher keinen Antritt kosten.
$config['NOTEN_OHNE_ANTRITT_BEZEICHNUNGEN'] = ['Noch nicht eingetragen', 'entschuldigt', 'Nicht beurteilt'];
// Anrechnungsnoten: die Leistung wurde vorab anerkannt, der/die Studierende ist zwar Teil der
// Lehrveranstaltung, tritt aber zu keiner Prüfung an. Solange die ZEUGNISNOTE eine davon ist, sind
// für diese Lehrveranstaltung keine Prüfungen möglich. Auflösung wie oben über die Bezeichnung,
// die PKs sind nur Fallback.
$config['NOTEN_ANRECHNUNG_BEZEICHNUNGEN'] = ['angerechnet', 'intern angerechnet'];
$config['NOTEN_ANRECHNUNG'] = [6, 16]; // tbl_note pk
$config['NOTEN_OCCURANCE_LIMIT_MAP'] = [17 => 1]; // über alle Antritte hinweg nur ein entschuldigt
// tbl_note pk of the 'entschuldigt' note. An entschuldigt Termin is preserved as its own dated
// entry when a new pruefung of the same type is created (instead of being overwritten).
$config['NOTE_ENTSCHULDIGT'] = 17;
// The maximum number of attempts that count, the first attempt and the kommissionelle attempt
// included. null derives the number from the old flags:
// 1 (the original assessment) + TERMIN2 + TERMIN3 + KOMMPRUEF
// The examination rules permit three attempts. With TERMIN2 and KOMMPRUEF the formula gives 3.
// An installation with TERMIN3 gets 4, which is one more attempt in the same chain.
$config['CIS_GESAMTNOTE_MAX_ANTRITTE'] = null;
// The exam types that take place before a commission.
$config['PRUEFUNG_KOMMISSIONELL_TYPEN'] = ['kommPruef', 'zusKommPruef'];
// The type that the tool writes for the last attempt. The last attempt is always kommissionell.
$config['PRUEFUNG_TYP_KOMMISSIONELL'] = 'kommPruef';
// Exam types that never use an attempt. A zusKommPruef repeats a kommissionelle Prüfung that had
// a procedural fault. It stands outside the attempt chain, and the student administration enters
// it. The examination rules do not describe it.
$config['PRUEFUNG_TYPEN_OHNE_ANTRITT'] = ['zusKommPruef'];
// Spaltenaufteilung der Prüfungen in der Notentabelle:
// 'antritt' - eine Spalte je Antrittsnummer, Datum steht in der Zelle (robust bei Einzelterminen)
// 'datum' - eine Spalte je Prüfungsdatum (kompakt, wenn ganze Jahrgänge am selben Tag antreten)
// Nur die Vorgabe; im Tool umschaltbar und pro Benutzer gespeichert.
$config['CIS_GESAMTNOTE_PRUEFUNGSSPALTEN'] = 'antritt';
// Bei der Notenübernahme durch die Assistenz den ersten Antritt als eigene Prüfung anlegen.
$config['CIS_GESAMTNOTE_ERSTANTRITT_BEI_UEBERNAHME'] = true;
// availability of the two Benotungstool import flows. When both are true they are shown as
// separate buttons/dialogs.
$config['CIS_GESAMTNOTE_PRUEFUNGSIMPORT'] = true; // dated import that creates a pruefung per row
$config['CIS_GESAMTNOTE_NOTENIMPORT'] = false; // classic note-only import (uid + note, no date)
// Noteneintragungsfrist (Prüfungsordnung §1): grade/pruefung entry is only permitted up to this
// deadline. The month/day below is applied to the studiensemester's year:
// Sommersemester (SSyyyy) -> deadline in the SAME calendar year (default 15th November)
// Wintersemester (WSyyyy) -> deadline in the FOLLOWING calendar year (default 15th May)
$config['CIS_GESAMTNOTE_NOTENEINTRAGUNGSFRIST'] = false; // switch to use the window enforcement
$config['NOTENEINTRAGUNGSFRIST_SS'] = ['month' => 11, 'day' => 15]; // Sommersemester deadline (same year)
$config['NOTENEINTRAGUNGSFRIST_WS'] = ['month' => 5, 'day' => 15]; // Wintersemester deadline (following year)
+6 -25
View File
@@ -61,29 +61,12 @@ $route['api/v1/organisation/[O|o]rganisationseinheit/(:any)'] = 'api/v1/organisa
$route['api/v1/ressource/[B|b]etriebsmittelperson/(:any)'] = 'api/v1/ressource/betriebsmittelperson2/$1';
$route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1';
// Legacy CMS news routes
$route['api/frontend/v1/Cms/getNewsItem/(:num)'] = 'api/frontend/v1/NewsAdministrationAPI/getNewsItem/$1';
$route['api/frontend/v1/Cms/storeNewsItem'] = 'api/frontend/v1/NewsAdministrationAPI/storeNewsItem';
$route['api/frontend/v1/Cms/updateNewsItem/(:num)'] = 'api/frontend/v1/NewsAdministrationAPI/updateNewsItem/$1';
$route['api/frontend/v1/Cms/deleteNewsItem/(:num)'] = 'api/frontend/v1/NewsAdministrationAPI/deleteNewsItem/$1';
// Keep legacy uppercase URLs refreshable while using the canonical controller method casing
$route['CisVue/Cms/NewsAdministration'] = 'CisVue/Cms/newsAdministration';
$route['Cis/LvPlan/.*'] = 'Cis/LvPlan/index/$1';
$route['Cis/MyLvPlan/.*'] = 'Cis/MyLvPlan/index/$1';
$route['Cis/MyLv/.*'] = 'Cis/MyLv/index/$1';
$route['Cis/OtherLvPlan/.*'] = 'Cis/OtherLvPlan/index/$1';
//Route for LV Plan Stg/Semester/Verband/Gruppe
$route['Cis/StgOrgLvPlan/.*'] = 'Cis/StgOrgLvPlan/index/$1';
$route['Cis/Benotungstool/.*'] = 'Cis/Benotungstool/index/$1';
$route['Cis/MyReservation'] = 'Cis/MyReservation/index/$1';
//Route for Zeitsperrenma
$route['Cis/Zeitsperrenma/.*'] = 'Cis/Zeitsperrenma/index/$1';
//Route für Zeitsperren
$route['Cis/Zeitsperren/.*'] = 'Cis/Zeitsperren/index/$1';
$route['Abgabetool/Assistenz'] = 'Cis/Abgabetool/Assistenz';
$route['Abgabetool/Assistenz/(:any)'] = 'Cis/Abgabetool/Assistenz/$1';
@@ -92,6 +75,10 @@ $route['Abgabetool/Student'] = 'Cis/Abgabetool/Student';
$route['Abgabetool/Student/(:any)'] = 'Cis/Abgabetool/Student/$1';
$route['Abgabetool/Deadlines'] = 'Cis/Abgabetool/Deadlines';
// CMS Admin App Routes
$route['cms'] = 'cms/index';
$route['cms/.*'] = 'cms/index';
// Studierendenverwaltung App Routes
$route['studvw'] = 'studentenverwaltung/index';
$route['studvw/.*'] = 'studentenverwaltung/index';
@@ -146,19 +133,13 @@ $route['api/frontend/v1/treemenudata/stv/.*'] = 'api/frontend/v1/stv/students/in
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByStg/$1/$2';
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/semester/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByStg/$1/$2/$3';
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/semester/(:any)/.*'] = 'api/frontend/v1/Lehrveranstaltung/getByStg/$1/$2/$3';
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/orgform/(:any)/semester/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByStgOrgform/$1/$2/$3/$4';
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/orgform/(:any)/semester/(:any)/.*'] = 'api/frontend/v1/Lehrveranstaltung/getByStgOrgform/$1/$2/$3/$4';
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/orgform/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByStgOrgform/$1/$2/$3';
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/orgform/(:any)/.*'] = 'api/frontend/v1/Lehrveranstaltung/getByStgOrgform/$1/$2/$3';
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/orgform/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByStg/$1/$2/$3';
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/orgform/(:any)/.*'] = 'api/frontend/v1/Lehrveranstaltung/getByStg/$1/$2/$3';
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/emp/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByEmp/$1/$2';
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/emp/(:any)/stg/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByEmp/$1/$2/$3';
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/emp/(:any)/stg/(:any)/.*'] = 'api/frontend/v1/Lehrveranstaltung/getByEmp/$1/$2/$3';
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/le/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByLe/$1/$2';
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/lv/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByLv/$1/$2';
// load routes from extensions, also look for environment-specific configs
$subdirs = ['application/config/extensions', 'application/config/' . ENVIRONMENT . '/extensions'];
+2 -3
View File
@@ -26,8 +26,7 @@ $config['root'] = [
'verband' => [
'group'
]
]
],
'orgform'
]
];
$config['root']['stg']['orgform'] = $config['root']['stg'];
@@ -1,37 +0,0 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*
*/
class Benotungstool extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct([
'index' => array('lehre/benotungstool:rw', 'lehre/benotungstool_assistenz:rw')
]);
$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']);
}
}
@@ -1,33 +0,0 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*
*/
class MyReservation extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct([
'index' => ['basis/cis:r']
]);
// Load Config
$this->load->config('calendar');
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @return void
*/
public function index()
{
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'MyReservation']);
}
}
@@ -1,30 +0,0 @@
<?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']);
}
}
@@ -1,30 +0,0 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Zeitsperren extends Auth_Controller
{
public function __construct()
{
parent::__construct([
'index' => ['cis/eigene_zeitsperren: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']);
}
}
@@ -1,31 +0,0 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*
*/
class Zeitsperrenma extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct([
'index' => ['cis/andere_zeitsperren:r']
]);
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @return void
*/
public function index()
{
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'Zeitsperrenma']);
}
}
+14 -20
View File
@@ -17,15 +17,14 @@ class Cms extends Auth_Controller
array(
'content' => 'basis/cis:r',
'getNews' => 'basis/cis:r',
'getNewsRowCount' => 'basis/cis:r',
'getRoomInformation' => 'basis/cis:r',
'news' => 'basis/cis:r',
'newsAdministration' => ['basis/news:r'],
'news' => 'basis/cis:r'
)
);
// Loads Libraries
$this->load->library('CmsLib');
$this->load->library('PermissionLib');
// Loads phrases system
$this->loadPhrases([
@@ -45,19 +44,22 @@ class Cms extends Auth_Controller
*
* @return void
*/
public function content($content_id, $version = null, $sprache = null, $sichtbar = true)
public function content($content_id = null, $version = null, $sprache = null, $sichtbar = true)
{
// return early if the content_id for the content is missing
if (!isset($content_id))
$this->terminateWithError("content_id is missing");
if (!is_numeric($content_id))
show_404();
$content = $this->ContentModel->load($content_id);
if (isError($content))
$this->terminateWithError(getError($content));
{
show_error('The content could not be loaded');
}
$content = getData($content);
if (NULL === $content)
$this->terminateWithError("Content not found");
if (NULL === $content) {
show_404();
}
$content = current($content);
@@ -84,18 +86,10 @@ class Cms extends Auth_Controller
*/
public function news($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true)
{
$this->load->view('CisRouterView/CisRouterView.php', [
'route' => 'News',
]);
$viewData = array();
$this->load->view('CisRouterView/CisRouterView.php', ['viewData'=>$viewData, 'route' => 'News']);
}
public function newsAdministration()
{
$this->load->view('CisRouterView/CisRouterView.php', [
'route' => 'NewsAdministration',
]);
}
public function getRoomInformation($ort_kurzbz)
{
// Load Config
+23
View File
@@ -0,0 +1,23 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Cms extends Auth_Controller
{
public function __construct()
{
parent::__construct(['index' => 'basis/cms:r']);
$this->load->library('PermissionLib');
$this->config->load('cms');
$this->loadPhrases(['global', 'ui', 'cms']);
}
public function index()
{
// decide whether the click statistics tab is shown at all.
$this->load->view('Cms.php', [
'clickstats' => $this->config->item('clickstats_enabled')
&& $this->permissionlib->isBerechtigt('admin')
]);
}
}
@@ -1916,7 +1916,7 @@ class Abgabe extends FHCAPI_Controller
if (!$mailres)
{
$this->terminateWithError($this->p->t('abgabetool', 'c4fehlerMailZweitBegutachterv2'), 'general');
$this->terminateWithError($this->p->t('abgabetool', 'c4fehlerMailBegutachterv2'), 'general');
}
}
@@ -1,48 +0,0 @@
<?php
/**
* Copyright (C) 2026 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (!defined('BASEPATH')) exit('No direct script access allowed');
class Benutzer extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getUserData' => self::PERM_LOGGED,
]);
$this->load->model('person/Benutzer_model', 'BenutzerModel');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function getUserData()
{
$uid = $this->input->get("uid") ?? getAuthUID();
$this->BenutzerModel->addSelect("uid, person_id, insertamum");
$userResult = $this->BenutzerModel->load(["uid" => $uid]);
$userData = $this->getDataOrTerminateWithError($userResult);
$this->terminateWithSuccess($userData);
}
}
+366 -46
View File
@@ -25,6 +25,10 @@ if (! defined('BASEPATH')) exit('No direct script access allowed');
*/
class Cms extends FHCAPI_Controller
{
// Upper bound for the tiefe of oe-personen and dms-liste. A hierarchy this deep does
// not occur, and the cap keeps one marker from walking a whole tree.
const MAX_TIEFE = 10;
/**
* Object initialization
@@ -36,15 +40,19 @@ class Cms extends FHCAPI_Controller
'ContentID' => self::PERM_LOGGED,
'getOrtKurzbzContent' => self::PERM_LOGGED,
'content' => self::PERM_LOGGED,
'newsForWidget' => self::PERM_LOGGED,
'news' => self::PERM_LOGGED,
'getNewsRowCount' => self::PERM_LOGGED,
'getNews' => self::PERM_LOGGED,
'getOePersonen' => self::PERM_LOGGED,
'getDmsKategorie' => self::PERM_LOGGED,
'getContentChilds' => self::PERM_LOGGED,
'getPerson' => self::PERM_LOGGED,
'getDmsDokumente' => self::PERM_LOGGED,
]);
$this->load->model('content/News_model', 'NewsModel');
$this->load->model('crm/Student_model', 'StudentModel');
$this->load->model('crm/Prestudent_model', 'PrestudentModel');
// setting up the papgination_size
$this->page_size = 10;
@@ -56,10 +64,7 @@ class Cms extends FHCAPI_Controller
]);
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
//------------------------------------------------------------------------------------------------------------------
// Public methods
@@ -80,7 +85,10 @@ class Cms extends FHCAPI_Controller
$sprache = $this->input->get("sprache",TRUE);
$sichtbar = $this->input->get("sichtbar",TRUE);
$content = $this->cmslib->getContent($content_id, $version, $sprache, $sichtbar);
// cms admin preview renders a content without counting a view
$preview = $this->input->get("preview",TRUE);
$content = $this->cmslib->getContent($content_id, $version, $sprache, $sichtbar, !$preview);
$content = $this->getDataOrTerminateWithError($content);
$this->terminateWithSuccess($content);
@@ -106,7 +114,7 @@ class Cms extends FHCAPI_Controller
$this->terminateWithSuccess($content_id);
}
public function newsForWidget()
public function news()
{
// form validation
@@ -118,7 +126,7 @@ class Cms extends FHCAPI_Controller
$this->load->model('content/news_model', 'NewsModel');
$limit = $this->input->get('limit',TRUE);
//query the news
$news = $this->NewsModel->getAll($limit);
@@ -157,7 +165,23 @@ class Cms extends FHCAPI_Controller
}
public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = false, $titel = '', $edit = false, $sichtbar = true)
public function getNewsRowCount($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $fachbereich_kurzbz = null, $maxalter = 0, $edit = false, $sichtbar = true, $page = 1, $page_size = 10)
{
list($studiengang_kz, $semester) = $this->cmslib->getStgAndSem($studiengang_kz, $semester);
$all = $edit;
$this->load->model('content/News_model','NewsModel');
$num_rows = $this->NewsModel->countNewsWithContent(getSprache(), $studiengang_kz, $semester, $fachbereich_kurzbz, $sichtbar, $maxalter, $page, $page_size, $all, $mischen);
$num_rows = $this->getDataOrTerminateWithError($num_rows);
$this->terminateWithSuccess($num_rows);
}
public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true)
{
//form validation
$this->load->library('form_validation');
@@ -177,45 +201,341 @@ class Cms extends FHCAPI_Controller
// default value for the page_size is 10
$page_size = $page_size ?? 10;
$passedSichtbar = $this->input->get('published', true);
if($passedSichtbar !== null)
{
$sichtbar = $passedSichtbar;
}
$filterForDegreePrograms = false;
$entitledDegreePrograms = [];
$student = $this->StudentModel->loadWhere(['student_uid' => getAuthUID()]);
if(isError($student))
{
$this->terminateWithError(getError($student));
}
if(hasData($student))
{
$student = current(getData($student));
$degreeProgramSemesterDataResult = $this->PrestudentModel->getStudiengaengAndAusbildungssemesterByStudentUid(getAuthUID());
$degreeProgramSemesterData = $this->getDataOrTerminateWithError($degreeProgramSemesterDataResult)[0];
if ($degreeProgramSemesterData) {
$studiengang_kz = isset($degreeProgramSemesterData->studiengang_kz) ? $degreeProgramSemesterData->studiengang_kz : null;
$semester = isset($degreeProgramSemesterData->ausbildungssemester) ? $degreeProgramSemesterData->ausbildungssemester : null;
}
$mischen = true;
} else {
$filterForDegreePrograms = true;
$entitledDegreePrograms = $this->permissionlib->getSTG_isEntitledFor("basis/news");
array_push($entitledDegreePrograms, 0);
}
$news = $this->cmslib->getNews($infoscreen, $studiengang_kz, $semester, $mischen, $titel, $edit, $sichtbar, $page, $page_size, $sprache, $filterForDegreePrograms, $entitledDegreePrograms, true);
$news = $this->cmslib->getNews($infoscreen, $studiengang_kz, $semester, $mischen, $titel, $edit, $sichtbar, $page, $page_size, $sprache);
$news = $this->getDataOrTerminateWithError($news);
$this->addMeta('row_count', $news["row_count"] ?? 0);
$this->terminateWithSuccess($news["content"]);
$this->addMeta('phrases', json_decode($this->p->getJson()));
$this->terminateWithSuccess($news);
}
/**
* Lists the people assigned to an organisation unit.
* Used by the CMS content component "oe-personen".
*/
public function getOePersonen()
{
// form validation
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('oe_kurzbz', 'Organisationseinheit', 'required');
if ($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
$this->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
$oe_kurzbz = $this->input->get('oe_kurzbz', TRUE);
$mitFoto = $this->input->get('foto', TRUE) ? true : false;
// tiefe 0 asks about the one unit. Deeper takes the sub units with it.
$oeResult = $this->OrganisationseinheitModel->getMitUntereinheiten(
$oe_kurzbz, $this->_tiefe()
);
$oeRows = $this->getDataOrTerminateWithError($oeResult) ?: array();
$uids = array();
$funktionen = array();
foreach ($oeRows as $oe)
{
// 'oezuordnung' is the function that assigns an employee to an organisation
// unit. The model already filters datum_von and datum_bis.
$rows = $this->BenutzerfunktionModel->getBenutzerFunktionenDetailed(
'oezuordnung', $oe->oe_kurzbz
);
$rows = $this->getDataOrTerminateWithError($rows) ?: array();
foreach ($rows as $row)
{
// The model returns the aktiv flag but does not filter on it.
if (!$row->aktiv)
continue;
// One person can hold the assignment in several units of the walk. The
// first one wins, so the list keeps one block per person.
if (isset($funktionen[$row->uid]))
continue;
$uids[] = $row->uid;
// tbl_benutzerfunktion.bezeichnung is the free label of one assignment,
// for example "Bibliothekarin". tbl_funktion.beschreibung is the generic
// name.
$funktionen[$row->uid] = $row->bezeichnung ?: $row->beschreibung;
}
}
$this->terminateWithSuccess($this->_cmsPersonen($uids, $funktionen, $mitFoto));
}
/**
* Contact data of one person.
* Used by the CMS content component "person-block".
*/
public function getPerson()
{
// form validation
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('uid', 'UID', 'required');
if ($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$uid = $this->input->get('uid', TRUE);
$mitFoto = $this->input->get('foto', TRUE) ? true : false;
// The function label comes from the marker, not from the database. The component
// passes it through as a property.
$personen = $this->_cmsPersonen(array($uid), array(), $mitFoto);
$this->terminateWithSuccess($personen ? current($personen) : null);
}
/**
* Lists the published documents of one DMS category.
* Used by the CMS content component "dms-liste".
*/
public function getDmsKategorie()
{
// form validation
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('kategorie_kurzbz', 'DMS-Kategorie', 'required');
if ($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$kategorie_kurzbz = $this->input->get('kategorie_kurzbz', TRUE);
$erlaubt = $this->_dmsKategorieErlaubt($kategorie_kurzbz);
if ($erlaubt === null)
$this->terminateWithError('DMS category not found', self::ERROR_TYPE_404);
// a missing entitlement answers with success and an empty list, not with
// an error. The component sits inside a public content page. An error would raise a
// toast for every reader who is simply not in the group.
$dokumente = array();
if ($erlaubt)
{
// tiefe 0 asks about the one category. Deeper takes the sub categories with it.
$katResult = $this->DmsModel->getMitUnterkategorien(
$kategorie_kurzbz, $this->_tiefe()
);
$kategorien = $this->getDataOrTerminateWithError($katResult) ?: array();
foreach ($kategorien as $kat)
{
// Every category carries its own group rule, so a sub category can be
// closed while the parent is open. Ask for each one instead of letting the
// entitlement of the root stand for the whole walk. The root is already
// answered above.
if ($kat->kategorie_kurzbz !== $kategorie_kurzbz
&& $this->_dmsKategorieErlaubt($kat->kategorie_kurzbz) !== true)
continue;
$rows = $this->DmsModel->getKategorieDokumente($kat->kategorie_kurzbz);
$rows = $this->getDataOrTerminateWithError($rows) ?: array();
foreach ($rows as $row)
$dokumente[] = $row;
}
}
$this->terminateWithSuccess(array(
'zugriff' => $erlaubt,
'dokumente' => $dokumente
));
}
/**
* Lists named documents, in the order the caller asked for them.
* Used by the CMS content component "dms-dokumente".
*/
public function getDmsDokumente()
{
// form validation
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('dms_ids', 'DMS-IDs', 'required');
if ($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$this->load->model('content/Dms_model', 'DmsModel');
$ids = array();
foreach (explode(',', $this->input->get('dms_ids', TRUE)) as $teil)
{
$teil = trim($teil);
if (is_numeric($teil))
$ids[] = (int) $teil;
}
// An unbounded list would let one marker fetch the whole DMS.
$ids = array_slice(array_unique($ids), 0, 50);
$dokumente = $this->DmsModel->getDokumenteByIds($ids);
$dokumente = $this->getDataOrTerminateWithError($dokumente) ?: array();
// A hand picked list holds few categories, so deciding per category is cheap and
// reuses the rule of getDmsKategorie().
$erlaubte = array();
$nachId = array();
foreach ($dokumente as $dokument)
{
if (!array_key_exists($dokument->kategorie_kurzbz, $erlaubte))
$erlaubte[$dokument->kategorie_kurzbz] = $this->_dmsKategorieErlaubt($dokument->kategorie_kurzbz);
if ($erlaubte[$dokument->kategorie_kurzbz] === true)
$nachId[$dokument->dms_id] = $dokument;
}
// The editor chose the order. Keep it.
$sortiert = array();
foreach ($ids as $id)
{
if (isset($nachId[$id]))
$sortiert[] = $nachId[$id];
}
$this->terminateWithSuccess($sortiert);
}
/**
* Lists the child pages of a content, as a reader may see them.
* Used by the CMS content component "contentchild-menu".
*/
public function getContentChilds()
{
// form validation
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural_no_zero');
if ($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$this->load->model('content/Contentchild_model', 'ContentchildModel');
$content_id = $this->input->get('content_id', TRUE);
$sprache = $this->input->get('sprache', TRUE);
if (!$sprache)
$sprache = getUserLanguage();
$childs = $this->ContentchildModel->getChildsForReader($content_id, $sprache, getAuthUID());
$this->terminateWithSuccess($this->getDataOrTerminateWithError($childs) ?: array());
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
/**
* Builds the shape both person content components render.
*
* The assembly follows Cis/Cms/News/Xml/Address/Detailed.php, so a person reads the
* same in the news extras and on a content page.
*
* @param array $uids in the order the result should keep
* @param array $funktionen uid => function label, may be empty
* @param boolean $mitFoto
* @return array
*/
private function _cmsPersonen($uids, $funktionen, $mitFoto)
{
if (empty($uids))
return array();
$this->load->model('person/Person_model', 'PersonModel');
$kontakt = $this->PersonModel->getCmsKontakt($uids, $mitFoto);
$kontakt = $this->getDataOrTerminateWithError($kontakt) ?: array();
$nachUid = array();
foreach ($kontakt as $row)
$nachUid[$row->uid] = $row;
$personen = array();
// Loops over $uids, not over the result, so the caller keeps its order.
foreach ($uids as $uid)
{
if (!isset($nachUid[$uid]))
continue;
$row = $nachUid[$uid];
$name = trim(implode(' ', array_filter(array(
$row->titelpre, $row->vorname, $row->nachname, $row->titelpost
))));
$telefon = '';
if ($row->telefonklappe)
$telefon = trim(($row->kontakt ?: '') . ' - ' . $row->telefonklappe);
$email = '';
if (defined('DOMAIN'))
$email = ($row->alias ?: $row->uid) . '@' . DOMAIN;
$personen[] = array(
'uid' => $row->uid,
'name' => $name,
'funktion' => isset($funktionen[$uid]) ? $funktionen[$uid] : '',
'telefon' => $telefon,
'email' => $email,
'ort' => $row->planbezeichnung,
'foto' => $row->foto
);
}
return $personen;
}
/**
* Decides whether the current user may see a DMS category.
*
* Same rule as cms/dms.php: an unlocked category is open to everyone. A locked one
* needs a granted group, or the category permission together with basis/dms.
*
* @param string $kategorie_kurzbz
* @return boolean|null null when the category does not exist
*/
/**
* Reads the tiefe parameter of a content component. 0 keeps the component to the unit
* or category it names, which is what a marker without the attribute means.
* Capped, because the depth drives a recursive walk and one query per level found.
*
* @return int
*/
private function _tiefe()
{
$tiefe = (int) $this->input->get('tiefe', TRUE);
if ($tiefe < 0)
return 0;
return min($tiefe, self::MAX_TIEFE);
}
private function _dmsKategorieErlaubt($kategorie_kurzbz)
{
$this->load->library('PermissionLib');
$this->load->model('content/Dms_model', 'DmsModel');
$zugriff = $this->DmsModel->getKategorieZugriff($kategorie_kurzbz, getAuthUID());
$zugriff = $this->getDataOrTerminateWithError($zugriff);
if (empty($zugriff))
return null;
$kategorie = current($zugriff);
if (!$kategorie->gesperrt || $kategorie->in_gruppe)
return true;
$erlaubt = $this->permissionlib->isBerechtigt('basis/dms');
if ($erlaubt && $kategorie->berechtigung_kurzbz != '')
$erlaubt = $this->permissionlib->isBerechtigt($kategorie->berechtigung_kurzbz);
return $erlaubt;
}
}
@@ -0,0 +1,760 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class CmsAdmin extends FHCAPI_Controller
{
const MAX_TREE_DEPTH = 50;
public function __construct()
{
parent::__construct([
'getTree' => ['basis/cms:r'],
'getSubtree' => ['basis/cms:r'],
'getContent' => ['basis/cms:r'],
'getContentsprache' => ['basis/cms:r'],
'getTemplates' => ['basis/cms:r'],
'getOrganisationseinheiten' => ['basis/cms:r'],
'getSprachen' => ['basis/cms:r'],
'getDmsKategorien' => ['basis/cms:r'],
'getMitarbeiter' => ['basis/cms:r'],
'getDmsKategorieDokumente' => ['basis/cms:r'],
'getUsage' => ['basis/cms:r'],
'getTreeMeta' => ['basis/cms:r'],
'getAncestors' => ['basis/cms:r'],
'getClickCount' => ['basis/cms:r'],
'getClickRanking' => ['basis/cms:r'],
'postContent' => ['basis/cms:rw'],
'postTranslation' => ['basis/cms:rw'],
'postVersion' => ['basis/cms:rw'],
'putProperties' => ['basis/cms:rw'],
'deleteContent' => ['basis/cms:rw'],
'deleteContentsprache' => ['basis/cms:rw']
]);
$this->load->library('CmsAdminLib');
$this->load->library('PermissionLib');
$this->load->model('content/Content_model', 'ContentModel');
$this->load->model('content/Contentsprache_model', 'ContentspracheModel');
$this->load->model('content/Contentchild_model', 'ContentchildModel');
$this->load->model('content/Contentgruppe_model', 'ContentgruppeModel');
$this->load->model('content/Template_model', 'TemplateModel');
$this->loadPhrases(['global', 'cms']);
}
public function getTree()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('menu', 'Menu', 'required|in_list[content,news]');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$menu = $this->input->get('menu', TRUE);
$filter = $this->input->get('filter', TRUE);
$entitledResult = $this->cmsadminlib->getEntitledOe();
$entitledOe = isError($entitledResult) ? [] : getData($entitledResult) ?? [];
$nodes = [];
if ($menu === 'news')
{
$newsResult = $this->ContentModel->getNewsContent();
if (!isError($newsResult) && getData($newsResult))
{
foreach (getData($newsResult) as $row)
{
$titel = '';
$csResult = $this->ContentspracheModel->getOne(
(int) $row->content_id, DEFAULT_LANGUAGE
);
if (!isError($csResult))
$titel = getData($csResult)->titel;
$node = new stdClass();
$node->content_id = (int) $row->content_id;
$node->titel = mb_substr($titel, 0, 15) . ' '
. date('d.m.Y', strtotime($row->insertamum));
$node->template_kurzbz = $row->template_kurzbz;
$node->oe_kurzbz = $row->oe_kurzbz;
$node->aktiv = ($row->aktiv === 't' || $row->aktiv === true);
$node->entitled = false;
$node->groups = [];
$node->children = [];
$nodes[] = $node;
}
}
}
else
{
if (!empty($filter))
{
$clean = str_replace(['!', '.', '?', ','], '', $filter);
$searchItems = preg_split('/\s+/', $clean, -1, PREG_SPLIT_NO_EMPTY);
$rootsResult = $this->ContentModel->searchCms($searchItems);
}
else
{
$rootsResult = $this->ContentModel->getRootContent();
}
if (!isError($rootsResult) && getData($rootsResult))
{
$visited = [];
foreach (getData($rootsResult) as $row)
{
$node = $this->buildTreeNode(
(int) $row->content_id, 0, $visited
);
if ($node !== null)
$nodes[] = $node;
}
}
}
$allIds = [];
$this->collectContentIds($nodes, $allIds);
$groupMap = [];
if (!empty($allIds))
{
$gResult = $this->ContentgruppeModel->getGruppenForContents($allIds);
if (!isError($gResult))
$groupMap = getData($gResult);
}
$this->applyMeta($nodes, $entitledOe, $groupMap);
$result = [];
foreach ($nodes as $node)
{
$pruned = $this->pruneUnentitled($node);
if ($pruned !== null)
$result[] = $pruned;
}
$this->terminateWithSuccess($result);
}
// One subtree in the shape of a getTree root node. Lets the client replace a branch
// instead of rebuilding the whole tree after a change.
// The visited map starts empty here, so a node with several parents can show up twice
// until the next full reload. Rebuilding the whole tree on every change costs more.
public function getSubtree()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$content_id = (int) $this->input->get('content_id', TRUE);
$entitledResult = $this->cmsadminlib->getEntitledOe();
$entitledOe = isError($entitledResult) ? [] : getData($entitledResult);
$visited = [];
$node = $this->buildTreeNode($content_id, 0, $visited);
// Deleted meanwhile. The client drops the branch.
if ($node === null)
$this->terminateWithSuccess(null);
$nodes = [$node];
$allIds = [];
$this->collectContentIds($nodes, $allIds);
$groupMap = [];
if (!empty($allIds))
{
$gResult = $this->ContentgruppeModel->getGruppenForContents($allIds);
if (!isError($gResult))
$groupMap = getData($gResult);
}
$this->applyMeta($nodes, $entitledOe, $groupMap);
$this->terminateWithSuccess($this->pruneUnentitled($node));
}
public function getContent()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$content_id = $this->input->get('content_id', TRUE);
$entitledResult = $this->cmsadminlib->isEntitledForContent($content_id);
$entitled = $this->getDataOrTerminateWithError($entitledResult);
if (!$entitled)
$this->terminateWithError($this->p->t('cms', 'keineBerechtigungFuerDiesenEintrag'));
$this->ContentModel->resetQuery();
$contentResult = $this->ContentModel->load($content_id);
$contentData = $this->getDataOrTerminateWithError($contentResult);
if (empty($contentData))
$this->terminateWithError($this->p->t('cms', 'contentNichtGefunden'));
$content = $contentData[0];
$langResult = $this->ContentspracheModel->getLanguages($content_id);
$languages = $this->getDataOrTerminateWithError($langResult);
$versions = new stdClass();
foreach ($languages as $sprache)
{
$vResult = $this->ContentspracheModel->getVersions($content_id, $sprache);
if (!isError($vResult) && getData($vResult))
{
$versions->$sprache = array_map(function ($v) {
return (int) $v->version;
}, getData($vResult));
}
}
$result = new stdClass();
$result->content_id = (int) $content->content_id;
$result->template_kurzbz = $content->template_kurzbz;
$result->oe_kurzbz = $content->oe_kurzbz;
$result->aktiv = ($content->aktiv === 't' || $content->aktiv === true);
$result->menu_open = ($content->menu_open === 't' || $content->menu_open === true);
$result->beschreibung = $content->beschreibung ?: '';
$result->entitled = true;
$result->languages = $languages;
$result->versions = $versions;
// Tells the CIS4 preview whether this template renders there at all.
$cis4Result = $this->TemplateModel->hasCis4Stylesheet($content->template_kurzbz);
$result->has_cis4_stylesheet = !isError($cis4Result) && getData($cis4Result);
$this->terminateWithSuccess($result);
}
public function getContentsprache()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
$this->form_validation->set_rules('sprache', 'Sprache', 'required');
$this->form_validation->set_rules('version', 'Version', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$content_id = $this->input->get('content_id', TRUE);
$sprache = $this->input->get('sprache', TRUE);
$version = $this->input->get('version', TRUE);
$result = $this->ContentspracheModel->getOne($content_id, $sprache, $version);
if (isError($result))
$this->terminateWithError($this->p->t('cms', 'versionNichtGefunden'));
$data = getData($result);
$data->sichtbar = ($data->sichtbar === 't' || $data->sichtbar === true);
$this->terminateWithSuccess($data);
}
// The tree lists a search hit out of its place, so a generic title carries no context.
// This answers where each one sits. One walk for the whole list, not one per node.
const MAX_ANCESTOR_TIEFE = 12;
/**
* Ancestors of the given contents, keyed by content_id and ordered from the top down.
*/
public function getAncestors()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('content_ids', 'Content IDs', 'required');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$ids = [];
foreach (explode(',', $this->input->get('content_ids', TRUE)) as $teil)
{
$teil = trim($teil);
if (is_numeric($teil))
$ids[] = (int) $teil;
}
$ids = array_values(array_unique($ids));
if (empty($ids))
$this->terminateWithSuccess(new stdClass());
$result = $this->ContentchildModel->getAncestors(
$ids, self::MAX_ANCESTOR_TIEFE, DEFAULT_LANGUAGE
);
$rows = $this->getDataOrTerminateWithError($result);
// Same rule as the tree: an entry of an organisational unit the editor is not
// entitled for stays readable but cannot be opened.
$entitledResult = $this->cmsadminlib->getEntitledOe();
$entitledOe = isError($entitledResult) ? [] : getData($entitledResult) ?? [];
// The query orders by the distance, farthest first, so appending keeps the path
// reading from the top down.
$pfade = [];
foreach ((array) $rows as $row)
{
$start = (int) $row->start_id;
if (!isset($pfade[$start]))
$pfade[$start] = [];
$pfade[$start][] = [
'content_id' => (int) $row->content_id,
'titel' => $row->titel,
'tiefe' => (int) $row->tiefe,
'entitled' => in_array($row->oe_kurzbz, $entitledOe)
];
}
$this->terminateWithSuccess($pfade ?: new stdClass());
}
/**
* Per content metadata for the tree filter, keyed by content_id.
* One query for the whole tree, so the client filters and sorts without a round trip.
*/
public function getTreeMeta()
{
$result = $this->ContentModel->getTreeMeta(getAuthUID(), DEFAULT_LANGUAGE);
$rows = $this->getDataOrTerminateWithError($result);
$meta = [];
foreach ((array) $rows as $row)
{
$meta[(int) $row->content_id] = [
'childcount' => (int) $row->childcount,
'insertamum' => $row->insertamum,
'updateamum' => $row->updateamum,
'mine' => ($row->mine === 't' || $row->mine === true),
'contentlength' => (int) $row->contentlength
];
}
$this->terminateWithSuccess($meta);
}
/**
* Two gates guard every click statistics request: the config flag switches the feature
* off for everybody, the admin right decides who may read the numbers.
*/
private function checkClickstatsAccess()
{
$this->config->load('cms');
if (!$this->config->item('clickstats_enabled'))
$this->terminateWithError($this->p->t('cms', 'klickstatistikDeaktiviert'));
if (!$this->permissionlib->isBerechtigt('admin'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigung'));
}
/**
* Period of a click statistics request. 0 counts the whole log.
* Call it after checkClickstatsAccess, which loads the config.
* @return array months and the start date, null for the whole log
*/
private function clickstatsPeriod()
{
$months = $this->input->get('months');
if ($months === null || $months === '')
$months = $this->config->item('clickstats_months');
$months = (int) $months;
$since = ($months > 0)
? date('Y-m-d H:i:s', strtotime('-' . $months . ' months'))
: null;
return [$months, $since];
}
/**
* Views of one content. One counting query, so the tab loads it right away.
* Empty if LOG_CONTENT is off.
*/
public function getClickCount()
{
$this->checkClickstatsAccess();
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
$this->form_validation->set_rules('months', 'Monate', 'is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$this->load->model('system/Webservicelog_model', 'WebservicelogModel');
list($months, $since) = $this->clickstatsPeriod();
$result = $this->WebservicelogModel->getContentClickCount(
(int) $this->input->get('content_id', TRUE), $since
);
$this->terminateWithSuccess([
'since' => $since,
'months' => $months,
'own' => $this->getDataOrTerminateWithError($result)
]);
}
/**
* The ranking of the most viewed contents. It groups the whole log and resolves a
* title per row, which takes seconds, so the tab fetches it only on request.
* Empty if LOG_CONTENT is off.
*/
public function getClickRanking()
{
$this->checkClickstatsAccess();
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('months', 'Monate', 'is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$this->load->model('system/Webservicelog_model', 'WebservicelogModel');
list($months, $since) = $this->clickstatsPeriod();
$sprache = $this->input->get('sprache', TRUE);
if (empty($sprache))
$sprache = DEFAULT_LANGUAGE;
$result = $this->WebservicelogModel->getContentClickCounts(
$since, $sprache, (int) $this->config->item('clickstats_limit')
);
$rows = $this->getDataOrTerminateWithError($result);
$ranked = [];
foreach ((array) $rows as $row)
{
$ranked[] = [
'content_id' => (int) $row->content_id,
'titel' => $row->titel,
'hits' => (int) $row->hits
];
}
$this->terminateWithSuccess([
'since' => $since,
'months' => $months,
'ranked' => $ranked
]);
}
public function getTemplates()
{
$this->TemplateModel->addSelect('template_kurzbz, bezeichnung');
$this->TemplateModel->addOrder('bezeichnung');
$result = $this->TemplateModel->loadWhere([]);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function getOrganisationseinheiten()
{
$this->load->model(
'organisation/Organisationseinheit_model', 'OrganisationseinheitModel'
);
$this->OrganisationseinheitModel->addSelect(
'oe_kurzbz, bezeichnung, organisationseinheittyp_kurzbz, aktiv'
);
$this->OrganisationseinheitModel->addOrder('bezeichnung');
$result = $this->OrganisationseinheitModel->loadWhere([]);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function getSprachen()
{
$this->load->model('system/Sprache_model', 'SpracheModel');
$this->SpracheModel->addSelect('sprache, bezeichnung');
$result = $this->SpracheModel->loadWhere(['content' => true]);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function getUsage()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$content_id = $this->input->get('content_id', TRUE);
$result = $this->ContentModel->getUsage($content_id);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function postContent()
{
if (!$this->permissionlib->isBerechtigt('basis/cms', 'i'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigung'));
$this->load->library('form_validation');
$this->form_validation->set_data($_POST);
$this->form_validation->set_rules('parent_content_id', 'Parent', 'is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$parent = $this->input->post('parent_content_id');
if (empty($parent))
$parent = null;
$result = $this->cmsadminlib->createContent($parent);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function postTranslation()
{
if (!$this->permissionlib->isBerechtigt('basis/cms', 'i'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigung'));
$this->load->library('form_validation');
$this->form_validation->set_data($_POST);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
$this->form_validation->set_rules('sprache', 'Sprache', 'required');
$this->form_validation->set_rules('version', 'Version', 'required|is_natural');
$this->form_validation->set_rules('target_sprache', 'Target Sprache', 'required');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$result = $this->cmsadminlib->createTranslation(
$this->input->post('content_id'),
$this->input->post('sprache'),
$this->input->post('version'),
$this->input->post('target_sprache')
);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function postVersion()
{
if (!$this->permissionlib->isBerechtigt('basis/cms', 'i'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigung'));
$this->load->library('form_validation');
$this->form_validation->set_data($_POST);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
$this->form_validation->set_rules('sprache', 'Sprache', 'required');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$result = $this->cmsadminlib->createVersion(
$this->input->post('content_id'),
$this->input->post('sprache')
);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function putProperties()
{
if (!$this->permissionlib->isBerechtigt('basis/cms', 'u'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigung'));
$this->load->library('form_validation');
$this->form_validation->set_data($_POST);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
$this->form_validation->set_rules('sprache', 'Sprache', 'required');
$this->form_validation->set_rules('version', 'Version', 'required|is_natural');
$this->form_validation->set_rules('template_kurzbz', 'Template', 'required');
$this->form_validation->set_rules('oe_kurzbz', 'OE', 'required');
$this->form_validation->set_rules('titel', 'Titel', 'required|max_length[256]');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$daten = [
'content_id' => $this->input->post('content_id'),
'sprache' => $this->input->post('sprache'),
'version' => $this->input->post('version'),
'template_kurzbz' => $this->input->post('template_kurzbz'),
'oe_kurzbz' => $this->input->post('oe_kurzbz'),
'titel' => $this->input->post('titel'),
'beschreibung' => $this->input->post('beschreibung') ?: '',
'aktiv' => filter_var($this->input->post('aktiv'), FILTER_VALIDATE_BOOLEAN),
'menu_open' => filter_var($this->input->post('menu_open'), FILTER_VALIDATE_BOOLEAN),
'sichtbar' => filter_var($this->input->post('sichtbar'), FILTER_VALIDATE_BOOLEAN)
];
$result = $this->cmsadminlib->saveProperties($daten);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function deleteContent()
{
if (!$this->permissionlib->isBerechtigt('basis/cms', 'd'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigung'));
$this->load->library('form_validation');
$this->form_validation->set_data($_POST);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$result = $this->cmsadminlib->deleteContent($this->input->post('content_id'));
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function deleteContentsprache()
{
if (!$this->permissionlib->isBerechtigt('basis/cms', 'd'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigung'));
$this->validateContentSpracheVersion();
$result = $this->cmsadminlib->deleteVersion(
$this->input->post('content_id'),
$this->input->post('sprache'),
$this->input->post('version')
);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
// --- Private helpers ---
private function validateContentSpracheVersion()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_POST);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
$this->form_validation->set_rules('sprache', 'Sprache', 'required');
$this->form_validation->set_rules('version', 'Version', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
private function buildTreeNode($content_id, $depth, &$visited)
{
if ($depth > self::MAX_TREE_DEPTH || isset($visited[$content_id]))
return null;
$visited[$content_id] = true;
$this->ContentModel->resetQuery();
$contentResult = $this->ContentModel->load($content_id);
if (isError($contentResult) || empty(getData($contentResult)))
return null;
$content = getData($contentResult)[0];
$titel = '';
$csResult = $this->ContentspracheModel->getOne($content_id, DEFAULT_LANGUAGE);
if (!isError($csResult))
$titel = getData($csResult)->titel;
$childrenResult = $this->ContentchildModel->getChilds($content_id, DEFAULT_LANGUAGE);
$children = [];
if (!isError($childrenResult) && getData($childrenResult))
{
foreach (getData($childrenResult) as $child)
{
$childNode = $this->buildTreeNode(
(int) $child->child_content_id, $depth + 1, $visited
);
if ($childNode !== null)
$children[] = $childNode;
}
}
$node = new stdClass();
$node->content_id = (int) $content_id;
$node->titel = $titel;
$node->template_kurzbz = $content->template_kurzbz;
$node->oe_kurzbz = $content->oe_kurzbz;
$node->aktiv = ($content->aktiv === 't' || $content->aktiv === true);
$node->entitled = false;
$node->groups = [];
$node->children = $children;
return $node;
}
private function collectContentIds($nodes, &$ids)
{
foreach ($nodes as $node)
{
$ids[] = $node->content_id;
if (!empty($node->children))
$this->collectContentIds($node->children, $ids);
}
}
private function applyMeta(&$nodes, $entitledOe, $groupMap)
{
foreach ($nodes as $node)
{
$node->entitled = in_array($node->oe_kurzbz, $entitledOe);
$node->groups = isset($groupMap[$node->content_id])
? $groupMap[$node->content_id]
: [];
if (!empty($node->children))
$this->applyMeta($node->children, $entitledOe, $groupMap);
}
}
private function pruneUnentitled($node)
{
$kept = [];
foreach ($node->children as $child)
{
$pruned = $this->pruneUnentitled($child);
if ($pruned !== null)
$kept[] = $pruned;
}
$node->children = $kept;
if ($node->entitled || !empty($node->children))
return $node;
return null;
}
/**
* Lists the DMS categories for the contentcomponent insert dialog.
*/
public function getDmsKategorien()
{
$this->load->model('content/Dms_model', 'DmsModel');
$result = $this->DmsModel->getKategorien();
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
/**
* Lists active staff for the contentcomponent insert dialog.
* Names only. See Mitarbeiter_model::getAktivesPersonalNamen().
*/
public function getMitarbeiter()
{
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$result = $this->MitarbeiterModel->getAktivesPersonalNamen();
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
/**
* Lists the documents of one DMS category for the contentcomponent insert dialog.
*/
public function getDmsKategorieDokumente()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('kategorie_kurzbz', 'DMS-Kategorie', 'required');
if ($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$this->load->model('content/Dms_model', 'DmsModel');
$result = $this->DmsModel->getKategorieDokumenteAlle($this->input->get('kategorie_kurzbz', TRUE));
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result) ?: array());
}
}
@@ -0,0 +1,195 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class CmsAdminInhalt extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'getFormSchema' => ['basis/cms:r'],
'getFormData' => ['basis/cms:r'],
'getLock' => ['basis/cms:r'],
'getVersions' => ['basis/cms:r'],
'putFormData' => ['basis/cms:rw'],
'postLock' => ['basis/cms:rw'],
'deleteLock' => ['basis/cms:rw'],
'deleteLockForced' => ['basis/cms:rw']
]);
$this->load->library('CmsAdminLib');
$this->load->library('XsdSchemaLib');
$this->load->library('PermissionLib');
$this->load->model('content/Contentsprache_model', 'ContentspracheModel');
$this->load->model('content/Template_model', 'TemplateModel');
$this->loadPhrases(['global', 'cms']);
}
public function getFormSchema()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('template_kurzbz', 'Template', 'required');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$template_kurzbz = $this->input->get('template_kurzbz', TRUE);
$this->TemplateModel->resetQuery();
$templateResult = $this->TemplateModel->load($template_kurzbz);
$templateData = $this->getDataOrTerminateWithError($templateResult);
if (empty($templateData))
$this->terminateWithError($this->p->t('cms', 'vorlageNichtGefunden'));
$schemaResult = $this->xsdschemalib->parseSchema(
$templateData[0]->xsd, $template_kurzbz
);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($schemaResult));
}
public function getFormData()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
$this->form_validation->set_rules('sprache', 'Sprache', 'required');
$this->form_validation->set_rules('version', 'Version', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$content_id = $this->input->get('content_id', TRUE);
$sprache = $this->input->get('sprache', TRUE);
$version = $this->input->get('version', TRUE);
$versionResult = $this->ContentspracheModel->getOne($content_id, $sprache, $version);
$row = $this->getDataOrTerminateWithError($versionResult);
$this->load->model('content/Content_model', 'ContentModel');
$this->ContentModel->resetQuery();
$contentResult = $this->ContentModel->load($content_id);
$contentData = $this->getDataOrTerminateWithError($contentResult);
if (empty($contentData))
$this->terminateWithError($this->p->t('cms', 'contentNichtGefunden'));
$this->TemplateModel->resetQuery();
$templateResult = $this->TemplateModel->load($contentData[0]->template_kurzbz);
$templateData = $this->getDataOrTerminateWithError($templateResult);
if (empty($templateData))
$this->terminateWithError($this->p->t('cms', 'vorlageNichtGefunden'));
$schemaResult = $this->xsdschemalib->parseSchema(
$templateData[0]->xsd, $contentData[0]->template_kurzbz
);
$schema = $this->getDataOrTerminateWithError($schemaResult);
$valuesResult = $this->xsdschemalib->extractValues($row->content, $schema);
$values = $this->getDataOrTerminateWithError($valuesResult);
$lockResult = $this->cmsadminlib->getLockState($content_id, $sprache, $version);
$lock = $this->getDataOrTerminateWithError($lockResult);
$this->terminateWithSuccess([
'schema' => $schema,
'values' => $values,
'sperre' => $lock
]);
}
public function getLock()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
$this->form_validation->set_rules('sprache', 'Sprache', 'required');
$this->form_validation->set_rules('version', 'Version', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$content_id = $this->input->get('content_id', TRUE);
$sprache = $this->input->get('sprache', TRUE);
$version = $this->input->get('version', TRUE);
$result = $this->cmsadminlib->getLockState($content_id, $sprache, $version);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function getVersions()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
$this->form_validation->set_rules('sprache', 'Sprache', 'required');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$content_id = $this->input->get('content_id', TRUE);
$sprache = $this->input->get('sprache', TRUE);
$result = $this->ContentspracheModel->getVersions($content_id, $sprache);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function putFormData()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_POST);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
$this->form_validation->set_rules('sprache', 'Sprache', 'required');
$this->form_validation->set_rules('version', 'Version', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$content_id = $this->input->post('content_id');
$sprache = $this->input->post('sprache');
$version = $this->input->post('version');
$values = $this->input->post('values');
if (is_string($values))
$values = json_decode($values, true);
if (!is_array($values))
$values = [];
$lockResult = $this->cmsadminlib->getLockState($content_id, $sprache, $version);
$lock = $this->getDataOrTerminateWithError($lockResult);
if (!$lock['own'])
$this->terminateWithError($this->p->t('cms', 'speichernNurMitSperre'));
$result = $this->cmsadminlib->saveContentXml($content_id, $sprache, $version, $values);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function postLock()
{
if (!$this->permissionlib->isBerechtigt('basis/cms', 'u'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigungFuerDieseAktion'));
$contentsprache_id = $this->input->post('contentsprache_id');
$result = $this->cmsadminlib->lock($contentsprache_id);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function deleteLock()
{
if (!$this->permissionlib->isBerechtigt('basis/cms', 'u'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigungFuerDieseAktion'));
$contentsprache_id = $this->input->post('contentsprache_id');
$result = $this->cmsadminlib->releaseOwnLock($contentsprache_id);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function deleteLockForced()
{
if (!$this->permissionlib->isBerechtigt('basis/cms_sperrfreigabe', 'su'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigungFuerDieseAktion'));
$contentsprache_id = $this->input->post('contentsprache_id');
$result = $this->cmsadminlib->forceRelease($contentsprache_id);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
}
@@ -0,0 +1,265 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class CmsAdminStruktur extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'getGruppen' => ['basis/cms:r'],
'getAllGruppen' => ['basis/cms:r'],
'getChilds' => ['basis/cms:r'],
'getParents' => ['basis/cms:r'],
'getPossibleChilds' => ['basis/cms:r'],
'postGruppe' => ['basis/cms:rw'],
'deleteGruppe' => ['basis/cms:rw'],
'postChild' => ['basis/cms:rw'],
'deleteChild' => ['basis/cms:rw'],
'putChildSort' => ['basis/cms:rw'],
'putChildOrder' => ['basis/cms:rw']
]);
$this->load->library('PermissionLib');
$this->load->model('content/Content_model', 'ContentModel');
$this->load->model('content/Contentchild_model', 'ContentchildModel');
$this->load->model('content/Contentgruppe_model', 'ContentgruppeModel');
$this->loadPhrases(['global', 'cms']);
}
public function getGruppen()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$content_id = $this->input->get('content_id', TRUE);
$result = $this->ContentgruppeModel->getGruppen($content_id);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function getAllGruppen()
{
$result = $this->ContentgruppeModel->getAllGruppen();
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function getChilds()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$content_id = $this->input->get('content_id', TRUE);
$sprache = $this->input->get('sprache', TRUE);
if (empty($sprache))
$sprache = DEFAULT_LANGUAGE;
$result = $this->ContentchildModel->getChilds($content_id, $sprache);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function getParents()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$content_id = $this->input->get('content_id', TRUE);
$sprache = $this->input->get('sprache', TRUE);
if (empty($sprache))
$sprache = DEFAULT_LANGUAGE;
$result = $this->ContentchildModel->getParents($content_id, $sprache);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function getPossibleChilds()
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$content_id = $this->input->get('content_id', TRUE);
$sprache = $this->input->get('sprache', TRUE);
if (empty($sprache))
$sprache = DEFAULT_LANGUAGE;
$result = $this->ContentModel->getPossibleChilds($content_id, $sprache);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function postGruppe()
{
if (!$this->permissionlib->isBerechtigt('basis/cms', 'u'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigung'));
$this->load->library('form_validation');
$this->form_validation->set_data($_POST);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
$this->form_validation->set_rules('gruppe_kurzbz', 'Gruppe', 'required');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$result = $this->ContentgruppeModel->insert([
'content_id' => $this->input->post('content_id'),
'gruppe_kurzbz' => $this->input->post('gruppe_kurzbz'),
'insertamum' => date('Y-m-d H:i:s'),
'insertvon' => getAuthUID()
]);
if (isError($result))
{
$this->terminateWithError($this->p->t('cms', 'gruppeBereitsZugeordnet'));
}
$this->terminateWithSuccess(true);
}
public function deleteGruppe()
{
if (!$this->permissionlib->isBerechtigt('basis/cms', 'u'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigung'));
$this->load->library('form_validation');
$this->form_validation->set_data($_POST);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
$this->form_validation->set_rules('gruppe_kurzbz', 'Gruppe', 'required');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$result = $this->ContentgruppeModel->delete([
$this->input->post('gruppe_kurzbz'),
$this->input->post('content_id')
]);
$this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(true);
}
public function postChild()
{
if (!$this->permissionlib->isBerechtigt('basis/cms', 'u'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigung'));
$this->load->library('form_validation');
$this->form_validation->set_data($_POST);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
$this->form_validation->set_rules('child_content_id', 'Child Content ID', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$content_id = $this->input->post('content_id');
$sortResult = $this->ContentchildModel->getMaxSort($content_id);
$sort = $this->getDataOrTerminateWithError($sortResult) + 1;
$result = $this->ContentchildModel->insert([
'content_id' => $content_id,
'child_content_id' => $this->input->post('child_content_id'),
'sort' => $sort,
'insertamum' => date('Y-m-d H:i:s'),
'insertvon' => getAuthUID()
]);
$this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(true);
}
public function deleteChild()
{
if (!$this->permissionlib->isBerechtigt('basis/cms', 'u'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigung'));
$this->load->library('form_validation');
$this->form_validation->set_data($_POST);
$this->form_validation->set_rules('contentchild_id', 'Contentchild ID', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$result = $this->ContentchildModel->delete($this->input->post('contentchild_id'));
$this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(true);
}
public function putChildSort()
{
if (!$this->permissionlib->isBerechtigt('basis/cms', 'u'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigung'));
$this->load->library('form_validation');
$this->form_validation->set_data($_POST);
$this->form_validation->set_rules('contentchild_id', 'Contentchild ID', 'required|is_natural');
$this->form_validation->set_rules('direction', 'Direction', 'required|in_list[up,down]');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$direction = $this->input->post('direction');
$result = $this->ContentchildModel->swapSort(
$this->input->post('contentchild_id'),
$direction
);
if (isError($result))
{
// The child already sits at the end it was moved towards. That is a normal
// answer, not a failure, and the direction decides the wording.
if (getError($result) === Contentchild_model::NO_NEIGHBOUR)
{
$this->terminateWithError($this->p->t('cms',
$direction === 'up' ? 'bereitsGanzOben' : 'bereitsGanzUnten'));
}
$this->terminateWithError($this->p->t('cms', 'sortierungFehlgeschlagen'));
}
$this->terminateWithSuccess(true);
}
// Writes the whole order at once. The drag and drop moves a child over any distance,
// and putChildSort would need one request per position.
public function putChildOrder()
{
if (!$this->permissionlib->isBerechtigt('basis/cms', 'u'))
$this->terminateWithError($this->p->t('cms', 'keineBerechtigung'));
$this->load->library('form_validation');
$this->form_validation->set_data($_POST);
$this->form_validation->set_rules('content_id', 'Content ID', 'required|is_natural');
if ($this->form_validation->run() == FALSE)
$this->terminateWithValidationErrors($this->form_validation->error_array());
$ids = $this->input->post('contentchild_ids');
if (is_string($ids))
$ids = json_decode($ids, true);
if (!is_array($ids) || empty($ids))
$this->terminateWithError($this->p->t('cms', 'sortierungFehlgeschlagen'));
$result = $this->ContentchildModel->setSortOrder(
$this->input->post('content_id'),
$ids
);
if (isError($result))
$this->terminateWithError($this->p->t('cms', 'sortierungFehlgeschlagen'));
$this->terminateWithSuccess(true);
}
}
@@ -18,9 +18,18 @@
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
*/
@@ -31,56 +40,38 @@ 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);
$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);
}
// return early if the required parameter is missing
if(!isset($lehreinheit_id))
{
$this->terminateWithError('Missing required parameter', self::ERROR_TYPE_GENERAL);
}
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$studentenMails = $this->LehreinheitModel->getStudentenMail($lehreinheit_id);
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$studentenMails = $this->LehreinheitModel->getStudentenMail($lehreinheit_id);
$studentenMails = $this->getDataOrTerminateWithError($studentenMails);
$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)
@@ -90,13 +81,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
*/
@@ -154,46 +145,5 @@ 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));
}
}
@@ -28,10 +28,7 @@ class Lehrveranstaltung extends FHCAPI_Controller
{
parent::__construct([
'getByEmp' => ['admin:r', 'assistenz:r'],
'getByLe' => ['admin:r', 'assistenz:r'],
'getByLv' => ['admin:r', 'assistenz:r'],
'getByStg' => ['admin:r', 'assistenz:r'],
'getByStgOrgform' => ['admin:r', 'assistenz:r'],
'loadByLV' => ['admin:r', 'assistenz:r'],
]);
@@ -80,117 +77,41 @@ class Lehrveranstaltung extends FHCAPI_Controller
$this->terminateWithSuccess($tree);
}
public function getByLe($studiensemester_kurzbz = null, $lehreinheit_id = null)
{
if (is_null($lehreinheit_id))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz);
$lehreinheiten = $this->_ci->LehreinheitModel->getByLeStudiensemester($lehreinheit_id, $studiensemester_kurzbz);
$lehreinheiten_data = $this->getDataOrTerminateWithError($lehreinheiten);
$this->terminateWithSuccess($lehreinheiten_data);
}
public function getByLv($studiensemester_kurzbz = null, $lehrveranstaltung_id = null)
{
if (is_null($lehrveranstaltung_id))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz);
$tree = [];
$this->_ci->LehrveranstaltungModel->addSelect('studiengang_kz');
$lv = $this->_ci->LehrveranstaltungModel->load($lehrveranstaltung_id);
$lv = $this->getDataOrTerminateWithError($lv);
if (empty($lv))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$lehrveranstaltungen_data = array();
foreach ($this->_loadLvRows(current($lv)->studiengang_kz, $studiensemester_kurzbz) as $row)
{
if ((string)$row->lehrveranstaltung_id === (string)$lehrveranstaltung_id)
{
$lehrveranstaltungen_data[] = $row;
break;
}
}
if (empty($lehrveranstaltungen_data))
{
$this->_ci->LehrveranstaltungModel->addSelect('
tbl_lehrveranstaltung.*,
tbl_lehrveranstaltung.kurzbz as lv_kurzbz,
lehrveranstaltung_id,
lehrtyp_kurzbz,
studiengang_kz as lv_studiengang_kz,
UPPER(CONCAT(tbl_studiengang.typ, tbl_studiengang.kurzbz)) as studiengang,
semester as semester,
tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung,
ects as lv_ects,
lehreverzeichnis as lv_lehreverzeichnis,
planfaktor as lv_planfaktor,
planlektoren as lv_planlektoren,
planpersonalkosten as lv_planpersonalkosten,
plankostenprolektor as lv_plankostenprolektor,
tbl_lehrveranstaltung.orgform_kurzbz as orgform_kurzbz,
tbl_lehrveranstaltung.sprache as sprache,
lehrform_kurzbz as lv_lehrform_kurzbz,
tbl_lehrveranstaltung.bezeichnung_english as lv_bezeichnung_english,
semesterstunden as lv_semesterstunden,
');
$this->_ci->LehrveranstaltungModel->addJoin('public.tbl_studiengang', 'studiengang_kz');
$lehrveranstaltungen = $this->_ci->LehrveranstaltungModel->load($lehrveranstaltung_id);
$lehrveranstaltungen_data = $this->getDataOrTerminateWithError($lehrveranstaltungen);
}
foreach ($lehrveranstaltungen_data as $lehrveranstaltung)
{
$lehreinheiten = $this->_ci->LehreinheitModel->getByLvidStudiensemester($lehrveranstaltung->lehrveranstaltung_id, $studiensemester_kurzbz);
$lehreinheiten_data = $this->getDataOrTerminateWithError($lehreinheiten);
if (!isset($lehrveranstaltung->_children))
{
$lehrveranstaltung->_children = $lehreinheiten_data;
}
$tree[] = $lehrveranstaltung;
}
$counter = 0;
$this->assignUniqueIndex($tree, $counter);
$this->terminateWithSuccess($tree);
}
public function getByStg($studiensemester_kurzbz = null, $stg_kz = null, $semester = null)
{
$this->_getLvTree($studiensemester_kurzbz, $stg_kz, $semester, null);
}
public function getByStgOrgform($studiensemester_kurzbz = null, $stg_kz = null, $orgform = null, $semester = null)
{
$orgform_kurzbz = $this->getOrgformKurzbz($orgform);
$this->_getLvTree($studiensemester_kurzbz, $stg_kz, $semester, $orgform_kurzbz);
}
private function _getLvTree($studiensemester_kurzbz, $stg_kz, $semester, $orgform_kurzbz)
{
$studiengang_kz = $this->getStudiengangKz($stg_kz);
if (is_null($studiengang_kz) || !preg_match("/^(-?[1-9][0-9]*|0)$/", (string)$studiengang_kz))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
if (!is_null($semester) && !ctype_digit((string)$semester))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$verband = null;
if (!is_null($semester) && !is_numeric($semester))
{
$verband = $this->getOrgformKurzbz($semester);
$semester = null;
}
$this->_ci->load->model('organisation/Studienplan_model', 'StudienplanModel');
$studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz);
$studienplan_data = $this->_ci->StudienplanModel->getStudienplaeneBySemester($studiengang_kz, $studiensemester_kurzbz, $semester, $verband);
$lehrveranstaltungen_data = $this->_loadLvRows($studiengang_kz, $studiensemester_kurzbz, $semester, $orgform_kurzbz);
$studienplan_ids = array();
$only_ids = array();
$placeholders = array();
if (hasData($studienplan_data))
{
foreach (getData($studienplan_data) as $studienplan) {
$placeholders[] = "(?, ?)";
$studienplan_ids[] = $studienplan->studienplan_id;
$studienplan_ids[] = $studienplan->semester;
$only_ids[] = $studienplan->studienplan_id;
}
}
$lehrveranstaltungen_data = $this->_ci->LehrveranstaltungModel->getLvsByStudiengang($studienplan_ids, $placeholders, $only_ids, $studiengang_kz, $studiensemester_kurzbz, $semester, $verband);
$lehrveranstaltungen_data = hasData($lehrveranstaltungen_data) ? getData($lehrveranstaltungen_data) : array();
$tree = [];
foreach ($lehrveranstaltungen_data as $row)
@@ -323,31 +244,6 @@ class Lehrveranstaltung extends FHCAPI_Controller
$this->LehreinheitModel->getLvsByFachbereich($fachbereich, $studiensemester_kurzbz, $mitarbeiter_uid);
}*/
private function _loadLvRows($studiengang_kz, $studiensemester_kurzbz, $semester = null, $orgform_kurzbz = null)
{
$this->_ci->load->model('organisation/Studienplan_model', 'StudienplanModel');
$studienplan_data = $this->_ci->StudienplanModel->getStudienplaeneBySemester($studiengang_kz, $studiensemester_kurzbz, $semester, $orgform_kurzbz);
$studienplan_ids = array();
$only_ids = array();
$placeholders = array();
if (hasData($studienplan_data))
{
foreach (getData($studienplan_data) as $studienplan) {
$placeholders[] = "(?, ?)";
$studienplan_ids[] = $studienplan->studienplan_id;
$studienplan_ids[] = $studienplan->semester;
$only_ids[] = $studienplan->studienplan_id;
}
}
$lehrveranstaltungen_data = $this->_ci->LehrveranstaltungModel->getLvsByStudiengang($studienplan_ids, $placeholders, $only_ids, $studiengang_kz, $studiensemester_kurzbz, $semester, $orgform_kurzbz);
return hasData($lehrveranstaltungen_data) ? getData($lehrveranstaltungen_data) : array();
}
private function _setAuthUID()
{
$this->_uid = getAuthUID();
@@ -413,8 +309,8 @@ class Lehrveranstaltung extends FHCAPI_Controller
$data = $this->getDataOrTerminateWithError($result);
if (!$data)
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
return $orgform_kurzbz;
return current($data)->orgform_kurzbz;
}
}
}
@@ -36,8 +36,7 @@ class LvMenu extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getLvMenu' => self::PERM_LOGGED,
'getMultipleLvMenu' => self::PERM_LOGGED
'getLvMenu' => self::PERM_LOGGED
]);
$this->load->model("ressource/Mitarbeiter_model");
@@ -62,23 +61,24 @@ 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 = $this->input->post('lvMenuOptionList', true);
public function getMultipleLvMenu($lvMenuOptionList){
$result =[];
foreach($lvMenuOptionList as $lvMenuOptions){
$lvMenu = $this->getLvMenuInternal($lvMenuOptions['lvid'],$lvMenuOptions['studiensemester_kurzbz']);
$lvMenu = $this->getLvMenu($lvMenuOptions['lvid'],$lvMenuOptions['studiensemester_kurzbz']);
if(isError($lvMenu)){
// TODO: some lvMenu threw an error, handle error here
}
$result[$lvMenuOptions['lvid']]=$lvMenu;
}
$this->terminateWithSuccess($result);
}
private function getLvMenuInternal($lvid, $studiensemester_kurzbz) {
/**
*
*/
public function getLvMenu($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,18 +299,11 @@ class LvMenu extends FHCAPI_Controller
array_multisort($pos, SORT_ASC, SORT_NUMERIC, $menu);
return $menu;
}
/**
*
*/
public function getLvMenu($lvid, $studiensemester_kurzbz)
{
$menu = $this->getLvMenuInternal($lvid, $studiensemester_kurzbz);
// HTTP response
// ##########################################################################################
$this->terminateWithSuccess($menu);
}
private function fhc_menu_lvinfo(&$menu, $lvid, $studiengang_kz, $lektor_der_lv, $is_lector, $lehrfach_oe_kurzbz_arr){
@@ -379,9 +372,7 @@ class LvMenu extends FHCAPI_Controller
'name'=>$this->p->t('lehre', 'gesamtnote'),
'phrase' => 'lehre/gesamtnote',
'c4_icon'=> base_url('skin/images/button_endnote.png'),
'c4_link'=> base_url('cis.php/Cis/Compat/legacy/cis/private/lehre/benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem))
//'c4_link'=> site_url('Cis/Benotungstool/'.urlencode($lvid).'/'.urlencode($angezeigtes_stsem))
//'c4_link'=> base_url('cis/private/lehre/benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem))
'c4_link'=> base_url('cis/private/lehre/benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem))
//'c4_link'=> base_url('benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem))
);
}
@@ -550,4 +541,4 @@ class LvMenu extends FHCAPI_Controller
);
}
}
}
}
@@ -1,167 +0,0 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class MaZeitsperren extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'getAllActiveZeitsperren' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'],
'getAllZeitsperrenFixeMa' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'],
'getAllZeitsperrenLector' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'],
'getAllZeitsperrenOes' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'],
'getZeitsperrenAss' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'],
'getStgLectors' => self::PERM_LOGGED,
'loadZeitsperrenLectorStg' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'],
'loadZeitsperrenMa' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'],
'getDetailsMa' => 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');
}
//only user with zeitsperren
public function getAllActiveZeitsperren($days)
{
$result = $this->ZeitsperreModel->getZeitsperrenForNextDays($days);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
//TODO: check query: more results after join with hr-table
//all fixe Ma
public function getAllZeitsperrenFixeMa($days)
{
$von = date('Y-m-d');
$bis = date('Y-m-d', strtotime("+{$days} days"));
$result = $this->ZeitsperreModel->getMitarbeiterWithZeitsperren($von, $bis, true, false, null, false);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function getAllZeitsperrenLector($days)
{
$von = date('Y-m-d');
$bis = date('Y-m-d', strtotime("+{$days} days"));
$result = $this->ZeitsperreModel->getMitarbeiterWithZeitsperren($von, $bis, false, true, null, false);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function getZeitsperrenAss($days)
{
$von = date('Y-m-d');
$bis = date('Y-m-d', strtotime("+{$days} days"));
$result = $this->ZeitsperreModel->getMitarbeiterWithZeitsperren($von, $bis, false, false, null, true);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function getAllZeitsperrenOes($days, $oe)
{
if(!$oe)
{
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'OE Kurzbezeichnung']), self::ERROR_TYPE_GENERAL);
}
$von = date('Y-m-d');
$bis = date('Y-m-d', strtotime("+{$days} days"));
$result = $this->ZeitsperreModel->getMitarbeiterWithZeitsperren($von, $bis, false, false, $oe, false);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function loadZeitsperrenLectorStg($days, $stg)
{
if(!$stg)
{
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Studiengangskennzahl']), self::ERROR_TYPE_GENERAL);
}
if(!is_numeric($stg))
{
return $this->terminateWithError($this->p->t('ui', 'error_paramNoNumber', ['param'=> 'Studiengangskennzahl']), self::ERROR_TYPE_GENERAL);
}
$von = date('Y-m-d');
$bis = date('Y-m-d', strtotime("+{$days} days"));
$result = $this->ZeitsperreModel->getMitarbeiterWithZeitsperren($von, $bis, false, true, false, false, $stg);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function getStgLectors()
{
$sql = "
select
studiengang_kz,
kurzbz,
kurzbzlang,
typ,
bezeichnung,
kurzbzlang || ' (' || bezeichnung || ')' AS label,
aktiv
from
public.tbl_studiengang
where
typ in ('b','m')
order by typ asc, kurzbz Asc
";
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$result = $this->StudiengangModel->execReadOnlyQuery($sql);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function loadZeitsperrenMa($days, $uid)
{
if ($uid === null || $uid === '')
{
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Mitarbeiter UID']), self::ERROR_TYPE_GENERAL);
}
$result = $this->ZeitsperreModel->getZeitsperrenForNextDays($days, $uid);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function getDetailsMa($uid)
{
if ($uid === null || $uid === '')
{
$this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Mitarbeiter UID']), self::ERROR_TYPE_GENERAL);
}
$this->load->model('person/Person_model', 'PersonModel');
$result = $this->PersonModel->getFullName($uid);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
}
@@ -1,948 +0,0 @@
<?php
/**
* Copyright (C) 2026 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (!defined('BASEPATH')) exit('No direct script access allowed');
class NewsAdministrationAPI extends FHCAPI_Controller
{
/**
* News API constructor.
*/
public function __construct()
{
parent::__construct([
'getNews' => 'basis/news:r',
'getNewsItem' => 'basis/news:r',
'storeNewsItem' => 'basis/news:w',
'updateNewsItem' => 'basis/news:w',
'deleteNewsItem' => 'basis/news:w'
]);
$this->load->model('content/Content_model', 'ContentModel');
$this->load->model('content/Contentsprache_model', 'ContentspracheModel');
$this->load->model('content/News_model', 'NewsModel');
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$this->load->model('system/Sprache_model', 'SpracheModel');
$this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
$this->loadPhrases([
'global'
]);
$this->load->library('CmsLib');
$this->load->helper('hlp_sancho_helper');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $titel = '', $active = true, $sichtbar = true)
{
$this->load->library('form_validation');
$this->form_validation->set_data($_GET);
$this->form_validation->set_rules('page','Page','required|is_natural');
$this->form_validation->set_rules('page_size', 'PageSize', 'is_natural');
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$page = intval($this->input->get('page', true));
$page_size = intval($this->input->get('page_size', true));
$sprache = $this->input->get('sprache', true);
if(!$sprache)
{
$sprache = getUserLanguage();
}
// default value for the page_size is 10
$page_size = $page_size ?? 10;
$passedSichtbar = $this->input->get('published', true);
if($passedSichtbar !== null)
{
$sichtbar = $passedSichtbar;
}
$passedIsActive = $this->input->get('isActive', true);
if ($passedIsActive !== null)
{
$isActive = filter_var(
$passedIsActive,
FILTER_VALIDATE_BOOLEAN,
FILTER_NULL_ON_FAILURE
);
if ($isActive !== null)
{
$active = $isActive;
}
}
$degreeProgramShortCode = $this->input->get('degreeProgramShortCode', true);
if ($degreeProgramShortCode !== null && $degreeProgramShortCode !== '')
{
if (!ctype_digit((string)$degreeProgramShortCode))
{
$this->terminateWithValidationErrors([
'degreeProgramShortCode' => 'A valid degree program is required'
]);
}
$studiengang_kz = $degreeProgramShortCode;
}
$passedSemester = $this->input->get('semester', true);
if ($passedSemester !== null && $passedSemester !== '')
{
if (
!ctype_digit((string)$passedSemester)
|| (int)$passedSemester < 1
|| (int)$passedSemester > 8
)
{
$this->terminateWithValidationErrors([
'semester' => 'A valid semester is required'
]);
}
$semester = filter_var($passedSemester, FILTER_VALIDATE_INT);
}
$entitledDegreePrograms = $this->permissionlib->getSTG_isEntitledFor("basis/news");
$news = $this->cmslib->getNews($infoscreen, $studiengang_kz, $semester, false, $titel, true, $sichtbar, $page, $page_size, $sprache, true, $entitledDegreePrograms, $active);
$news = $this->getDataOrTerminateWithError($news);
$this->addMeta('row_count', $news["row_count"] ?? 0);
$this->terminateWithSuccess($news["content"]);
}
/**
* Returns one news entry with all available language versions for editing.
*
* @return void
*/
public function getNewsItem($newsId)
{
if (!is_numeric($newsId) || (int)$newsId < 1)
{
$this->terminateWithValidationErrors([
'newsId' => 'A valid news ID is required'
]);
}
$newsId = (int)$newsId;
$newsResult = $this->NewsModel->load($newsId);
if (isError($newsResult))
{
$this->terminateWithError(getError($newsResult), self::ERROR_TYPE_DB);
}
if (!hasData($newsResult))
{
$this->terminateWithValidationErrors([
'newsId' => 'The requested news entry does not exist'
]);
}
$news = current(getData($newsResult));
$entitledDegreePrograms = $this->permissionlib->getSTG_isEntitledFor("basis/news");
if (!in_array($news->studiengang_kz, $entitledDegreePrograms)) {
$this->terminateWithError('You are not entitled to access this news entry', self::ERROR_TYPE_PERMISSION);
}
$translationsResult = $this->ContentspracheModel->loadWhere([
'content_id' => $news->content_id
]);
if (isError($translationsResult))
{
$this->terminateWithError(getError($translationsResult), self::ERROR_TYPE_DB);
}
$languages = [];
foreach ((array)getData($translationsResult) as $translation)
{
$languages[$translation->sprache] = true;
}
$translations = [];
foreach (array_keys($languages) as $language)
{
$contentResult = $this->ContentModel->getContent(
$news->content_id,
$language,
null,
null,
false
);
if (isError($contentResult))
{
$this->terminateWithError(getError($contentResult), self::ERROR_TYPE_DB);
}
$content = getData($contentResult);
$document = new DOMDocument('1.0', 'UTF-8');
if ($content->content !== '' && $document->loadXML($content->content) === false)
{
$this->terminateWithError(
'The stored news content is invalid',
self::ERROR_TYPE_GENERAL
);
}
$author = $document->getElementsByTagName('verfasser')->item(0);
$title = $document->getElementsByTagName('betreff')->item(0);
$text = $document->getElementsByTagName('text')->item(0);
$translations[] = [
'language' => $language,
'author' => $author ? $author->nodeValue : '',
'title' => $title ? $title->nodeValue : '',
'text' => $text ? str_replace('dms.php', APP_ROOT . 'cms/dms.php', $text->nodeValue) : '',
'isPublished' => (bool)$content->sichtbar
];
}
$this->terminateWithSuccess([
'newsId' => (int)$news->news_id,
'visibleFrom' => $news->datum,
'visibleTo' => $news->datum_bis,
'degreeProgramShortCode' => $news->studiengang_kz,
'semester' => $news->semester,
'translations' => $translations
]);
}
/**
* Creates a news entry and all supplied translations.
*
* @return void
*/
public function storeNewsItem()
{
$this->load->library('form_validation');
$postData = $this->input->post(null, true);
$this->form_validation->set_data(is_array($postData) ? $postData : []);
$this->form_validation->set_rules('visibleFrom', 'Visible from', 'required|is_valid_date');
$this->form_validation->set_rules('visibleTo', 'Visible to', 'is_valid_date');
$this->form_validation->set_rules(
'degreeProgramShortCode',
'Degree program',
'required|is_natural'
);
$this->form_validation->set_rules(
'semester',
'Semester',
'is_natural_no_zero|less_than_equal_to[8]'
);
$errors = $this->form_validation->run() === false
? $this->form_validation->error_array()
: [];
$visibleFrom = $this->input->post('visibleFrom', true);
$visibleTo = $this->input->post('visibleTo', true);
$translations = $this->input->post('translations', true);
if (
is_string($visibleFrom)
&& is_string($visibleTo)
&& $visibleTo !== ''
&& is_valid_date($visibleFrom)
&& is_valid_date($visibleTo)
&& new DateTime($visibleTo) < new DateTime($visibleFrom)
)
{
$errors['visibleTo'] = 'Visible to must not be before Visible from';
}
if (!is_array($translations) || count($translations) === 0)
{
$errors['translations'] = 'At least one translation is required';
}
else
{
foreach ($translations as $index => $translation)
{
if (!is_array($translation))
{
$errors['translations.' . $index] = 'Each translation must be an object';
}
}
}
if (count($errors) > 0)
{
$this->terminateWithValidationErrors($errors);
}
$this->_ensureUserIsEntitledForDegreeProgram($this->input->post('degreeProgramShortCode', true));
$degreeProgramShortCode = $this->input->post('degreeProgramShortCode', true);
$semester = $this->input->post('semester', true);
$semester = $semester === '' ? null : $semester;
$visibleTo = $visibleTo === '' ? null : $visibleTo;
$studiengang = $this->_getStudiengang($degreeProgramShortCode);
$data = [
'studiengang_kz' => $degreeProgramShortCode,
'semester' => $semester,
'datum' => $visibleFrom,
'datum_bis' => $visibleTo,
'translations' => []
];
foreach ($translations as $index => $translation)
{
$data['translations'][$index] = [
'sprache' => $translation['language'] ?? '',
'verfasser' => $translation['author'] ?? '',
'betreff' => $translation['title'] ?? '',
'text' => $translation['text'] ?? '',
'sichtbar' => isset($translation['isPublished']) ? (bool)$translation['isPublished'] : false
];
}
$this->_validateLanguages($data['translations']);
foreach ($data['translations'] as $index => $translation)
{
$data['translations'][$index]['content'] = $this->_buildNewsXml($translation);
}
$uid = getAuthUID();
$now = date('Y-m-d H:i:s');
if ($this->db->trans_begin() === false)
{
$this->terminateWithError($this->db->error(), self::ERROR_TYPE_DB);
}
$contentResult = $this->ContentModel->insert([
'template_kurzbz' => 'news',
'oe_kurzbz' => $studiengang->oe_kurzbz,
'aktiv' => true,
'menu_open' => false,
'insertamum' => $now,
'insertvon' => $uid
]);
$contentId = (int)$this->_getInsertedIdOrRollback($contentResult);
$newsResult = $this->NewsModel->insert([
'uid' => $uid,
'studiengang_kz' => $data['studiengang_kz'],
'semester' => $data['semester'],
'datum' => $data['datum'],
'datum_bis' => $data['datum_bis'],
'content_id' => $contentId,
'insertamum' => $now,
'insertvon' => $uid,
'updateamum' => $now,
'updatevon' => $uid
]);
$newsId = (int)$this->_getInsertedIdOrRollback($newsResult);
foreach ($data['translations'] as $translation)
{
$translation['content'] = $this->formatContentURLsToRelative($translation['content']);
$translationResult = $this->ContentspracheModel->insert([
'sprache' => $translation['sprache'],
'content_id' => $contentId,
'version' => 1,
'sichtbar' => $translation['sichtbar'],
'content' => $translation['content'],
'titel' => $translation['betreff'],
'insertamum' => $now,
'insertvon' => $uid,
'updateamum' => $now,
'updatevon' => $uid
]);
$this->_getInsertedIdOrRollback($translationResult);
}
$newsItemResult = $this->NewsModel->load($newsId);
$newsItem = $this->getDataOrTerminateWithError($newsItemResult);
$newsItem = $newsItem[0];
$this->_sendNewsItemTranslationRequiredMail(
$newsItem
);
if ($this->db->trans_status() === false)
{
$error = $this->db->error();
$this->db->trans_rollback();
$this->terminateWithError($error, self::ERROR_TYPE_DB);
}
if ($this->db->trans_commit() === false)
{
$error = $this->db->error();
$this->db->trans_rollback();
$this->terminateWithError($error, self::ERROR_TYPE_DB);
}
$this->terminateWithSuccess($newsId);
}
/**
* Updates a news entry and its supplied translations.
*
* @return void
*/
public function updateNewsItem($newsId)
{
if (!is_numeric($newsId) || (int)$newsId < 1)
{
$this->terminateWithValidationErrors([
'newsId' => 'A valid news ID is required'
]);
}
$newsId = (int)$newsId;
$existingNewsResult = $this->NewsModel->load($newsId);
if (isError($existingNewsResult))
{
$this->terminateWithError(getError($existingNewsResult), self::ERROR_TYPE_DB);
}
if (!hasData($existingNewsResult))
{
$this->terminateWithValidationErrors([
'newsId' => 'The requested news entry does not exist'
]);
}
$existingNews = current(getData($existingNewsResult));
$this->_ensureUserIsEntitledForDegreeProgram($existingNews->studiengang_kz);
$this->load->library('form_validation');
$postData = $this->input->post(null, true);
$this->form_validation->set_data(is_array($postData) ? $postData : []);
$this->form_validation->set_rules('visibleFrom', 'Visible from', 'required|is_valid_date');
$this->form_validation->set_rules('visibleTo', 'Visible to', 'is_valid_date');
$this->form_validation->set_rules(
'degreeProgramShortCode',
'Degree program',
'required|is_natural'
);
$this->form_validation->set_rules(
'semester',
'Semester',
'is_natural_no_zero|less_than_equal_to[8]'
);
$errors = $this->form_validation->run() === false
? $this->form_validation->error_array()
: [];
$visibleFrom = $this->input->post('visibleFrom', true);
$visibleTo = $this->input->post('visibleTo', true);
$translations = $this->input->post('translations', true);
if (
is_string($visibleFrom)
&& is_string($visibleTo)
&& $visibleTo !== ''
&& is_valid_date($visibleFrom)
&& is_valid_date($visibleTo)
&& new DateTime($visibleTo) < new DateTime($visibleFrom)
)
{
$errors['visibleTo'] = 'Visible to must not be before Visible from';
}
if (!is_array($translations) || count($translations) === 0)
{
$errors['translations'] = 'At least one translation is required';
}
else
{
foreach ($translations as $index => $translation)
{
if (!is_array($translation))
{
$errors['translations.' . $index] = 'Each translation must be an object';
}
}
}
if (count($errors) > 0)
{
$this->terminateWithValidationErrors($errors);
}
$degreeProgramShortCode = $this->input->post('degreeProgramShortCode', true);
$semester = $this->input->post('semester', true);
$semester = $semester === '' ? null : $semester;
$visibleTo = $visibleTo === '' ? null : $visibleTo;
$studiengang = $this->_getStudiengang($degreeProgramShortCode);
$data = [
'studiengang_kz' => $degreeProgramShortCode,
'semester' => $semester,
'datum' => $visibleFrom,
'datum_bis' => $visibleTo,
'translations' => []
];
foreach ($translations as $index => $translation)
{
$data['translations'][$index] = [
'sprache' => $translation['language'] ?? '',
'verfasser' => $translation['author'] ?? '',
'betreff' => $translation['title'] ?? '',
'text' => $translation['text'] ?? '',
'sichtbar' => isset($translation['isPublished']) ? (bool)$translation['isPublished'] : false
];
}
$this->_validateLanguages($data['translations']);
foreach ($data['translations'] as $index => $translation)
{
$data['translations'][$index]['content'] = $this->_buildNewsXml($translation);
}
$uid = getAuthUID();
$now = date('Y-m-d H:i:s');
if ($this->db->trans_begin() === false)
{
$this->terminateWithError($this->db->error(), self::ERROR_TYPE_DB);
}
$contentResult = $this->ContentModel->update($existingNews->content_id, [
'oe_kurzbz' => $studiengang->oe_kurzbz,
'updateamum' => $now,
'updatevon' => $uid
]);
if (isError($contentResult))
{
$this->db->trans_rollback();
$this->terminateWithError(getError($contentResult), self::ERROR_TYPE_DB);
}
$newsResult = $this->NewsModel->update($newsId, [
'studiengang_kz' => $data['studiengang_kz'],
'semester' => $data['semester'],
'datum' => $data['datum'],
'datum_bis' => $data['datum_bis'],
'updateamum' => $now,
'updatevon' => $uid
]);
if (isError($newsResult))
{
$this->db->trans_rollback();
$this->terminateWithError(getError($newsResult), self::ERROR_TYPE_DB);
}
$existingTranslationsResult = $this->ContentspracheModel->loadWhere([
'content_id' => $existingNews->content_id
]);
if (isError($existingTranslationsResult))
{
$this->db->trans_rollback();
$this->terminateWithError(getError($existingTranslationsResult), self::ERROR_TYPE_DB);
}
$existingTranslations = [];
foreach ((array)getData($existingTranslationsResult) as $existingTranslation)
{
$language = $existingTranslation->sprache;
if (
!isset($existingTranslations[$language])
|| (int)$existingTranslation->version > (int)$existingTranslations[$language]->version
)
{
$existingTranslations[$language] = $existingTranslation;
}
}
$submittedLanguages = array_column($data['translations'], 'sprache');
$this->db->where('content_id', $existingNews->content_id);
$this->db->where_not_in('sprache', $submittedLanguages);
if ($this->db->delete('campus.tbl_contentsprache') === false)
{
$error = $this->db->error();
$this->db->trans_rollback();
$this->terminateWithError($error, self::ERROR_TYPE_DB);
}
foreach ($data['translations'] as $translation)
{
$translation['content'] = $this->formatContentURLsToRelative($translation['content']);
$translationData = [
'sichtbar' => $translation['sichtbar'],
'content' => $translation['content'],
'titel' => $translation['betreff'],
'updateamum' => $now,
'updatevon' => $uid
];
$existingTranslation = $existingTranslations[$translation['sprache']] ?? null;
if ($existingTranslation)
{
$translationResult = $this->ContentspracheModel->update(
$existingTranslation->contentsprache_id,
$translationData
);
}
else
{
$translationResult = $this->ContentspracheModel->insert([
'sprache' => $translation['sprache'],
'content_id' => $existingNews->content_id,
'version' => 1,
'insertamum' => $now,
'insertvon' => $uid
] + $translationData);
}
$this->_getInsertedIdOrRollback($translationResult);
}
if ($this->db->trans_status() === false)
{
$error = $this->db->error();
$this->db->trans_rollback();
$this->terminateWithError($error, self::ERROR_TYPE_DB);
}
if ($this->db->trans_commit() === false)
{
$error = $this->db->error();
$this->db->trans_rollback();
$this->terminateWithError($error, self::ERROR_TYPE_DB);
}
$this->terminateWithSuccess($newsId);
}
/**
* Deletes one news entry and its linked content.
*
* @return void
*/
public function deleteNewsItem($newsId)
{
if (!is_numeric($newsId) || (int)$newsId < 1)
{
$this->terminateWithValidationErrors([
'newsId' => 'A valid news ID is required'
]);
}
$newsId = (int)$newsId;
$newsResult = $this->NewsModel->load($newsId);
if (isError($newsResult))
{
$this->terminateWithError(getError($newsResult), self::ERROR_TYPE_DB);
}
if (!hasData($newsResult))
{
$this->terminateWithValidationErrors([
'newsId' => 'The requested news entry does not exist'
]);
}
$news = current(getData($newsResult));
$this->_ensureUserIsEntitledForDegreeProgram($news->studiengang_kz);
if ($this->db->trans_begin() === false)
{
$this->terminateWithError($this->db->error(), self::ERROR_TYPE_DB);
}
$newsDeleteResult = $this->NewsModel->delete($newsId);
if (isError($newsDeleteResult))
{
$this->db->trans_rollback();
$this->terminateWithError(getError($newsDeleteResult), self::ERROR_TYPE_DB);
}
$this->db->where('content_id', $news->content_id);
if ($this->db->delete('campus.tbl_contentsprache') === false)
{
$error = $this->db->error();
$this->db->trans_rollback();
$this->terminateWithError($error, self::ERROR_TYPE_DB);
}
$contentDeleteResult = $this->ContentModel->delete($news->content_id);
if (isError($contentDeleteResult))
{
$this->db->trans_rollback();
$this->terminateWithError(getError($contentDeleteResult), self::ERROR_TYPE_DB);
}
if ($this->db->trans_status() === false)
{
$error = $this->db->error();
$this->db->trans_rollback();
$this->terminateWithError($error, self::ERROR_TYPE_DB);
}
if ($this->db->trans_commit() === false)
{
$error = $this->db->error();
$this->db->trans_rollback();
$this->terminateWithError($error, self::ERROR_TYPE_DB);
}
$this->terminateWithSuccess($newsId);
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
/**
* Loads the requested Studiengang or reports a validation error.
*
* @param integer $studiengangKz
* @return stdClass
*/
private function _getStudiengang($studiengangKz)
{
$result = $this->StudiengangModel->load($studiengangKz);
if (isError($result))
{
$this->terminateWithError(getError($result), self::ERROR_TYPE_DB);
}
if (!hasData($result))
{
$this->terminateWithValidationErrors([
'studiengang_kz' => 'The requested Studiengang does not exist'
]);
}
$studiengaenge = getData($result);
return current($studiengaenge);
}
/**
* Ensures that all supplied languages exist in tbl_sprache.
*
* @param array $translations
* @return void
*/
private function _validateLanguages($translations)
{
$languages = [];
foreach ($translations as $translation)
{
$languages[] = $translation['sprache'];
}
$result = $this->SpracheModel->loadMultiple($languages);
if (isError($result))
{
$this->terminateWithError(getError($result), self::ERROR_TYPE_DB);
}
$existingLanguages = [];
foreach ((array)getData($result) as $language)
{
$existingLanguages[$language->sprache] = true;
}
$errors = [];
foreach ($translations as $index => $translation)
{
if (!isset($existingLanguages[$translation['sprache']]))
{
$errors['translations.' . $index . '.sprache'] = 'The requested language does not exist';
}
}
if (count($errors) > 0)
{
$this->terminateWithValidationErrors($errors);
}
}
private function _ensureUserIsEntitledForDegreeProgram($degreeProgramShortCode)
{
$entitledDegreePrograms = $this->permissionlib->getSTG_isEntitledFor("basis/news:w");
if (!is_array($entitledDegreePrograms))
{
$this->terminateWithError('Failed to retrieve entitled degree programs for the user', self::ERROR_TYPE_GENERAL);
}
if (!in_array($degreeProgramShortCode, $entitledDegreePrograms)) {
$this->terminateWithError("User is not entitled for the specified degree program");
}
}
/**
* Returns an inserted identifier or rolls the transaction back on error.
*
* @param stdClass $result
* @return mixed
*/
private function _getInsertedIdOrRollback($result)
{
if (isError($result))
{
$this->db->trans_rollback();
$this->terminateWithError(getError($result), self::ERROR_TYPE_DB);
}
return getData($result);
}
/**
* Builds the XML format consumed by existing news readers.
*
* @param array $translation
* @return string
*/
private function _buildNewsXml($translation)
{
$document = new DOMDocument('1.0', 'UTF-8');
$news = $document->createElement('news');
$document->appendChild($news);
$this->_appendCdataElement($document, $news, 'verfasser', $translation['verfasser']);
$this->_appendCdataElement($document, $news, 'betreff', $translation['betreff']);
$this->_appendCdataElement($document, $news, 'text', $translation['text']);
return $document->saveXML($news);
}
/**
* Appends a CDATA element and safely preserves CDATA terminators.
*
* @param DOMDocument $document
* @param DOMElement $parent
* @param string $name
* @param string $value
* @return void
*/
private function _appendCdataElement($document, $parent, $name, $value)
{
$element = $document->createElement($name);
$parts = explode(']]>', $value);
$lastIndex = count($parts) - 1;
foreach ($parts as $index => $part)
{
if ($index > 0)
{
$part = '>' . $part;
}
if ($index < $lastIndex)
{
$part .= ']]';
}
$element->appendChild($document->createCDATASection($part));
}
$parent->appendChild($element);
}
private function _sendNewsItemTranslationRequiredMail($newsItem)
{
$translatorsResult = $this->BenutzerfunktionModel->getBenutzerFunktionen("translate", $newsItem->studiengang_kz);
$translators = $this->getDataOrTerminateWithError($translatorsResult);
$newsItemContentResult = $this->ContentspracheModel->loadWhere([
'content_id' => $newsItem->content_id,
'sprache' => 'German'
]);
$newsItemContentEntry = getData($newsItemContentResult)[0] ?? null;
if ($newsItemContentEntry) {
$newsItem->sichtbar = $newsItemContentEntry->sichtbar ?? false;
$newsItem->content = $newsItemContentEntry->content;
}
$content = getData(
$this->cmslib->createNewsContent(
$this->cmslib->createNewsWrapper($newsItem, true),
false,
""
)
);
foreach ($translators as $translator) {
sendSanchoMail(
"Sancho_Mail_News_Translation_Req",
[
"newsItemUrl" => APP_ROOT . "cis.php/CisVue/Cms/newsAdministration?newsId=" . $newsItem->news_id,
"newsItemAuthor" => $newsItem->verfasser,
"newsItemSubject" => $newsItem->betreff,
"newsItemContent" => $content
],
$translator->uid . "@" . DOMAIN,
"Übersetzung erforderlich / Translation required"
);
}
}
private function formatContentURLsToRelative($content)
{
$content = preg_replace(
'#https?://[^"\'>\s]*/(dms\.php(?:\?[^"\'>\s]*)?)#i',
'$1',
$content
);
$content = preg_replace(
'#(?:\.\./)+[^"\'>\s]*/(dms\.php(?:\?[^"\'>\s]*)?)#i',
'$1',
$content
);
$content = preg_replace(
'#/[^"\'>\s]*/(dms\.php(?:\?[^"\'>\s]*)?)#i',
'$1',
$content
);
return $content;
}
}
File diff suppressed because it is too large Load Diff
+6 -137
View File
@@ -25,19 +25,7 @@ if (! defined('BASEPATH')) exit('No direct script access allowed');
*/
class Ort extends FHCAPI_Controller
{
// advanced filters of getRooms: tbl_ort column => filter type, the same list as in Raumsuche.js.
// text: contains, case-insensitive; select: equals; range: <column>_min, <column>_max; flag: true; exists: not null
const ADVANCED_FILTERS = [
'ort_kurzbz' => 'text',
'bezeichnung' => 'text',
'planbezeichnung' => 'text',
'stockwerk' => 'range',
'gebteil' => 'select',
'm2' => 'range',
'lehre' => 'flag',
'content_id' => 'exists'
];
/**
* Object initialization
*/
@@ -48,10 +36,7 @@ class Ort extends FHCAPI_Controller
'ContentID' => self::PERM_LOGGED,
'getOrtKurzbzContent' => self::PERM_LOGGED,
'getRooms' => self::PERM_LOGGED,
'getTypes' => self::PERM_LOGGED,
'getRoomInfo' => self::PERM_LOGGED,
'getStandorte' => self::PERM_LOGGED,
'getAdvancedFilterOptions' => self::PERM_LOGGED
'getTypes' => self::PERM_LOGGED
]);
$this->load->model('ressource/Ort_model', 'OrtModel');
@@ -71,17 +56,6 @@ class Ort extends FHCAPI_Controller
$this->form_validation->set_rules('datum','Datum','required');
$this->form_validation->set_rules('von','Uhrzeit Von','required|regex_match[/^[0-9]{2}:[0-9]{2}$/]');
$this->form_validation->set_rules('bis','Uhrzeit Bis','required|regex_match[/^[0-9]{2}:[0-9]{2}$/]');
$this->form_validation->set_rules('standort_id','Standort','regex_match[/^([0-9]+|none)$/]');
foreach (self::ADVANCED_FILTERS as $column => $type) {
if ($type == 'range') {
$this->form_validation->set_rules($column.'_min', $column, 'numeric');
$this->form_validation->set_rules($column.'_max', $column, 'numeric');
} elseif ($type == 'flag' || $type == 'exists') {
$this->form_validation->set_rules($column, $column, 'in_list[true]');
} else {
$this->form_validation->set_rules($column, $column, 'max_length[255]');
}
}
if($this->form_validation->run() == FALSE) {
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
@@ -91,7 +65,6 @@ class Ort extends FHCAPI_Controller
$bis = $this->input->get('bis', TRUE);
$typ = $this->input->get('typ', TRUE);
$personenanzahl = $this->input->get('personenanzahl', TRUE);
$standort_id = $this->input->get('standort_id', TRUE);
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
@@ -101,36 +74,22 @@ class Ort extends FHCAPI_Controller
$vonStunde = getData($this->StundeModel->getStundeForTime($von))[0]->stunde;
$bisStunde = getData($this->StundeModel->getStundeForTime($bis))[0]->stunde;
$this->load->model('ressource/Ort_model', 'OrtModel');
$params = array();
$qry = "SELECT DISTINCT tbl_ort.*, tbl_standort.bezeichnung AS standort
$qry = "SELECT DISTINCT tbl_ort.*
FROM public.tbl_ort JOIN public.tbl_ortraumtyp USING(ort_kurzbz)
LEFT JOIN public.tbl_standort USING(standort_id)
WHERE aktiv AND reservieren";
WHERE aktiv AND lehre AND ort_kurzbz NOT LIKE '\\\\_%'";
if($typ) {
$params[] = $typ;
$qry.= " AND raumtyp_kurzbz = ?";
$qry.= "AND raumtyp_kurzbz = ?";
}
if($standort_id === 'none') { // rooms without a Standort
$qry.= " AND standort_id IS NULL";
} elseif($standort_id) {
$params[] = $standort_id;
$qry.= " AND standort_id = ?";
} else {
$qry.= " AND standort_id IS NOT NULL";
}
$qry.= $this->advancedFilterSql($params);
if(!$isMitarbeiter) { // students are only allowed to get a subset defined by config
$qry.= ' AND raumtyp_kurzbz IN ?';
$params[] = $this->config->item('roomtypes_student');
$this->addMeta('config', $this->config->item('roomtypes_student'));
}
$qry.= " AND (max_person>= ? OR max_person is null)";
$qry.= "AND (max_person>= ? OR max_person is null)";
$params[] = $personenanzahl;
$qry.=" AND ort_kurzbz NOT IN
@@ -168,41 +127,6 @@ class Ort extends FHCAPI_Controller
$this->terminateWithSuccess(getData($result));
}
/**
* Standorte of the rooms that pass the base filter of getRooms
*/
public function getStandorte()
{
$qry = "SELECT DISTINCT standort_id, tbl_standort.bezeichnung
FROM public.tbl_ort JOIN public.tbl_standort USING(standort_id)
WHERE aktiv AND reservieren
ORDER BY tbl_standort.bezeichnung";
$result = $this->OrtModel->execReadOnlyQuery($qry);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
/**
* Values of the select filters: column => distinct values of the rooms that pass the base filter of getRooms
*/
public function getAdvancedFilterOptions()
{
$options = [];
foreach (self::ADVANCED_FILTERS as $column => $type) {
if ($type != 'select')
continue;
$result = $this->OrtModel->execReadOnlyQuery("SELECT DISTINCT ".$column." AS value
FROM public.tbl_ort
WHERE aktiv AND reservieren AND ".$column." <> ''
ORDER BY ".$column);
$options[$column] = array_column($this->getDataOrTerminateWithError($result), 'value');
}
$this->terminateWithSuccess($options);
}
/**
* Gets a JSON body via HTTP POST and provides the parameters
*/
@@ -250,60 +174,5 @@ class Ort extends FHCAPI_Controller
$this->terminateWithSuccess($content);
}
public function getRoomInfo()
{
$ortKurzbz = $this->input->get("ortKurzbz");
$roomResult = $this->OrtModel->getRoomInfo($ortKurzbz);
$roomData = $this->getDataOrTerminateWithError($roomResult);
$room = count($roomData) ? $roomData[0] : null;
$this->terminateWithSuccess($room);
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
/**
* SQL conditions of the advanced filters in the GET parameters, adds their values to $params
*/
private function advancedFilterSql(&$params)
{
// no xss_clean: the values are bound, and it could change a select value so that it no longer equals the column
$sql = '';
foreach (self::ADVANCED_FILTERS as $column => $type) {
if ($type == 'range') {
$min = $this->input->get($column.'_min');
$max = $this->input->get($column.'_max');
if ($min !== null && $min !== '') {
$sql.= " AND tbl_ort.".$column." >= ?::numeric";
$params[] = $min;
}
if ($max !== null && $max !== '') {
$sql.= " AND tbl_ort.".$column." <= ?::numeric";
$params[] = $max;
}
continue;
}
$value = $this->input->get($column);
if ($value === null || $value === '')
continue;
if ($type == 'flag') {
$sql.= " AND tbl_ort.".$column;
} elseif ($type == 'exists') {
$sql.= " AND tbl_ort.".$column." IS NOT NULL";
} elseif ($type == 'select') {
$sql.= " AND tbl_ort.".$column." = ?";
$params[] = $value;
} else {
$sql.= " AND strpos(lower(tbl_ort.".$column."), lower(?)) > 0";
$params[] = $value;
}
}
return $sql;
}
}
@@ -1,6 +1,6 @@
<?php
/**
* Copyright (C) 2026 fhcomplete.org
* 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
@@ -1,61 +0,0 @@
<?php
/**
* Copyright (C) 2026 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (!defined('BASEPATH'))
exit('No direct script access allowed');
class Permission extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getPermissions' => self::PERM_LOGGED,
]);
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
* retrieves permissions for auth user
* @access public
*/
public function getPermissions()
{
$permissionsParam = $this->input->post("permissions");
if (!$permissionsParam || !is_array($permissionsParam)) {
$this->terminateWithError("Invalid parameter!");
}
$permissions = [];
foreach ($permissionsParam as $permission) {
$permissions[$permission] = $this->permissionlib->isBerechtigt($permission);
}
$this->terminateWithSuccess($permissions);
}
// -----------------------------------------------------------------------------------------------------------------
// Private methods
}
@@ -1,67 +0,0 @@
<?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 Reservierung extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getMyReservation' => 'basis/cis:r',
'deleteReservation' => 'lehre/reservierung:rw'
]);
$this->load->model('ressource/Reservierung_model', 'ReservierungModel');
}
/**
* Retrieves all reservations from lehre.vw_reservation of the logged in user
*/
public function getMyReservation()
{
$result = $this->ReservierungModel->getMyReservation();
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
/**
* deletes a reservation by ID
*/
public function deleteReservation()
{
$reservierung_id = $this->input->post('reservierung_id');
if ($reservierung_id === NULL || trim((string)$reservierung_id) === '') {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$result = $this->ReservierungModel->deleteReservation($reservierung_id);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
}
@@ -27,7 +27,7 @@ class Studgang extends FHCAPI_Controller
{
parent::__construct([
'getStudiengangInfo'=> self::PERM_LOGGED,
'getDegreePrograms'=> self::PERM_LOGGED,
]);
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
@@ -43,46 +43,15 @@ class Studgang extends FHCAPI_Controller
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function getDegreePrograms() {
$entitledDegreePrograms = $this->permissionlib->getSTG_isEntitledFor("basis/news");
if (!$entitledDegreePrograms) {
$this->terminateWithError("User is not entitled for any degree program");
}
$activeDegreePrograms = $this->StudiengangModel->getDegreePrograms();
$activeDegreePrograms= $this->getDataOrTerminateWithError($activeDegreePrograms);
$activeDegreePrograms = array_values(array_filter($activeDegreePrograms, function($degreeProgram) use ($entitledDegreePrograms) {
return in_array($degreeProgram->studiengang_kz, $entitledDegreePrograms);
}));
$this->terminateWithSuccess($activeDegreePrograms);
}
public function getStudiengangInfo(){
$studiengang_kz = $this->input->get('studiengang_kz', true);
$semester = $this->input->get('semester', true);
// without a Studiengang the info of the logged in Student is returned
if (!is_numeric($studiengang_kz))
{
$isMitarbeiter = $this->MitarbeiterModel->isMitarbeiter(getAuthUID());
$isMitarbeiter = $this->getDataOrTerminateWithError($isMitarbeiter);
if($isMitarbeiter) {
$this->terminateWithSuccess(null);
}
$studiengang_kz = null;
$semester = null;
$isMitarbeiter = $this->MitarbeiterModel->isMitarbeiter(getAuthUID());
$isMitarbeiter = $this->getDataOrTerminateWithError($isMitarbeiter);
if($isMitarbeiter) {
$this->terminateWithSuccess(null);
}
else
{
$studiengang_kz = (int)$studiengang_kz;
$semester = is_numeric($semester) ? (int)$semester : null;
}
// fetches the Studiengang Information which is used next to the news
$studiengangInfo = $this->StudiengangModel->getStudiengangInfoForNews($studiengang_kz, $semester);
$studiengangInfo = $this->StudiengangModel->getStudiengangInfoForNews();
$studiengangInfo= $this->getDataOrTerminateWithError($studiengangInfo);
$this->terminateWithSuccess($studiengangInfo);
}
@@ -1,67 +0,0 @@
<?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
}
@@ -1,145 +0,0 @@
<?php
/**
* Copyright (C) 2026 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
class StudstatusAntraege extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getTodos' => self::PERM_LOGGED
]);
//load models
$this->load->model('education/Studierendenantrag_model', 'StudierendenantragModel');
$this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
* gets all todos of antraegen of the user with stg leitungsfunction
* or list of open antraege for user with assistance function
* @access public
* @return array || []
*/
public function getTodos()
{
$uid = getAuthUID();
//at first get studiengang with leitungsfunktion
$result = $this->BenutzerfunktionModel->getSTGLByUID($uid);
if(hasData($result))
{
$funktionen = getData($result);
$studiengaenge = [];
foreach ($funktionen as $funktion) {
$studiengaenge[] = $funktion->studiengang_kz;
}
$dataAntrage = [];
$result = $this->StudierendenantragModel->getOpenAntraegeForStgl($studiengaenge);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$dataAntrage[] = getData($result) ?: [];
$statusRole = 1;
$this->terminateWithSuccess([count($dataAntrage) > 0 ? $dataAntrage[0] : $dataAntrage, $statusRole]);
}
//TODO delete after check if needed or not
//get studiengaenge of assistance (regarding benutzerfunktion)
//$result = $this->BenutzerfunktionModel->getSTGAssByUID($uid);
/* if(hasData($result)) {
$statusRole = 2;
$funktionen = getData($result);
$studiengaenge = [];
foreach ($funktionen as $funktion) {
$studiengaenge[] = $funktion->studiengang_kz;
}
$ci->addMeta('stgAss', $studiengaenge);
$dataAntrage = [];
foreach ($studiengaenge as $studiengang)
{
$result = $this->StudierendenantragModel->getOpenAntraegeForAss($studiengang);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
if(hasData($result)) {
$dataAntrage[] = getData($result);
$statusRole = 2;
}
}
}
else
{
$statusRole = 0;
$dataAntrage = [];
//$this->terminateWithSuccess([[], $statusRole]);
}*/
//get studiengaenge of assistance (regarding rights)
$stgAss = $this->permissionlib->getSTG_isEntitledFor('student/studierendenantrag');
if(!is_array($stgAss))
{
$statusRole = 0;
$this->terminateWithSuccess([[], $statusRole]);
}
else {
$statusRole = 2;
$studiengaenge = [];
foreach ($stgAss as $stg) {
$studiengaenge[] = (int) $stg;
}
$dataAntrage = [];
$result = $this->StudierendenantragModel->getOpenAntraegeForAss($studiengaenge);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
if(hasData($result)) {
$dataAntrage[] = getData($result);
$statusRole = 2;
}
$this->terminateWithSuccess([count($dataAntrage) > 0 ? $dataAntrage[0] : $dataAntrage, $statusRole]);
}
}
}
@@ -1,469 +0,0 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Zeitsperren extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'getZeitsperrenUser' => ['cis/eigene_zeitsperren:r', 'admin:r', 'assistenz:r'],
'getTypenZeitsperren' => self::PERM_LOGGED,
'getTypenErreichbarkeit' => self::PERM_LOGGED,
'getStunden' => self::PERM_LOGGED,
'loadZeitsperre' => ['cis/eigene_zeitsperren:r', 'admin:r', 'assistenz:r'],
'add' => ['cis/eigene_zeitsperren:rw', 'admin:rw', 'assistenz:rw'],
'update' => ['cis/eigene_zeitsperren:rw', 'admin:rw', 'assistenz:rw'],
'delete' => ['cis/eigene_zeitsperren:rw', 'admin:rw', 'assistenz:rw'],
]);
// 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()
{
$excludedTypes = array('ZVerfueg');
$result = $this->ZeitsperretypModel->getTypenZeitsperren($excludedTypes);
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);
if($zeitsperretyp_kurzbz == 'ZA')
{
$this->sendSanchoMailToVorgesetzten(
'Sancho_Content_Zeitausgleich',
array(
'beschreibung' =>$zeitsperretyp_kurzbz,
'vonDatum' => (new DateTime($vondatum))->format('d.m.Y'),
'bisDatum' => (new DateTime($bisdatum))->format('d.m.Y'),
'Saldo'=> ''
),
$this->p->t('zeitsperren', 'zeitausgleichNeu'),
$mitarbeiter_uid
);
}
$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 = (array) $this->getDataOrTerminateWithError($result);
$data = array_filter($data, function($zeitsperre) use ($zeitsperre_id) {
return $zeitsperre->zeitsperre_id != $zeitsperre_id;
});
if(count($data) > 0)
{
$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);
if($zeitsperretyp_kurzbz == 'ZA')
{
$this->sendSanchoMailToVorgesetzten(
'Sancho_Content_ZA_Aendern',
array(
'beschreibung' =>$zeitsperretyp_kurzbz,
'vonDatum' => (new DateTime($vondatum))->format('d.m.Y'),
'bisDatum' => (new DateTime($bisdatum))->format('d.m.Y'),
'Saldo'=> ''
),
$this->p->t('zeitsperren', 'zeitausgleichGeaendert'),
$mitarbeiter_uid
);
}
$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);
$zeitsperre = (is_array($data) && count($data) > 0) ? current($data) : null;
$uid = (!is_null($zeitsperre)) ? $zeitsperre->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);
}
if($zeitsperre->zeitsperretyp_kurzbz == 'ZA')
{
$res = $this->sendSanchoMailToVorgesetzten(
'Sancho_Mail_ZA_loeschen',
array(
'beschreibung' =>$zeitsperre->zeitsperretyp_kurzbz,
'vonDatum' => (new DateTime($zeitsperre->vondatum))->format('d.m.Y'),
'bisDatum' => (new DateTime($zeitsperre->bisdatum))->format('d.m.Y')
),
$this->p->t('zeitsperren', 'zeitausgleichGeloescht'),
$zeitsperre->mitarbeiter_uid
);
}
$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;
}
protected function sendSanchoMailToVorgesetzten($sancho_vorlage_kurzbz,
$sancho_vorlage_data, $subject, $mauid)
{
$this->load->helper('hlp_sancho_helper');
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$this->load->model('person/Person_model', 'PersonModel');
$fullname = getData($this->PersonModel->getFullName($mauid));
$vorgesetzte = (array) getData($this->MitarbeiterModel->getVorgesetzte($mauid));
$to = array();
$vorgesetzte_namen = array();
$mailsent = false;
$msg = $this->p->t('zeitsperren', 'konnteKeinFreigabemailVersendetWerden');
if(count($vorgesetzte) > 0)
{
foreach($vorgesetzte as $vg)
{
$to[] = $vg->vorgesetzter .'@' . DOMAIN;
$vorgesetzter_fullname = getData($this->PersonModel->getFullName($vg->vorgesetzter));
$vorgesetzte_namen[] = $vorgesetzter_fullname;
}
$sancho_vorlage_data['vorgesetzter'] = implode(', ', $vorgesetzte_namen);
$sancho_vorlage_data['nameMitarbeiter'] = $fullname;
$mailsent = sendSanchoMail(
$sancho_vorlage_kurzbz,
$sancho_vorlage_data,
implode(',', $to),
$subject
);
if($mailsent)
{
$msg = $this->p->t('zeitsperren', 'VorgesetzteInformiert',
array('vorgesetzte' => $sancho_vorlage_data['vorgesetzter']));
}
else
{
$msg = $this->p->t('zeitsperren', 'fehlerBeimSendenAufgetreten',
array('vorgesetzte' => $sancho_vorlage_data['vorgesetzter']));
}
}
$res = (object) array(
'mailsent' => $mailsent,
'msg' => $msg
);
$this->addMeta('mailstatus', $res);
}
}
@@ -1,276 +0,0 @@
<?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,
'getGroupOptions' => 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) : []));
}
public function getGroupOptions()
{
$studiengangKz = $this->input->get('studiengangKz');
$this->load->model('organisation/Lehrverband_model', 'LehrverbandModel');
$this->LehrverbandModel->addSelect('semester, verband, gruppe');
$this->LehrverbandModel->db->where('aktiv', true);
$this->LehrverbandModel->db->where('studiengang_kz', $studiengangKz);
$lehrverbandResult = $this->LehrverbandModel->load();
if (isError($lehrverbandResult))
$this->terminateWithError($lehrverbandResult);
$lehrverbanden = hasData($lehrverbandResult) ? getData($lehrverbandResult) : [];
$grouppedValues = [];
foreach ($lehrverbanden as $lehrverband) {
if (isset($grouppedValues[$lehrverband->semester]) && $lehrverband->verband !== ' ') {
$grouppedValues[$lehrverband->semester][] = $lehrverband;
} else if (!isset($grouppedValues[$lehrverband->semester]) && $lehrverband->verband !== ' ') {
$grouppedValues[$lehrverband->semester] = [$lehrverband];
} else if (!isset($grouppedValues[$lehrverband->semester])) {
$grouppedValues[$lehrverband->semester] = [];
}
}
$grouppedValues = array_map(
function ($semester) {
$verbandGruppen = [];
foreach ($semester as $lehrverband) {
if (isset($verbandGruppen[$lehrverband->verband]) && $lehrverband->gruppe !== ' ') {
$verbandGruppen[$lehrverband->verband][] = $lehrverband->gruppe;
} else if (!isset($verbandGruppen[$lehrverband->verband]) && $lehrverband->gruppe !== ' ') {
$verbandGruppen[$lehrverband->verband] = [$lehrverband->gruppe];
} else if (!isset($verbandGruppen[$lehrverband->verband])) {
$verbandGruppen[$lehrverband->verband] = [];
}
}
return $verbandGruppen;
},
$grouppedValues
);
$this->terminateWithSuccess($grouppedValues);
}
}
@@ -101,9 +101,6 @@ class Widget extends FHCAPI_Controller
$tmpsetup = json_decode($widget->setup);
$tmpsetup->file = absoluteJsImportUrl($tmpsetup->file);
$widget->setup = $tmpsetup;
$widget->permitted = empty($widget->berechtigung_kurzbz)
|| $this->permissionlib->isBerechtigt($widget->berechtigung_kurzbz);
return $widget;
}, $widgets);
@@ -1,218 +0,0 @@
<?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.");
//~ }
//~ }
//~ }
}
@@ -12,9 +12,9 @@ class Funktionen extends FHCAPI_Controller
parent::__construct(array(
'getAllFunctions' => ['admin:r', 'assistenz:r'],
'getAllUserFunctions' => ['admin:r', 'assistenz:r'],
'getOrgHeads' => self::PERM_LOGGED,
'getOrgetsForCompany' => self::PERM_LOGGED,
'getAllOrgUnits' => self::PERM_LOGGED,
'getOrgHeads' => ['admin:r', 'assistenz:r'],
'getOrgetsForCompany' => ['admin:r', 'assistenz:r'],
'getAllOrgUnits' => ['admin:r', 'assistenz:r'],
'loadFunction' => ['admin:r', 'assistenz:r'],
'insertFunction' => ['admin:rw', 'assistenz:rw'],
'updateFunction' => ['admin:rw', 'assistenz:rw'],
@@ -1,81 +0,0 @@
<?php
/**
* Copyright (C) 2026 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Betreuungen extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getBetreuungen' => self::PERM_LOGGED,
]);
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function getBetreuungen()
{
$semester = $this->input->get("semester");
if (!$semester) $this->terminateWithError("Missing parameters!");
$personId = getAuthPersonId();
$betreuungenQuery = "SELECT
tbl_lehrveranstaltung.bezeichnung,
tbl_projektarbeit.titel,
(SELECT nachname || ' ' || (CASE WHEN wahlname IS NOT NULL THEN wahlname ELSE vorname END)
FROM public.tbl_benutzer
JOIN public.tbl_person USING(person_id)
WHERE uid=student_uid)
as name,
(SELECT uid
FROM public.tbl_benutzer
WHERE uid=student_uid)
as uid,
tbl_lehrveranstaltung.studiengang_kz,
tbl_lehrveranstaltung.semester,
tbl_studiengang.kurzbzlang as studiengang,
tbl_studiengang.email,
tbl_betreuerart.beschreibung AS beutreuerart_beschreibung,
tbl_projektbetreuer.stunden
FROM
lehre.tbl_lehrveranstaltung, lehre.tbl_projektarbeit, lehre.tbl_projektbetreuer, public.tbl_studiengang, lehre.tbl_betreuerart
WHERE
tbl_lehrveranstaltung.lehrveranstaltung_id=tbl_projektarbeit.lehrveranstaltung_id AND
tbl_projektarbeit.studiensemester_kurzbz = '$semester' AND
tbl_projektarbeit.projektarbeit_id = tbl_projektbetreuer.projektarbeit_id AND
tbl_lehrveranstaltung.studiengang_kz = tbl_studiengang.studiengang_kz AND
tbl_projektbetreuer.betreuerart_kurzbz = tbl_betreuerart.betreuerart_kurzbz AND
tbl_projektbetreuer.person_id = '$personId'";
$this->addMeta("query", $betreuungenQuery);
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$result = $this->LehrveranstaltungModel->execReadOnlyQuery($betreuungenQuery);
$result = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($result);
}
}
@@ -19,8 +19,7 @@ class Lehreinheit extends FHCAPI_Controller
'getLehrfach' => ['admin:r', 'assistenz:r'],
'getSprache' => ['admin:r', 'assistenz:r'],
'getRaumtyp' => ['admin:r', 'assistenz:r'],
'getLehrform' => ['admin:r', 'assistenz:r'],
'getLehreinheitenBySemester' => self::PERM_LOGGED,
'getLehrform' => ['admin:r', 'assistenz:r']
]);
$this->_ci = &get_instance();
@@ -439,27 +438,6 @@ class Lehreinheit extends FHCAPI_Controller
$this->terminateWithSuccess($this->p->t('global', 'gespeichert'));
}
public function getLehreinheitenBySemester()
{
$semester = $this->input->get('semester');
$lehreinheiten = $this->LehreinheitModel->getLehrenheitenBySemester(getAuthUID(), $semester);
$lehreinheiten = $this->getDataOrTerminateWithError($lehreinheiten);
foreach ($lehreinheiten as $lehreinheit) {
$groups = $this->LehreinheitModel->getGroupsByLehreinheit($lehreinheit->lehreinheit_id);
$groups = $this->getDataOrTerminateWithError($groups);
$stgKurzbz = $lehreinheit->stg_kurzbz;
$lehreinheit->groups = array_map(
function ($group) use ($stgKurzbz) {
return $group->gruppe_kurzbz ?? $stgKurzbz . "-" . $group->semester . $group->verband . $group->gruppe;
},
$groups
);
}
$this->terminateWithSuccess($lehreinheiten);
}
private function checkPermission($lehreinheit_id)
{
@@ -28,8 +28,7 @@ class Studiensemester extends FHCAPI_Controller
'getStudienjahrByStudiensemester' => self::PERM_LOGGED,
'getAllStudiensemesterAndAktOrNext' => self::PERM_LOGGED,
'current' => self::PERM_LOGGED,
'set' => self::PERM_LOGGED,
'getContainingOrNearestByDateRange' => self::PERM_LOGGED,
'set' => self::PERM_LOGGED
)
);
// Load model StudiensemesterModel
@@ -204,15 +203,4 @@ class Studiensemester extends FHCAPI_Controller
$this->terminateWithSuccess(true);
}
public function getContainingOrNearestByDateRange()
{
$from = $this->input->get("from");
$to = $this->input->get("to");
$result = $this->StudiensemesterModel->getContainingOrNearestByDateRange($from, $to);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
}
@@ -41,9 +41,6 @@ class Grades extends FHCAPI_Controller
// Load Libraries
$this->load->library('VariableLib', ['uid' => getAuthUID()]);
$this->load->library('PruefungsverlaufLib', null, 'VerlaufLib');
$this->load->config('noten');
// Load Phrases
$this->loadPhrases([
@@ -358,24 +355,23 @@ class Grades extends FHCAPI_Controller
$data['lehrveranstaltung_id'] = $lehrveranstaltung_id;
$data['student_uid'] = $student_uid;
$data['studiensemester_kurzbz'] = $studiensemester_kurzbz;
$this->ZeugnisnoteModel->insert($data);
if (defined('FAS_PRUEFUNG_BEI_NOTENEINGABE_ANLEGEN')
&& FAS_PRUEFUNG_BEI_NOTENEINGABE_ANLEGEN) {
$result = $this->addTestsForGrade(
$studiensemester_kurzbz,
$student_uid,
$lehrveranstaltung_id,
$teacherGrade->note,
$teacherGrade->punkte
);
$this->getDataOrTerminateWithError($result);
}
}
// Der erste Antritt wird bereits bei der Übernahme als eigene Prüfung angelegt, damit der
// Prüfungsverlauf von Anfang an vollständig ist und das Benotungstool ihn nicht erst beim
// Anlegen einer Wiederholung nachtragen muss. Läuft bewusst in BEIDEN Zweigen (auch beim
// Überschreiben einer bestehenden Zeugnisnote) und ist idempotent.
if ($this->config->item('CIS_GESAMTNOTE_ERSTANTRITT_BEI_UEBERNAHME')) {
$result = $this->addErstantrittForGrade(
$studiensemester_kurzbz,
$student_uid,
$lehrveranstaltung_id,
$teacherGrade
);
$this->getDataOrTerminateWithError($result);
}
$this->terminateWithSuccess(true);
}
@@ -495,32 +491,20 @@ class Grades extends FHCAPI_Controller
}
/**
* Legt bei der Notenübernahme den ersten Prüfungsantritt als eigenen Datensatz an
* (Eintrag in lehre.tbl_pruefung).
* Helper function that adds tests for a student
* (Entries in lehre.tbl_pruefung)
*
* Idempotent: existiert für den Studenten in dieser LV bereits ein Termin, passiert nichts -
* die Übernahme darf beliebig oft laufen, ohne den Verlauf zu verfälschen.
*
* Datiert auf das Freigabedatum der LV-Note: das ist der Zeitpunkt, zu dem die
* Leistungsfeststellung verbindlich wurde. Das Benotungsdatum ändert sich bei jeder Korrektur,
* der Übernahmezeitpunkt ist reine Verwaltungsarbeit - beide taugen nicht als Termindatum.
*
* @param string $studiensemester_kurzbz
* @param string $student_uid
* @param integer $lehrveranstaltung_id
* @param stdClass $teacherGrade die übernommene LV-Gesamtnote
* @param string $studiensemester_kurzbz
* @param string $student_uid
* @param integer $lehrveranstaltung_id
* @param integer $note
* @param numeric $punkte
*
* @return stdClass
*/
protected function addErstantrittForGrade($studiensemester_kurzbz, $student_uid, $lehrveranstaltung_id, $teacherGrade)
protected function addTestsForGrade($studiensemester_kurzbz, $student_uid, $lehrveranstaltung_id, $note, $punkte)
{
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->load->model('education/LePruefung_model', 'LePruefungModel');
// bereits ein Termin vorhanden -> der Verlauf beginnt schon, nichts nachzutragen
$vorhanden = $this->VerlaufLib->getPruefungen($student_uid, $lehrveranstaltung_id, $studiensemester_kurzbz);
if (count($vorhanden) > 0)
return success(null);
// Get Lehreinheit
$result = $this->LehrveranstaltungModel->getLeByStudent($student_uid, $studiensemester_kurzbz, $lehrveranstaltung_id);
@@ -531,24 +515,19 @@ class Grades extends FHCAPI_Controller
return error($this->p->t("stv", "grades_error_lehreinheit_id"));
$le = current(getData($result));
$datum = $teacherGrade->freigabedatum ?: ($teacherGrade->benotungsdatum ?: date('Y-m-d'));
$now = date('Y-m-d H:i:s');
// Prepare
$this->load->model('education/LePruefung_model', 'LePruefungModel');
$data = [
"student_uid" => $student_uid,
"lehreinheit_id" => $le->lehreinheit_id,
"mitarbeiter_uid" => $teacherGrade->mitarbeiter_uid,
"datum" => $datum,
"pruefungstyp_kurzbz" => $this->VerlaufLib->legacyTypFuerAntritt(1),
"note" => $teacherGrade->note,
"insertamum" => $now,
"insertvon" => getAuthUID()
"datum" => date('Y-m-d'),
"pruefungstyp_kurzbz" => "Termin1",
"note" => $note
];
if (defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE)
$data["punkte"] = $teacherGrade->punkte;
$data["punkte"] = $punkte;
// Get Anwesenheit
$this->load->model('education/Anwesenheit_model', 'AnwesenheitModel');
$result = $this->AnwesenheitModel->loadAnwesenheitStudiensemester($studiensemester_kurzbz, $student_uid, $lehrveranstaltung_id);
@@ -556,8 +535,7 @@ class Grades extends FHCAPI_Controller
return $result;
$anwesenheit = getData($result);
if (defined('FAS_PRUEFUNG_BEI_NOTENEINGABE_ANLEGEN') && FAS_PRUEFUNG_BEI_NOTENEINGABE_ANLEGEN
&& $anwesenheit && (float)current($anwesenheit)->prozent < FAS_ANWESENHEIT_ROT) {
if ($anwesenheit && (float)current($anwesenheit)->prozent < FAS_ANWESENHEIT_ROT) {
// Get Anwesenheitsbefreiung
$this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
$result = $this->BenutzerfunktionModel->getBenutzerFunktionByUidInStdsem($student_uid, $studiensemester_kurzbz, 'awbefreit');
@@ -567,10 +545,7 @@ class Grades extends FHCAPI_Controller
$anwesenheitsbefreit = hasData($result);
// Bei zu geringer Anwesenheit ohne Befreiung wird ein zusätzlicher Termin
// 'Nicht beurteilt' vor dem eigentlichen Antritt festgehalten. ACHTUNG: diese Note
// zählt nicht mehr als Antritt (NOTEN_OHNE_ANTRITT_BEZEICHNUNGEN) - der frühere
// "Antritt abziehen"-Effekt entfällt damit, der Eintrag ist reine Dokumentation.
// Wenn nicht Anwesenheitsbefreit und Anwesenheit unter einem bestimmten Prozentsatz fällt dann wird ein Pruefungsantritt abgezogen
if (!$anwesenheitsbefreit) {
$data2 = $data;
$data2["note"] = 7;
@@ -581,6 +556,8 @@ class Grades extends FHCAPI_Controller
if (isError($result))
return $result;
$data["pruefungstyp_kurzbz"] = "Termin2";
}
}
@@ -574,19 +574,6 @@ class Kontakt extends FHCAPI_Controller
$person_id = $this->input->post('person_id');
$standort_id = $this->input->post('standort_id');
//check person_id: allow editing only of persons who are no mitarbeiter
if($person_id)
{
$this->load->model('person/Benutzer_model', 'BenutzerModel');
$this->BenutzerModel->addJoin('public.tbl_mitarbeiter ma', 'ON (ma.mitarbeiter_uid = public.tbl_benutzer.uid)');
$result = $this->BenutzerModel->loadWhere([
'person_id' => $person_id
]);
if(hasData($result))
return $this->terminateWithError($this->p->t('ui', 'editingMitarbeiterPVonly'), self::ERROR_TYPE_GENERAL);
}
$result = $this->KontaktModel->update(
[
'kontakt_id' => $kontakt_id
@@ -13,7 +13,6 @@ class Mylv extends Auth_Controller
*/
public function __construct()
{
parent::__construct([
'Student' => self::PERM_LOGGED,
'Studiensemester' => self::PERM_LOGGED,
@@ -45,27 +44,13 @@ 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');
$isMitarbeiter = getData($this->MitarbeiterModel->isMitarbeiter(getAuthUID())) ?? false;
if($isMitarbeiter) {
$result = $this->StudiensemesterModel->getWhereMitarbeiterHasLvs(getAuthUID());
$result = $this->StudiensemesterModel->getWhereStudentHasLvs(getAuthUID());
if (isError($result))
return $this->outputJsonError(getError($result));
if (isError($result))
return $this->outputJsonError(getError($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');
}
$this->outputJsonSuccess(getData($result));
}
/**
@@ -73,27 +58,13 @@ 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');
$isMitarbeiter = getData($this->MitarbeiterModel->isMitarbeiter(getAuthUID())) ?? false;
if($isMitarbeiter) {
$result = $this->LehrveranstaltungModel->getLvsByMitarbeiterInSemester(getAuthUID(), $studiensemester_kurzbz);
$result = $this->LehrveranstaltungModel->getLvsByStudentWithGrades(getAuthUID(), $studiensemester_kurzbz, getUserLanguage());
if (isError($result))
return $this->outputJsonError(getError($result));
if (isError($result))
return $this->outputJsonError(getError($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');
}
$this->outputJsonSuccess(getData($result));
}
/**
@@ -215,11 +215,8 @@ 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
View File
@@ -82,21 +82,6 @@ function generateCSSsInclude($CSSs)
}
}
/**
* Generates tags for the style sheets you want to include, the parameter could by a string or an array of strings
*/
function generatePrintCSS($path)
{
$cssLink = '<link rel="stylesheet" media="print" href="%s" />';
$ci =& get_instance();
$cachetoken = '?'.$ci->config->item('fhcomplete_build_version');
if (isset($path)) {
echo sprintf($cssLink, base_url($path).$cachetoken) . PHP_EOL;
}
}
/**
* Generates global JS-Object to pass parms to other javascripts
*/
+563
View File
@@ -0,0 +1,563 @@
<?php
if (!defined('BASEPATH'))
exit('No direct script access allowed');
class CmsAdminLib
{
private $ci;
public function __construct()
{
$this->ci =& get_instance();
$this->ci->load->model('content/Content_model', 'ContentModel');
$this->ci->load->model('content/Contentsprache_model', 'ContentspracheModel');
$this->ci->load->model('content/Contentchild_model', 'ContentchildModel');
$this->ci->load->model('content/Contentgruppe_model', 'ContentgruppeModel');
$this->ci->load->model('content/Contentlog_model', 'ContentlogModel');
$this->ci->load->model('content/Template_model', 'TemplateModel');
$this->ci->load->library('XsdSchemaLib');
$this->ci->load->library('PermissionLib');
$this->ci->config->load('cms');
}
/**
* @return object success(array) entitled oe_kurzbz values
*/
public function getEntitledOe()
{
$oe = $this->ci->permissionlib->getOE_isEntitledFor('basis/cms');
if ($oe === false || !is_array($oe))
return success([]);
return success($oe);
}
/**
* @param int $content_id
* @return object success(bool)
*/
public function isEntitledForContent($content_id)
{
$oeResult = $this->ci->ContentModel->getOeKurzbz($content_id);
if (isError($oeResult))
return $oeResult;
$entitledResult = $this->getEntitledOe();
if (isError($entitledResult))
return $entitledResult;
return success(in_array(getData($oeResult), getData($entitledResult)));
}
/**
* @return object success(string) or error
*/
public function getDefaultOe()
{
$default = $this->ci->config->item('default_oe_kurzbz');
$entitledResult = $this->getEntitledOe();
if (isError($entitledResult))
return $entitledResult;
$entitled = getData($entitledResult);
if (empty($entitled))
return error($this->ci->p->t('cms', 'keineBerechtigteOe'));
if (in_array($default, $entitled))
return success($default);
return success($entitled[0]);
}
/**
* @param int|null $parent_content_id
* @return object success(array) with content_id, sprache, version
*/
public function createContent($parent_content_id = null)
{
$templatesResult = $this->ci->TemplateModel->loadWhere([]);
if (isError($templatesResult))
return $templatesResult;
$templates = getData($templatesResult);
if (empty($templates))
return error($this->ci->p->t('cms', 'keineVorlageVorhanden'));
$template = $templates[0];
$oeResult = $this->getDefaultOe();
if (isError($oeResult))
return $oeResult;
$oe = getData($oeResult);
$titel = $this->ci->p->t('cms', 'neuerEintrag');
$now = date('Y-m-d H:i:s');
$uid = getAuthUID();
$this->ci->db->trans_start();
$contentResult = $this->ci->ContentModel->insert([
'template_kurzbz' => $template->template_kurzbz,
'oe_kurzbz' => $oe,
'aktiv' => true,
'menu_open' => false,
'beschreibung' => '',
'insertamum' => $now,
'insertvon' => $uid
]);
if (isError($contentResult))
return $contentResult;
$content_id = getData($contentResult);
$spracheResult = $this->ci->ContentspracheModel->insert([
'content_id' => $content_id,
'sprache' => DEFAULT_LANGUAGE,
'version' => 1,
'sichtbar' => true,
'titel' => $titel,
'content' => '<?xml version="1.0" encoding="UTF-8" ?><content></content>',
'insertamum' => $now,
'insertvon' => $uid
]);
if (isError($spracheResult))
return $spracheResult;
if ($parent_content_id !== null)
{
$maxSortResult = $this->ci->ContentchildModel->getMaxSort($parent_content_id);
if (isError($maxSortResult))
return $maxSortResult;
$childResult = $this->ci->ContentchildModel->insert([
'content_id' => $parent_content_id,
'child_content_id' => $content_id,
'sort' => getData($maxSortResult) + 1,
'insertamum' => $now,
'insertvon' => $uid
]);
if (isError($childResult))
return $childResult;
}
$this->ci->db->trans_complete();
if ($this->ci->db->trans_status() === false)
return error($this->ci->p->t('cms', 'speichernFehlgeschlagen'));
return success([
'content_id' => $content_id,
'sprache' => DEFAULT_LANGUAGE,
'version' => 1
]);
}
/**
* @param int $content_id
* @param string $sprache source language
* @param int $version source version
* @param string $neueSprache target language
* @return object success(array) with contentsprache_id
*/
public function createTranslation($content_id, $sprache, $version, $neueSprache)
{
$existsResult = $this->ci->ContentspracheModel->exists($content_id, $neueSprache);
if (isError($existsResult))
return $existsResult;
if (getData($existsResult))
return error($this->ci->p->t('cms', 'uebersetzungExistiert'));
$sourceResult = $this->ci->ContentspracheModel->getOne($content_id, $sprache, $version);
if (isError($sourceResult))
return $sourceResult;
$source = getData($sourceResult);
$now = date('Y-m-d H:i:s');
$uid = getAuthUID();
$insertResult = $this->ci->ContentspracheModel->insert([
'content_id' => $content_id,
'sprache' => $neueSprache,
'version' => $source->version,
'sichtbar' => true,
'titel' => $source->titel,
'content' => $source->content,
// The review feature is gone from this admin, but the columns stay
'reviewvon' => null,
'reviewamum' => null,
'gesperrt_uid' => null,
'insertamum' => $now,
'insertvon' => $uid,
'updateamum' => $now,
'updatevon' => $uid
]);
if (isError($insertResult))
return $insertResult;
return success(['contentsprache_id' => getData($insertResult)]);
}
/**
* @param int $content_id
* @param string $sprache
* @return object success(array) with version
*/
public function createVersion($content_id, $sprache)
{
$sourceResult = $this->ci->ContentspracheModel->getOne($content_id, $sprache, null);
if (isError($sourceResult))
return $sourceResult;
$source = getData($sourceResult);
$maxResult = $this->ci->ContentspracheModel->getMaxVersion($content_id, $sprache);
if (isError($maxResult))
return $maxResult;
$newVersion = getData($maxResult) + 1;
$now = date('Y-m-d H:i:s');
$uid = getAuthUID();
$insertResult = $this->ci->ContentspracheModel->insert([
'content_id' => $content_id,
'sprache' => $sprache,
'version' => $newVersion,
'sichtbar' => false,
'titel' => $source->titel,
'content' => $source->content,
// The review feature is gone from this admin, but the columns stay
'reviewvon' => null,
'reviewamum' => null,
'gesperrt_uid' => null,
'insertamum' => $now,
'insertvon' => $uid,
'updateamum' => $now,
'updatevon' => $uid
]);
if (isError($insertResult))
return $insertResult;
return success(['version' => $newVersion]);
}
/**
* Writes the properties of one content version.
* @param array $daten content_id, sprache, version, template_kurzbz, oe_kurzbz,
* aktiv, menu_open, beschreibung, titel, sichtbar
* @return object success(true) or error
*/
public function saveProperties($daten)
{
$versionResult = $this->ci->ContentspracheModel->getOne(
$daten['content_id'], $daten['sprache'], $daten['version']
);
if (isError($versionResult))
return $versionResult;
$row = getData($versionResult);
$now = date('Y-m-d H:i:s');
$uid = getAuthUID();
$this->ci->db->trans_start();
$this->ci->ContentModel->update($daten['content_id'], [
'template_kurzbz' => $daten['template_kurzbz'],
'oe_kurzbz' => $daten['oe_kurzbz'],
'aktiv' => $daten['aktiv'],
'menu_open' => $daten['menu_open'],
'beschreibung' => $daten['beschreibung'],
'updateamum' => $now,
'updatevon' => $uid
]);
$this->ci->ContentspracheModel->update($row->contentsprache_id, [
'titel' => $daten['titel'],
'sichtbar' => $daten['sichtbar'],
'updateamum' => $now,
'updatevon' => $uid
]);
$this->ci->db->trans_complete();
if ($this->ci->db->trans_status() === false)
return error($this->ci->p->t('cms', 'speichernFehlgeschlagen'));
return success(true);
}
// legacy cms checks neither the permission
// type nor the lock. The form appears only when the user holds the lock, but the POST
// itself is unprotected.
/**
* @param int $content_id
* @param string $sprache
* @param int $version
* @param array $values field name => value
* @return object success(true) or error
*/
public function saveContentXml($content_id, $sprache, $version, $values)
{
$versionResult = $this->ci->ContentspracheModel->getOne($content_id, $sprache, $version);
if (isError($versionResult))
return $versionResult;
$row = getData($versionResult);
$contentResult = $this->ci->ContentModel->load($content_id);
if (isError($contentResult))
return $contentResult;
$content = getData($contentResult);
if (empty($content))
return error($this->ci->p->t('cms', 'contentNichtGefunden'));
$templateResult = $this->ci->TemplateModel->load($content[0]->template_kurzbz);
if (isError($templateResult))
return $templateResult;
$template = getData($templateResult);
if (empty($template))
return error($this->ci->p->t('cms', 'keineVorlageVorhanden'));
$schemaResult = $this->ci->xsdschemalib->parseSchema(
$template[0]->xsd, $content[0]->template_kurzbz
);
if (isError($schemaResult))
return $schemaResult;
$xmlResult = $this->ci->xsdschemalib->buildXml(getData($schemaResult), $values);
if (isError($xmlResult))
return $xmlResult;
$updateResult = $this->ci->ContentspracheModel->update(
$row->contentsprache_id,
['content' => getData($xmlResult)]
);
if (isError($updateResult))
return $updateResult;
return success(true);
}
/**
* Locks a version for the current user, or takes over an expired lock.
* @param int $contentsprache_id
* @return object success(true) or error
*/
public function lock($contentsprache_id)
{
$uid = getAuthUID();
$rowResult = $this->ci->ContentspracheModel->load($contentsprache_id);
if (isError($rowResult))
return $rowResult;
$rowData = getData($rowResult);
if (empty($rowData))
return error($this->ci->p->t('cms', 'versionNichtGefunden'));
$holder = ($rowData[0]->gesperrt_uid === '') ? null : $rowData[0]->gesperrt_uid;
if ($holder !== null && $holder !== $uid)
{
$openResult = $this->ci->ContentlogModel->getOpenLock($contentsprache_id, $holder);
$openEntry = (!isError($openResult)) ? getData($openResult) : null;
if (!$this->isLockExpired($holder, $openEntry))
return error($this->ci->p->t('cms', 'bereitsGesperrt'));
}
$this->ci->db->trans_start();
$this->ci->ContentlogModel->closeOpenEntries($contentsprache_id);
$logResult = $this->ci->ContentlogModel->insert([
'uid' => $uid,
'contentsprache_id' => $contentsprache_id,
'start' => date('Y-m-d H:i:s')
]);
if (isError($logResult))
return $logResult;
$updateResult = $this->ci->ContentspracheModel->update($contentsprache_id, [
'gesperrt_uid' => $uid
]);
if (isError($updateResult))
return $updateResult;
$this->ci->db->trans_complete();
if ($this->ci->db->trans_status() === false)
return error($this->ci->p->t('cms', 'speichernFehlgeschlagen'));
return success(true);
}
// legacy released ALL locks of the user. Releasing now ends one lock.
// The uid predicate stops a user releasing a lock of somebody else.
/**
* Releases the lock of the current user on one version.
* @param int $contentsprache_id
* @return object success(true)
*/
public function releaseOwnLock($contentsprache_id)
{
$uid = getAuthUID();
$this->ci->db->trans_start();
$this->ci->ContentlogModel->closeOpenEntries($contentsprache_id, $uid);
$this->ci->ContentspracheModel->releaseLock($contentsprache_id, $uid);
$this->ci->db->trans_complete();
if ($this->ci->db->trans_status() === false)
return error($this->ci->p->t('cms', 'speichernFehlgeschlagen'));
return success(true);
}
/**
* @param int $contentsprache_id
* @return object success(true)
*/
public function forceRelease($contentsprache_id)
{
$this->ci->db->trans_start();
$this->ci->ContentlogModel->closeOpenEntries($contentsprache_id);
$this->ci->ContentspracheModel->releaseLock($contentsprache_id);
$this->ci->db->trans_complete();
if ($this->ci->db->trans_status() === false)
return error($this->ci->p->t('cms', 'speichernFehlgeschlagen'));
return success(true);
}
/**
* @param int $content_id
* @param string $sprache
* @param int $version
* @return object success(array) gesperrt_uid, start, expires, own, expired, may_force
*/
public function getLockState($content_id, $sprache, $version)
{
$versionResult = $this->ci->ContentspracheModel->getOne($content_id, $sprache, $version);
if (isError($versionResult))
return $versionResult;
$row = getData($versionResult);
// legacy admin.php inserts an empty string, releasing writes NULL.
// this code reports NULL.
$gesperrt_uid = ($row->gesperrt_uid === '') ? null : $row->gesperrt_uid;
$logEntry = null;
if ($gesperrt_uid !== null)
{
$lockResult = $this->ci->ContentlogModel->getOpenLock(
$row->contentsprache_id, $gesperrt_uid
);
$logEntry = (!isError($lockResult)) ? getData($lockResult) : null;
}
return success([
'contentsprache_id' => (int) $row->contentsprache_id,
'gesperrt_uid' => $gesperrt_uid,
'start' => $logEntry ? $logEntry->start : null,
'own' => ($gesperrt_uid !== null && $gesperrt_uid === getAuthUID()),
'expired' => $this->isLockExpired($gesperrt_uid, $logEntry),
'expires' => $this->lockExpiryDate($logEntry),
'may_force' => $this->ci->permissionlib->isBerechtigt('basis/cms_sperrfreigabe', 'su')
]);
}
/**
* End of the lock window, null if the entry carries no start.
* @param stdClass|null $logEntry
* @return string|null
*/
private function lockExpiryDate($logEntry)
{
if ($logEntry === null || empty($logEntry->start))
return null;
$ttlHours = (int) $this->ci->config->item('lock_ttl_hours');
// lock() writes start with PHP time, so stay on PHP time.
return date('Y-m-d H:i:s', strtotime($logEntry->start) + $ttlHours * 3600);
}
// Locks now age out after lock_ttl_hours.
/**
* A lock without a log entry has no age and counts as expired.
* @param string|null $gesperrt_uid
* @param stdClass|null $logEntry
* @return bool
*/
private function isLockExpired($gesperrt_uid, $logEntry)
{
if ($gesperrt_uid === null)
return false;
$expires = $this->lockExpiryDate($logEntry);
return $expires === null || strtotime($expires) < time();
}
/**
* @param int $content_id
* @return object success(true) or error
*/
public function deleteContent($content_id)
{
$this->ci->db->trans_start();
$this->ci->ContentchildModel->deleteByContent($content_id);
$this->ci->ContentlogModel->deleteByContent($content_id);
$this->ci->ContentspracheModel->deleteByContent($content_id);
$this->ci->ContentgruppeModel->deleteByContent($content_id);
$this->ci->ContentModel->delete($content_id);
$this->ci->db->trans_complete();
if ($this->ci->db->trans_status() === false)
return error($this->ci->p->t('cms', 'loeschenNichtMoeglichReferenzen'));
return success(true);
}
/**
* @param int $content_id
* @param string $sprache
* @param int $version
* @return object success(true) or error
*/
public function deleteVersion($content_id, $sprache, $version)
{
if ($sprache === DEFAULT_LANGUAGE)
{
$countResult = $this->ci->ContentspracheModel->getNumberOfVersions($content_id, $sprache);
if (isError($countResult))
return $countResult;
if (getData($countResult) === 1)
return error($this->ci->p->t('cms', 'letzteVersionNichtLoeschbar'));
}
$versionResult = $this->ci->ContentspracheModel->getOne($content_id, $sprache, $version);
if (isError($versionResult))
return $versionResult;
$row = getData($versionResult);
$this->ci->db->trans_start();
$this->ci->ContentlogModel->deleteByContentsprache($row->contentsprache_id);
$this->ci->ContentspracheModel->deleteVersion($content_id, $sprache, $version);
$this->ci->db->trans_complete();
if ($this->ci->db->trans_status() === false)
return error($this->ci->p->t('cms', 'loeschenFehlgeschlagen'));
return success(true);
}
}
+97 -77
View File
@@ -4,7 +4,6 @@ if (!defined('BASEPATH'))
exit('No direct script access allowed');
use \DateTime as DateTime;
use \DateInterval as DateInterval;
use \DOMDocument as DOMDocument;
use \XSLTProcessor as XSLTProcessor;
@@ -43,10 +42,12 @@ class CmsLib
* @param int $version
* @param string $sprache
* @param boolean $sichtbar
* @param boolean $log false suppresses the click log entry. The CMS admin
* preview renders a content without counting a view.
*
* @return stdClass
*/
public function getContent($content_id, $version = null, $sprache = null, $sichtbar = true)
public function getContent($content_id, $version = null, $sprache = null, $sichtbar = true, $log = true)
{
if (!is_numeric($content_id))
return error('ContentID ist ungueltig');
@@ -73,7 +74,7 @@ class CmsLib
return $content;
// Legt einen Logeintrag für die Klickstatistik an
if (defined('LOG_CONTENT') && LOG_CONTENT) {
if ($log && defined('LOG_CONTENT') && LOG_CONTENT) {
// Nur eingeloggte User werden geloggt, das sonst auch alle Infoscreenaufrufe und dgl. mitgeloggt werden
if (isLogged()) {
$request_data = 'content_id=' . $content_id;
@@ -142,6 +143,63 @@ class CmsLib
]);
}
/**
* @param stdClass $stg_obj
*
* @return stdClass
*/
protected function getNewsExtras($stg_obj, $semester)
{
$this->ci->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
$stg_ltg = $this->ci->StudiengangModel->getLeitungDetailed($stg_obj->studiengang_kz);
if (isError($stg_ltg))
return $stg_ltg;
$stg_ltg = getData($stg_ltg) ?: [];
$gf_ltg = $this->ci->BenutzerfunktionModel->getBenutzerFunktionenDetailed('gLtg', $stg_obj->oe_kurzbz);
if (isError($gf_ltg))
return $gf_ltg;
$gf_ltg = getData($gf_ltg) ?: [];
$stv_ltg = $this->ci->BenutzerfunktionModel->getBenutzerFunktionenDetailed('stvLtg', $stg_obj->oe_kurzbz);
if (isError($stv_ltg))
return $stv_ltg;
$stv_ltg = getData($stv_ltg) ?: [];
$ass = $this->ci->BenutzerfunktionModel->getBenutzerFunktionenDetailed('ass', $stg_obj->oe_kurzbz);
if (isError($ass))
return $ass;
$ass = getData($ass) ?: [];
$hochschulvertr = $this->ci->BenutzerfunktionModel->getBenutzerFunktionenDetailed('hsv');
if (isError($hochschulvertr))
return $hochschulvertr;
$hochschulvertr = getData($hochschulvertr) ?: [];
$stdv = $this->ci->BenutzerfunktionModel->getBenutzerFunktionenDetailed('stdv', $stg_obj->oe_kurzbz);
if (isError($stdv))
return $stdv;
$stdv = getData($stdv) ?: [];
$jahrgangsvertr = $this->ci->BenutzerfunktionModel->getBenutzerFunktionenDetailed('jgv', $stg_obj->oe_kurzbz, $semester);
if (isError($jahrgangsvertr))
return $jahrgangsvertr;
$jahrgangsvertr = getData($jahrgangsvertr) ?: [];
return success($this->ci->load->view('Cis/Cms/News/Xml/NewsExtras', [
'studiengang' => $stg_obj,
'semester' => $semester,
'stg_ltg' => $stg_ltg,
'gf_ltg' => $gf_ltg,
'stv_ltg' => $stv_ltg,
'ass' => $ass,
'hochschulvertr' => $hochschulvertr,
'stdv' => $stdv,
'jahrgangsvertr' => $jahrgangsvertr
], true));
}
/**
* @param string $studiengang_kz
* @param string $semester
@@ -177,102 +235,64 @@ class CmsLib
*
* @return void
*/
public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true, $page = 1, $page_size = 10, $sprache, $filterForDegreePrograms = false, $allowedDegreePrograms = [], $active = true)
public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true, $page = 1, $page_size = 10, $sprache)
{
$this->ci->load->model('organisation/Studiengang_model', 'StudiengangModel');
list($studiengang_kz, $semester) = $this->getStgAndSem($studiengang_kz, $semester);
$all = $edit;
$xml = '<?xml version="1.0" encoding="UTF-8"?><content>';
$this->ci->load->model('content/News_model', 'NewsModel');
$news = $this->ci->NewsModel->getNewsWithContent($sprache, $studiengang_kz, $semester, null, $sichtbar, $page, $page_size, $active, $mischen, $filterForDegreePrograms, $allowedDegreePrograms);
$news = getData($news) ?? [];
$newsWrappers = [];
$news = $this->ci->NewsModel->getNewsWithContent($sprache, $studiengang_kz, $semester, null, $sichtbar, 0, $page, $page_size, $all, $mischen);
if (isError($news))
return $news;
$news = getData($news);
foreach ($news as $newsobj) {
if ($studiengang_kz && $edit && !$newsobj->studiengang_kz)
continue;
$newsWrappers[] = $this->createNewsWrapper($newsobj, $edit);
$date = new DateTime($newsobj->datum);
$datum = '<datum><![CDATA[' . $date->format('d.m.Y') . ']]></datum>';
$datum .= '<datumdetail><![CDATA[' . $date->format('Y-m-d H:i') . ']]></datumdetail>';
$id = $edit ? '<news_id><![CDATA[' . $newsobj->news_id . ']]></news_id>' : '';
$xml .= "<newswrapper>" . $newsobj->content . $datum . $id . "</newswrapper>";
}
$content = $this->createNewsContent($newsWrappers, $infoscreen, $titel);
if (isError($content))
return $content;
/* if ($studiengang_kz != 0) {
$stg_obj = $this->ci->StudiengangModel->load($studiengang_kz);
if (isError($stg_obj))
return $stg_obj;
$stg_obj = current(getData($stg_obj) ?: []);
return success([
"content" => getData($content),
"row_count" => $news[0]->row_count ?? 0
]);
}
/**
* Creates a newswrapper XML element from a news item.
*
* @param stdClass $newsobj
* @param boolean $edit
*
* @return DOMElement
*/
public function createNewsWrapper($newsobj, $edit)
{
$id = $edit ? '<news_id><![CDATA[' . $newsobj->news_id . ']]></news_id>' : '';
$isPublished = $edit ? '<is_published><![CDATA[' . ($newsobj->sichtbar ? 'true' : 'false') . ']]></is_published>' : '';
$date = new DateTime($newsobj->datum);
$datum = '<datum><![CDATA[' . $date->format('d.m.Y') . ']]></datum>';
$datum .= '<datumdetail><![CDATA[' . $date->format('Y-m-d H:i') . ']]></datumdetail>';
$hasVisibleToDate = isset($newsobj->datum_bis) && $newsobj->datum_bis !== '';
$datumTo = $hasVisibleToDate
? new DateTime($newsobj->datum_bis)
: (clone $date)->add(new DateInterval('P' . MAXNEWSALTER . 'D'));
if ($hasVisibleToDate)
$datumTo->setTime(23, 59, 59);
$now = new DateTime();
$isActive = '<is_active><![CDATA[' . ($datumTo >= $now ? 'true' : 'false') . ']]></is_active>';
$newsWrapper = new DOMDocument();
$newsWrapper->loadXML('<newswrapper>' . $newsobj->content . $datum . $id . $isPublished . $isActive . '</newswrapper>');
return $newsWrapper->documentElement;
}
/**
* Creates rendered news content from one or more newswrapper XML elements.
*
* @param DOMElement|array $newsWrappers
* @param boolean $infoscreen
* @param string $titel
*
* @return stdClass
*/
public function createNewsContent($newsWrappers, $infoscreen, $titel)
{
if (!is_array($newsWrappers))
$newsWrappers = [$newsWrappers];
$XML = new DOMDocument('1.0', 'UTF-8');
$contentElement = $XML->createElement('content');
$XML->appendChild($contentElement);
foreach ($newsWrappers as $newsWrapper)
$contentElement->appendChild($XML->importNode($newsWrapper, true));
if ($stg_obj) {
if (!$edit && !$infoscreen) {
$extras = $this->getNewsExtras($stg_obj, $semester);
if (isError($extras))
return $extras;
$xml .= getData($extras);
}
$xml .= '<studiengang_bezeichnung><![CDATA[' . $stg_obj->bezeichnung . ']]></studiengang_bezeichnung>';
}
} */
if ($titel != '') {
$titleElement = $XML->createElement('news_titel');
$titleElement->appendChild($XML->createTextNode($titel));
$contentElement->appendChild($titleElement);
$xml .= '<news_titel>' . $titel . '</news_titel>';
}
$xml .= '</content>';
//XSLT Vorlage laden
$template = $this->ci->TemplateModel->load($infoscreen ? 'news_infoscreen' : 'news');
if (isError($template))
return $template;
$template = current(getData($template));
$XML = new DOMDocument();
$XML->loadXML($xml);
$xsltemplate = new DOMDocument();
$xsltemplate->loadXML($template->xslt_xhtml_c4);
@@ -1,410 +0,0 @@
<?php
/**
* Copyright (C) 2026 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
* The exam history of one student in one course: the order of the attempts, the attempt count
* and the limits. This class is the only source of the examination rules. The controller and the
* client only read the result.
*/
class PruefungsverlaufLib
{
/** The first attempt: the original assessment, which is the transcript grade itself. */
const ROLLE_ERSTANTRITT = 'erstantritt';
/** Each attempt after the first one. */
const ROLLE_PRUEFUNG = 'pruefung';
/** The last attempt. The examination rules require a commission for it. */
const ROLLE_KOMMISSIONELL = 'kommissionell';
/** Maps an attempt number to the old exam type. Kept for backward compatibility. */
private static $_legacyTypen = [1 => 'Termin1', 2 => 'Termin2', 3 => 'Termin3'];
private $_ci;
private $_noteCache = [];
private $_specialNotes = null;
private $_typen = null;
public function __construct()
{
$this->_ci =& get_instance();
$this->_ci->load->model('education/LePruefung_model', 'LePruefungModel');
$this->_ci->load->model('education/Note_model', 'NoteModel');
$this->_ci->load->model('education/Pruefungstyp_model', 'PruefungstypModel');
$this->_ci->load->config('noten');
}
/**
* @param mixed $lvNote counts as the implicit first attempt while no exam counts
* @param mixed $zeugnisNote a credited grade makes all exams impossible
* @return stdClass
*/
public function getVerlauf($student_uid, $lv_id, $sem_kurzbz, $lvNote = null, $zeugnisNote = null)
{
return $this->buildVerlauf($this->getPruefungen($student_uid, $lv_id, $sem_kurzbz), $lvNote, $zeugnisNote);
}
/** All exams of one student in one course, in chronological order. @return array */
public function getPruefungen($student_uid, $lv_id, $sem_kurzbz)
{
$result = $this->_ci->LePruefungModel->getPruefungenByUidTypLvStudiensemester($student_uid, null, $lv_id, $sem_kurzbz);
if (isError($result) || !hasData($result)) return [];
return $this->sortPruefungen(getData($result));
}
/** Sort keys: datum, then tbl_pruefungstyp.sort (old rows without a date), then pruefung_id. */
public function sortPruefungen($pruefungen)
{
$pruefungen = array_values($pruefungen);
$typen = $this->getTypen();
usort($pruefungen, function ($a, $b) use ($typen) {
$da = substr((string) $a->datum, 0, 10);
$db = substr((string) $b->datum, 0, 10);
if ($da !== $db) {
if ($da === '') return -1;
if ($db === '') return 1;
return ($da < $db) ? -1 : 1;
}
$sa = isset($typen[$a->pruefungstyp_kurzbz]) ? (int) $typen[$a->pruefungstyp_kurzbz]->sort : 0;
$sb = isset($typen[$b->pruefungstyp_kurzbz]) ? (int) $typen[$b->pruefungstyp_kurzbz]->sort : 0;
if ($sa !== $sb) return ($sa < $sb) ? -1 : 1;
return ((int) $a->pruefung_id < (int) $b->pruefung_id) ? -1 : 1;
});
return $pruefungen;
}
/**
* Builds the history from a list of exams. Each exam gets: position, zaehlt, antritt_nr (null
* if the exam does not count) and terminal.
*
* @return stdClass
*/
public function buildVerlauf($pruefungen, $lvNote = null, $zeugnisNote = null)
{
$pruefungen = $this->sortPruefungen($pruefungen);
$antrittCount = 0;
$terminal = false;
$eintraege = [];
foreach ($pruefungen as $i => $p) {
$zaehlt = $this->zaehltAlsAntritt($p->note, $p->pruefungstyp_kurzbz);
if ($zaehlt) $antrittCount++;
// A kommissionelle exam closes the chain, also if its grade uses no attempt. A student
// with a kommissionelle Prüfung gets no further exam from this tool.
$kommissionell = $this->istKommissionell($p->pruefungstyp_kurzbz);
if ($kommissionell) $terminal = true;
$eintrag = clone $p;
$eintrag->position = $i + 1;
$eintrag->zaehlt = $zaehlt;
$eintrag->antritt_nr = $zaehlt ? $antrittCount : null;
$eintrag->kommissionell = $kommissionell;
$eintraege[] = $eintrag;
}
// if no exam counts, the course grade itself is the first attempt
$implizit = ($antrittCount === 0 && $lvNote !== null && $this->zaehltAlsAntritt($lvNote));
if ($implizit) $antrittCount = 1;
$maxAntritte = $this->getMaxAntritte();
// credited: the row stays visible, but you cannot enter anything
$angerechnet = $this->istAnrechnungsnote($zeugnisNote);
$verlauf = new stdClass();
$verlauf->pruefungen = $eintraege;
$verlauf->antrittCount = $antrittCount;
$verlauf->maxAntritte = $maxAntritte;
$verlauf->terminal = $terminal;
$verlauf->angerechnet = $angerechnet;
$verlauf->impliziterErstantritt = $implizit;
$verlauf->canAdd = !$terminal && !$angerechnet && $antrittCount < $maxAntritte;
// after the first exam each new entry is a repeat attempt
$verlauf->erstantrittMoeglich = !$angerechnet && (count($eintraege) === 0);
// The last attempt of the chain is kommissionell (§17 Abs 1). The role therefore follows
// from the position, and the user interface offers no type to select.
if (count($eintraege) === 0 && !$implizit) {
$verlauf->naechsteRolle = self::ROLLE_ERSTANTRITT;
} elseif (($antrittCount + 1) >= $maxAntritte) {
$verlauf->naechsteRolle = self::ROLLE_KOMMISSIONELL;
} else {
$verlauf->naechsteRolle = self::ROLLE_PRUEFUNG;
}
return $verlauf;
}
/** 'angerechnet' or 'intern angerechnet'. The TRANSCRIPT grade decides. @return bool */
public function istAnrechnungsnote($note)
{
if ($note === null || $note === '') return false;
return in_array($note, $this->getSpecialNotes()['anrechnung']);
}
/**
* All attempts that count, the kommissionelle attempt included. The explicit configuration
* wins. Without it the number comes from the old flags: the original assessment, one attempt
* for each active repeat, and the kommissionelle attempt.
*
* @return int
*/
public function getMaxAntritte()
{
$max = $this->_ci->config->item('CIS_GESAMTNOTE_MAX_ANTRITTE');
if (is_numeric($max) && (int) $max > 0) return (int) $max;
$max = 1;
if (defined('CIS_GESAMTNOTE_PRUEFUNG_TERMIN2') && CIS_GESAMTNOTE_PRUEFUNG_TERMIN2) $max++;
if (defined('CIS_GESAMTNOTE_PRUEFUNG_TERMIN3') && CIS_GESAMTNOTE_PRUEFUNG_TERMIN3) $max++;
if (defined('CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF') && CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF) $max++;
return $max;
}
/**
* The NOTE decides, therefore the same type can occur more than one time. The type decides in
* one case only: a type in PRUEFUNG_TYPEN_OHNE_ANTRITT never uses an attempt.
*
* @return bool
*/
public function zaehltAlsAntritt($note, $typ = null)
{
if ($typ !== null && in_array($typ, $this->getTypenOhneAntritt())) return false;
if ($note === null || $note === '') return false;
if (in_array($note, $this->getSpecialNotes()['ohneAntritt'])) return false;
// a credited grade is not an assessment, therefore it uses no attempt
if ($this->istAnrechnungsnote($note)) return false;
$noteRow = $this->getNote($note);
return $noteRow ? (bool) $noteRow->lehre : false;
}
/** Tells you if an exam takes place before a commission. @return bool */
public function istKommissionell($typ)
{
$typen = $this->_ci->config->item('PRUEFUNG_KOMMISSIONELL_TYPEN');
if (!is_array($typen)) $typen = ['kommPruef', 'zusKommPruef'];
return in_array($typ, $typen);
}
/** The exam types that never use an attempt. @return array */
public function getTypenOhneAntritt()
{
$typen = $this->_ci->config->item('PRUEFUNG_TYPEN_OHNE_ANTRITT');
return is_array($typen) ? $typen : ['zusKommPruef'];
}
/**
* The type for an attempt number. The student administration still reads this column, so the
* value must be correct. The last attempt gets the kommissionelle type, the attempts before it
* get the old Termin types.
*
* @return string
*/
public function legacyTypFuerAntritt($antrittNr)
{
$antrittNr = max(1, (int) $antrittNr);
$typen = $this->getTypen();
$maxAntritte = $this->getMaxAntritte();
$kommissionell = $this->_ci->config->item('PRUEFUNG_TYP_KOMMISSIONELL');
if (!is_string($kommissionell) || $kommissionell === '') $kommissionell = 'kommPruef';
if ($maxAntritte > 1 && $antrittNr >= $maxAntritte && isset($typen[$kommissionell])) {
return $kommissionell;
}
for ($n = $antrittNr; $n >= 1; $n--) {
if (isset(self::$_legacyTypen[$n]) && isset($typen[self::$_legacyTypen[$n]])) {
return self::$_legacyTypen[$n];
}
}
return self::$_legacyTypen[1];
}
/**
* The old type for a new repeat attempt. It is never lower than Termin2, also if the first
* exam does not use an attempt.
*
* @return string
*/
public function legacyTypFuerWiederholung($verlauf)
{
return $this->legacyTypFuerAntritt(max(2, $verlauf->antrittCount + 1));
}
/** @return array{entschuldigt: mixed, ohneAntritt: array, anrechnung: array, limitMap: array} */
public function getSpecialNotes()
{
if ($this->_specialNotes !== null) return $this->_specialNotes;
$cfgEntschuldigt = $this->_ci->config->item('NOTE_ENTSCHULDIGT');
$cfgOhneAntritt = $this->_ci->config->item('NOTEN_OHNE_ANTRITT');
$cfgLimitMap = $this->_ci->config->item('NOTEN_OCCURANCE_LIMIT_MAP');
$bezeichnungen = $this->_ci->config->item('NOTEN_OHNE_ANTRITT_BEZEICHNUNGEN');
if (!is_array($cfgOhneAntritt)) $cfgOhneAntritt = [];
if (!is_array($cfgLimitMap)) $cfgLimitMap = [];
if (!is_array($bezeichnungen)) $bezeichnungen = [];
$resEnt = $this->_ci->NoteModel->getEntschuldigtNote();
$entschuldigt = (!isError($resEnt) && hasData($resEnt)) ? getData($resEnt)[0]->note : $cfgEntschuldigt;
// The name wins against the configured key. Do NOT merge the two sets: the same key has a
// different meaning in each installation (17 is 'entschuldigt' here, but 'nicht zugelassen'
// in the standard data).
$ohneAntritt = [];
foreach ($bezeichnungen as $bezeichnung) {
$note = $this->getNoteByBezeichnung($bezeichnung);
if ($note !== null && !in_array($note, $ohneAntritt)) $ohneAntritt[] = $note;
}
if (count($ohneAntritt) === 0) $ohneAntritt = $cfgOhneAntritt;
if ($entschuldigt !== null && !in_array($entschuldigt, $ohneAntritt)) $ohneAntritt[] = $entschuldigt;
// move the configured limit for 'entschuldigt' to the key that was found
$limitMap = [];
foreach ($cfgLimitMap as $k => $v) {
$limitMap[($k == $cfgEntschuldigt) ? $entschuldigt : $k] = $v;
}
$this->_specialNotes = [
'entschuldigt' => $entschuldigt,
'ohneAntritt' => array_values($ohneAntritt),
'anrechnung' => $this->resolveNoten('NOTEN_ANRECHNUNG_BEZEICHNUNGEN', 'NOTEN_ANRECHNUNG'),
'limitMap' => $limitMap
];
return $this->_specialNotes;
}
/**
* The occurrence limit from NOTEN_OCCURANCE_LIMIT_MAP. $excludePruefungId skips the record
* that the user edits at this moment.
*
* @return bool
*/
public function ueberschreitetNotenLimit($pruefungen, $note, $excludePruefungId = null)
{
$limitMap = $this->getSpecialNotes()['limitMap'];
$limit = null;
foreach ($limitMap as $limitNote => $limitVal) {
if ($limitNote == $note) { $limit = $limitVal; break; }
}
if ($limit === null) return false;
$count = 0;
foreach ($pruefungen as $p) {
if ($excludePruefungId !== null && $p->pruefung_id == $excludePruefungId) continue;
if ($p->note == $note) $count++;
}
return ($count + 1) > $limit;
}
/** An exam with a later date locks the grade. You can still correct the date. @return bool */
public function hatSpaeterenTermin($verlauf, $pruefung_id)
{
$current = null;
foreach ($verlauf->pruefungen as $p) {
if ($p->pruefung_id == $pruefung_id) { $current = $p; break; }
}
if ($current === null) return false;
foreach ($verlauf->pruefungen as $p) {
if ($p->pruefung_id == $current->pruefung_id) continue;
if ($p->position > $current->position) return true;
}
return false;
}
/**
* tbl_pruefungstyp as a map. It sorts old rows, and it limits the legacy type to the types
* that exist. A type that does not exist breaks the foreign key. Termin3 is absent from the
* standard data.
*
* @return array
*/
private function getTypen()
{
if ($this->_typen !== null) return $this->_typen;
$this->_typen = [];
$result = $this->_ci->PruefungstypModel->load();
if (!isError($result) && hasData($result)) {
foreach (getData($result) as $typ) {
$this->_typen[$typ->pruefungstyp_kurzbz] = $typ;
}
}
return $this->_typen;
}
/** Finds the grades by their name. The configured keys are the fallback only. @return array */
private function resolveNoten($bezeichnungKey, $pkKey)
{
$bezeichnungen = $this->_ci->config->item($bezeichnungKey);
if (!is_array($bezeichnungen)) $bezeichnungen = [];
$noten = [];
foreach ($bezeichnungen as $bezeichnung) {
$note = $this->getNoteByBezeichnung($bezeichnung);
if ($note !== null && !in_array($note, $noten)) $noten[] = $note;
}
if (count($noten) === 0) {
$fallback = $this->_ci->config->item($pkKey);
if (is_array($fallback)) $noten = $fallback;
}
return array_values($noten);
}
/**
* @return stdClass|null
*/
private function getNote($note)
{
if (array_key_exists($note, $this->_noteCache)) return $this->_noteCache[$note];
$result = $this->_ci->NoteModel->load($note);
$this->_noteCache[$note] = (!isError($result) && hasData($result)) ? getData($result)[0] : null;
return $this->_noteCache[$note];
}
/**
* @return mixed|null
*/
private function getNoteByBezeichnung($bezeichnung)
{
$result = $this->_ci->NoteModel->loadWhere(['bezeichnung' => $bezeichnung]);
return (!isError($result) && hasData($result)) ? getData($result)[0]->note : null;
}
}
+4 -101
View File
@@ -33,7 +33,7 @@ class SearchBarLib
const ERROR_NOT_AUTH = 'ERR005';
// List of allowed types of search
const ALLOWED_TYPES = ['mitarbeiter', 'mitarbeiter_ohne_zuordnung', 'organisationunit', 'raum', 'person', 'student','studentStv', 'prestudent', 'document', 'cms', 'lehreinheit', 'lehrveranstaltung'];
const ALLOWED_TYPES = ['mitarbeiter', 'mitarbeiter_ohne_zuordnung', 'organisationunit', 'raum', 'person', 'student','studentStv', 'prestudent', 'document', 'cms'];
const PHOTO_IMG_URL = '/cis/public/bild.php?src=person&person_id=';
@@ -180,14 +180,10 @@ class SearchBarLib
protected function buildSearchClause(DB_Model $dbModel, array $columns, $searchstr)
{
$searchstr = preg_replace('/[[:punct:]]/', ' ', $searchstr);
$words = preg_split('/\s+/', trim($searchstr), -1, PREG_SPLIT_NO_EMPTY);
if (empty($words)) return array();
$document = implode(' || \' \' || ', $columns);
$query = '\'' . implode(':* & ', $words) . ':*\'';
$reversequery = '\'*:' . implode(' & *:', $words) . '\'';
$nospacequery = '\'' . implode('', $words) . ':*\'';
$query = '\'' . implode(':* & ', explode(' ', trim($searchstr))) . ':*\'';
$reversequery = '\'*:' . implode(' & *:', explode(' ', trim($searchstr))) . '\'';
$nospacequery = '\'' . implode('', explode(' ', trim($searchstr))) . ':*\'';
$searchclause = <<<EOSC
to_tsvector(lower(regexp_replace({$document}, '[[:punct:]]', ' ', 'g'))) @@ to_tsquery(lower({$query}))
@@ -600,98 +596,5 @@ EOSC;
// Otherwise return an empty array
return array();
}
private function _lehreinheit($searchstr, $type)
{
$dbModel = new DB_Model();
$idwhere = '';
if (ctype_digit(trim($searchstr)))
{
$id = (int)trim($searchstr);
$idwhere = ' OR le.lehreinheit_id = ' . $id;
}
$lehreinheit = $dbModel->execReadOnlyQuery('
SELECT
\'teachingunit\' AS renderer,
\''.$type.'\' AS type,
le.lehreinheit_id AS id,
le.studiensemester_kurzbz,
lv.bezeichnung,
\'lv_table_icon icon-\' AS foto,
UPPER(stg.typ || stg.kurzbz) AS studiengang,
le.lehrform_kurzbz,
lv.semester
FROM lehre.tbl_lehreinheit le
JOIN lehre.tbl_lehrveranstaltung lv ON (lv.lehrveranstaltung_id = le.lehrveranstaltung_id)
JOIN public.tbl_studiengang stg ON (stg.studiengang_kz = lv.studiengang_kz)
JOIN public.tbl_studiensemester sem ON (sem.studiensemester_kurzbz = le.studiensemester_kurzbz)
WHERE (' .
$this->buildSearchClause(
$dbModel,
array(
'(stg.typ || stg.kurzbz)',
'lv.semester',
'lv.bezeichnung'
),
$searchstr
) . ')' . $idwhere . '
');
// If something has been found then return it
if (hasData($lehreinheit)) return getData($lehreinheit);
// Otherwise return an empty array
return array();
}
private function _lehrveranstaltung($searchstr, $type)
{
$dbModel = new DB_Model();
$idwhere = '';
if (ctype_digit(trim($searchstr)))
{
$id = (int)trim($searchstr);
$idwhere = ' OR tbl_lehrveranstaltung.lehrveranstaltung_id = ' . $id;
}
$lehreinheit = $dbModel->execReadOnlyQuery('
SELECT
\'teachingunit\' AS renderer,
\''.$type.'\' AS type,
lehrveranstaltung_id as id,
tbl_lehrveranstaltung.bezeichnung,
\'lv_table_icon icon-lv\' as foto,
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as studiengang,
semester,
lehrform_kurzbz,
tbl_organisationseinheit.bezeichnung as oe_bezeichnung
FROM lehre.tbl_lehrveranstaltung
JOIN public.tbl_studiengang USING(studiengang_kz)
JOIN public.tbl_organisationseinheit ON tbl_lehrveranstaltung.oe_kurzbz = tbl_organisationseinheit.oe_kurzbz
WHERE (' .
$this->buildSearchClause(
$dbModel,
array(
'(tbl_studiengang.typ || tbl_studiengang.kurzbz)',
'semester',
'tbl_lehrveranstaltung.bezeichnung'
),
$searchstr
) . ')' . $idwhere . '
');
// If something has been found then return it
if (hasData($lehreinheit)) return getData($lehreinheit);
// Otherwise return an empty array
return array();
}
}
+17 -242
View File
@@ -7,6 +7,7 @@ use \DateTime as DateTime;
use \DateTimeZone as DateTimeZone;
use \DateInterval as DateInterval;
use \DatePeriod as DatePeriod;
use CI3_Events as Events;
class StundenplanLib
{
@@ -116,7 +117,6 @@ 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;
@@ -144,7 +144,7 @@ class StundenplanLib
return $stundenplan_data;
$stundenplan_data = getData($stundenplan_data) ?? [];
$function_error = $this->expandObjectInformation($stundenplan_data);
$function_error = $this->expandObjectInformation($stundenplan_data, true);
if ($function_error)
return $function_error;
@@ -304,7 +304,7 @@ class StundenplanLib
return success($lektoren);
}
public function expandObjectInformation($data)
public function expandObjectInformation($data, $callExtensionsEvent = false)
{
$this->_ci =& get_instance();
@@ -392,40 +392,21 @@ 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;
}
// info retrieved from extensions might not be necessary for some stundenplan use cases so the event
// will be gated behind this flag
if($callExtensionsEvent) {
Events::trigger(
'extendStundenplanData',
function & () use (&$data)
{
return $data;
}
);
}
}
public function fetchFerienTageEvents($start_date, $end_date, $studiengang_kz)
@@ -520,212 +501,6 @@ 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);
$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
+195
View File
@@ -0,0 +1,195 @@
<?php
if (!defined('BASEPATH'))
exit('No direct script access allowed');
use \DOMDocument as DOMDocument;
class XsdSchemaLib
{
private $ci;
public function __construct()
{
$this->ci =& get_instance();
}
/**
* Converts an XSD into a field schema.
* @param string $xsd the XSD as a string
* @param string $templateKurzbz short name of the template
* @return object success(array) with template_kurzbz, root, fields
*/
public function parseSchema($xsd, $templateKurzbz)
{
libxml_use_internal_errors(true);
$dom = new DOMDocument();
if (!$dom->loadXML($xsd))
{
libxml_clear_errors();
return error($this->ci->p->t('cms', 'xsdUngueltig'));
}
libxml_clear_errors();
$root = null;
$fields = array();
$this->_walkElements($dom->documentElement, $root, $fields);
return success(array(
'template_kurzbz' => $templateKurzbz,
'root' => $root,
'fields' => $fields
));
}
/**
* Reads the values of a content from the XML.
* @param string $xml the value of tbl_contentsprache.content
* @param array $schema the result of parseSchema
* @return object success(array) field name => value
*/
public function extractValues($xml, $schema)
{
$values = array();
foreach ($schema['fields'] as $field)
{
$values[$field['name']] = '';
}
if ($xml === null || trim($xml) === '')
{
return success($values);
}
libxml_use_internal_errors(true);
$dom = new DOMDocument();
if (!$dom->loadXML($xml))
{
libxml_clear_errors();
return success($values);
}
libxml_clear_errors();
foreach ($schema['fields'] as $field)
{
$nodes = $dom->getElementsByTagName($field['name']);
if ($nodes->length > 0)
{
$values[$field['name']] = $nodes->item(0)->nodeValue;
}
}
return success($values);
}
/**
* Builds the XML from the values.
* @param array $schema the result of parseSchema
* @param array $values field name => value
* @return object success(string) the XML
*/
public function buildXml($schema, $values)
{
$xml = '<?xml version="1.0" encoding="UTF-8"?>';
$xml .= "\n<" . $schema['root'] . '>';
foreach ($schema['fields'] as $field)
{
$value = isset($values[$field['name']]) ? $values[$field['name']] : '';
$xml .= "\n<" . $field['name'] . '><![CDATA[' . $this->_escapeCdata($value) . ']]></' . $field['name'] . '>';
}
$xml .= "\n</" . $schema['root'] . '>';
libxml_use_internal_errors(true);
$check = new DOMDocument();
if (!$check->loadXML($xml))
{
libxml_clear_errors();
return error($this->ci->p->t('cms', 'xmlUngueltig'));
}
libxml_clear_errors();
return success($xml);
}
// -------------------------------------------------------------------------
// Private methods
/**
* Walks xs:element nodes recursively.
* The first element without a type attribute is the root.
* Elements with a type attribute are fields.
* xs:complexType and xs:sequence are traversed and ignored.
*/
private function _walkElements($node, &$root, &$fields)
{
if ($node->nodeType !== XML_ELEMENT_NODE)
{
return;
}
$localName = $node->localName;
if ($localName === 'element')
{
$typeAttr = $node->getAttribute('type');
if ($typeAttr === '')
{
if ($root === null)
{
$root = $node->getAttribute('name');
}
}
else
{
$minOccurs = $node->getAttribute('minOccurs');
$required = ($minOccurs === '') ? true : ((int)$minOccurs > 0);
$fields[] = array(
'name' => $node->getAttribute('name'),
'type' => $this->_mapType($typeAttr),
'required' => $required
);
}
}
foreach ($node->childNodes as $child)
{
$this->_walkElements($child, $root, $fields);
}
}
/**
* Maps an XSD type to the field schema type.
*/
private function _mapType($xsdType)
{
$map = array(
'xs:string' => 'string',
'xs:decimal' => 'string',
'xs:integer' => 'string',
'xs:positiveInteger' => 'string',
'xs:date' => 'date',
'wysiwyg' => 'wysiwyg',
'file' => 'file',
'boolean' => 'boolean'
);
return isset($map[$xsdType]) ? $map[$xsdType] : 'string';
}
// Escapes ]]> in the value. The legacy code does not do this and writes broken XML.
private function _escapeCdata($value)
{
return str_replace(']]>', ']]]]><![CDATA[>', $value);
}
}
@@ -159,6 +159,8 @@ class LvVwLib
if (!$stg->mischform)
return [];
$this->_ci->StudiengangModel->db->select('TRUE AS leaf', false);
return $this->_ci->stglib->orgform($path_template, $stg->studiengang_kz);
}
@@ -11,73 +11,4 @@ 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;
}
}
@@ -323,4 +323,266 @@ class Content_model extends DB_Model
return success(isset($result[$root_content_id]) ? $result[$root_content_id] : null);
}
/**
* Root contents without a parent in tbl_contentchild. Excludes news.
* @return stdClass success with array of rows or error
*/
public function getRootContent()
{
$query = '
SELECT *
FROM (
SELECT DISTINCT ON (content_id) *
FROM campus.tbl_content
LEFT JOIN campus.tbl_contentchild USING (content_id)
WHERE tbl_content.template_kurzbz <> ?
AND content_id NOT IN (
SELECT child_content_id FROM campus.tbl_contentchild
WHERE child_content_id = tbl_content.content_id)
) AS a
ORDER BY sort, content_id
';
return $this->execReadOnlyQuery($query, ['news']);
}
/**
* Recent news from the last two months, max 100 rows.
* @return stdClass success with array of rows or error
*/
public function getNewsContent()
{
$query = "
SELECT *
FROM campus.tbl_content
JOIN campus.tbl_news USING (content_id)
WHERE tbl_news.datum >= now() - '2 month'::interval
ORDER BY datum DESC
LIMIT 100
";
return $this->execReadOnlyQuery($query);
}
/**
* Search contents by content_id or titel. Excludes news. Returns content_ids only.
* @param array $searchItems search terms
* @return stdClass success with array of rows or error
*/
public function searchCms($searchItems)
{
if (empty($searchItems))
return success([]);
$conditions = [];
$params = [];
foreach ($searchItems as $term)
{
$conditions[] = '(content_id::text = ? OR lower(titel) LIKE lower(?))';
$params[] = $term;
$params[] = '%' . $term . '%';
}
$query = '
SELECT DISTINCT tbl_content.content_id
FROM campus.tbl_contentsprache
JOIN campus.tbl_content USING (content_id)
WHERE tbl_content.template_kurzbz <> ?
AND (' . implode(' OR ', $conditions) . ')
ORDER BY content_id
';
array_unshift($params, 'news');
return $this->execReadOnlyQuery($query, $params);
}
/**
* Per content metadata for the tree filter. Excludes news, like getRootContent().
* updateamum is the last touch on the content or on any of its versions.
*
* contentlength counts the visible characters of the newest version, the way getOne()
* picks it: the default language first, then the highest version. The XML scaffolding,
* the CDATA markers, the HTML tags, the entities and every space drop out, so a content
* that nobody ever filled measures 0 whatever empty shape it carries. The title counts
* too, so a content with a title and no body measures short instead of zero.
*
* @param string $uid current user, decides the mine flag
* @param string $sprache preferred language of the measured version
* @return stdClass success with array of rows or error
*/
public function getTreeMeta($uid, $sprache)
{
$query = "
SELECT tbl_content.content_id,
(SELECT COUNT(*) FROM campus.tbl_contentchild
WHERE tbl_contentchild.content_id = tbl_content.content_id) AS childcount,
tbl_content.insertamum,
GREATEST(
tbl_content.updateamum,
(SELECT MAX(updateamum) FROM campus.tbl_contentsprache
WHERE tbl_contentsprache.content_id = tbl_content.content_id)
) AS updateamum,
COALESCE(
tbl_content.insertvon = ? OR tbl_content.updatevon = ? OR EXISTS (
SELECT 1 FROM campus.tbl_contentsprache
WHERE tbl_contentsprache.content_id = tbl_content.content_id
AND (tbl_contentsprache.insertvon = ?
OR tbl_contentsprache.updatevon = ?)
),
false
) AS mine,
(SELECT length(
regexp_replace(
regexp_replace(
regexp_replace(
replace(replace(cs.content::text, '<![CDATA[', ''), ']]>', ''),
'<[^>]*>', '', 'g'),
'&[a-zA-Z]+;|&#[0-9]+;', '', 'g'),
'\s+', '', 'g')
)
FROM campus.tbl_contentsprache cs
WHERE cs.content_id = tbl_content.content_id
ORDER BY (cs.sprache = ?) DESC, cs.version DESC
LIMIT 1) AS contentlength
FROM campus.tbl_content
WHERE tbl_content.template_kurzbz <> ?
";
return $this->execReadOnlyQuery(
$query, [$uid, $uid, $uid, $uid, $sprache, 'news']
);
}
/**
* All contents eligible as children: excludes ancestors, self, and news.
* @param int $content_id the content to find children for
* @param string $sprache language for the titel subselect
* @return stdClass success with array of rows or error
*/
public function getPossibleChilds($content_id, $sprache)
{
$query = '
SELECT content_id, oe_kurzbz, template_kurzbz,
(SELECT titel FROM campus.tbl_contentsprache
WHERE sprache = ? AND content_id = tbl_content.content_id
ORDER BY version LIMIT 1) AS titel
FROM campus.tbl_content
WHERE content_id NOT IN (
WITH RECURSIVE parents(content_id, child_content_id) AS (
SELECT content_id, child_content_id FROM campus.tbl_contentchild
WHERE child_content_id = ?
UNION ALL
SELECT cc.content_id, cc.child_content_id
FROM campus.tbl_contentchild cc, parents
WHERE cc.child_content_id = parents.content_id)
SELECT content_id FROM parents GROUP BY content_id)
AND content_id <> ?
AND template_kurzbz <> ?
ORDER BY titel
';
return $this->execReadOnlyQuery($query, [$sprache, $content_id, $content_id, 'news']);
}
/**
* Where a content is referenced. Returns [{table, label}] for the delete dialog.
* Tolerates missing addon/testtool schemas.
* @param int $content_id
* @return stdClass success with flat array or error
*/
public function getUsage($content_id)
{
$coreQuery = "
SELECT 'campus.tbl_infoscreen_content' AS \"table\",
infoscreen_id::text AS label
FROM campus.tbl_infoscreen_content WHERE content_id = ?
UNION ALL
SELECT 'campus.tbl_news', betreff
FROM campus.tbl_news WHERE content_id = ?
UNION ALL
SELECT 'public.tbl_ort', ort_kurzbz
FROM public.tbl_ort WHERE content_id = ?
UNION ALL
SELECT 'public.tbl_service', bezeichnung
FROM public.tbl_service WHERE content_id = ?
UNION ALL
SELECT 'public.tbl_statistik', bezeichnung
FROM public.tbl_statistik WHERE content_id = ?
";
$coreParams = [$content_id, $content_id, $content_id, $content_id, $content_id];
$coreResult = $this->execReadOnlyQuery($coreQuery, $coreParams);
$rows = [];
if (!isError($coreResult) && getData($coreResult))
$rows = getData($coreResult);
// Optional schemas: testtool and addon may not exist. A failing query still writes a
// db error into the response envelope, so check for the table before querying it.
$optionalQueries = [
[
'testtool', 'tbl_ablauf_vorgaben',
"SELECT 'testtool.tbl_ablauf_vorgaben' AS \"table\",
ablauf_vorgaben_id::text AS label
FROM testtool.tbl_ablauf_vorgaben WHERE content_id = ?"
],
[
'addon', 'tbl_software',
"SELECT 'addon.tbl_software' AS \"table\",
software_id::text AS label
FROM addon.tbl_software WHERE content_id = ?"
]
];
foreach ($optionalQueries as $opt)
{
if (!$this->tableExists($opt[0], $opt[1]))
continue;
$result = $this->execReadOnlyQuery($opt[2], [$content_id]);
if (!isError($result) && getData($result))
$rows = array_merge($rows, getData($result));
}
return success($rows);
}
/**
* True if a base table exists. Guards the optional testtool and addon schemas.
* @param string $schema
* @param string $table
* @return bool
*/
private function tableExists($schema, $table)
{
$query = "
SELECT 1 AS exists
FROM information_schema.tables
WHERE table_catalog = ? AND table_schema = ? AND table_name = ?
";
return hasData($this->execReadOnlyQuery($query, [DB_NAME, $schema, $table]));
}
/**
* Returns oe_kurzbz for a content as a string.
* @param int $content_id
* @return stdClass success with string or error
*/
public function getOeKurzbz($content_id)
{
$query = 'SELECT oe_kurzbz FROM campus.tbl_content WHERE content_id = ?';
$result = $this->execReadOnlyQuery($query, [$content_id]);
if (isError($result))
return $result;
$data = getData($result);
if (empty($data))
return error('Content not found');
return success($data[0]->oe_kurzbz);
}
}
@@ -1,6 +1,9 @@
<?php
class Contentchild_model extends DB_Model
{
// swapSort reports the two boundary cases with this. See swapSort().
const NO_NEIGHBOUR = 'no neighbour';
/**
* Constructor
@@ -11,4 +14,345 @@ class Contentchild_model extends DB_Model
$this->dbTable = 'campus.tbl_contentchild';
$this->pk = 'contentchild_id';
}
/**
* Children with titel, sorted by sort.
* @param int $content_id
* @param string $sprache
* @return stdClass success with array of rows or error
*/
public function getChilds($content_id, $sprache)
{
$query = '
SELECT cc.contentchild_id, cc.child_content_id, cc.sort,
(SELECT titel FROM campus.tbl_contentsprache
WHERE content_id = cc.child_content_id AND sprache = ?
ORDER BY version LIMIT 1) AS titel
FROM campus.tbl_contentchild cc
WHERE cc.content_id = ?
ORDER BY cc.sort, cc.contentchild_id
';
return $this->execReadOnlyQuery($query, [$sprache, $content_id]);
}
/**
* Parents of a content, with the title of each parent.
* A content can hang under more than one parent, so this returns a list.
* @param int $content_id
* @param string $sprache
* @return stdClass success with array of rows or error
*/
public function getParents($content_id, $sprache)
{
$query = '
SELECT cc.contentchild_id, cc.content_id, cc.sort,
(SELECT titel FROM campus.tbl_contentsprache
WHERE content_id = cc.content_id AND sprache = ?
ORDER BY version LIMIT 1) AS titel
FROM campus.tbl_contentchild cc
WHERE cc.child_content_id = ?
ORDER BY cc.content_id
';
return $this->execReadOnlyQuery($query, [$sprache, $content_id]);
}
/**
* Children of a content as a reader may see them.
*
* Differs from getChilds() on purpose. getChilds() serves the admin and returns every
* child with the lowest version. A reader needs the newest visible version, only active
* contents, and no page that a group rule keeps from them.
*
* The group rule follows CmsLib::getContent(): a content without a row in
* tbl_contentgruppe is open, a content with rows needs membership in one of them.
*
* @param int $content_id
* @param string $sprache
* @param string $uid
* @return stdClass success with array of rows or error
*/
public function getChildsForReader($content_id, $sprache, $uid)
{
$query = '
SELECT
cc.child_content_id AS content_id,
cc.sort,
COALESCE(
(SELECT cs.titel FROM campus.tbl_contentsprache cs
WHERE cs.content_id = cc.child_content_id
AND cs.sprache = ?
AND cs.sichtbar
AND LENGTH(cs.titel) > 0
ORDER BY cs.version DESC NULLS LAST LIMIT 1),
(SELECT cs.titel FROM campus.tbl_contentsprache cs
WHERE cs.content_id = cc.child_content_id
AND cs.sichtbar
AND LENGTH(cs.titel) > 0
ORDER BY cs.version DESC NULLS LAST LIMIT 1),
c.beschreibung
) AS titel
FROM campus.tbl_contentchild cc
JOIN campus.tbl_content c ON c.content_id = cc.child_content_id
WHERE cc.content_id = ?
AND c.aktiv
-- Without a visible version the reader cannot open the page at all.
AND EXISTS (
SELECT 1 FROM campus.tbl_contentsprache cs
WHERE cs.content_id = cc.child_content_id AND cs.sichtbar
)
AND (
NOT EXISTS (
SELECT 1 FROM campus.tbl_contentgruppe cg
WHERE cg.content_id = cc.child_content_id
)
OR EXISTS (
SELECT 1 FROM campus.tbl_contentgruppe cg
JOIN public.vw_gruppen vg ON vg.gruppe_kurzbz = cg.gruppe_kurzbz
WHERE cg.content_id = cc.child_content_id AND vg.uid = ?
)
)
ORDER BY cc.sort, cc.contentchild_id
';
return $this->execReadOnlyQuery($query, [$sprache, $content_id, $uid]);
}
/**
* Highest sort value. Returns 0 if no row exists.
* @param int $content_id
* @return stdClass success with int or error
*/
public function getMaxSort($content_id)
{
$query = '
SELECT COALESCE(MAX(sort), 0) AS max_sort
FROM campus.tbl_contentchild
WHERE content_id = ?
';
$result = $this->execReadOnlyQuery($query, [$content_id]);
if (isError($result))
return $result;
return success((int) getData($result)[0]->max_sort);
}
/**
* All descendant content_ids recursively. Returns a flat array.
* @param int $content_id
* @return stdClass success with array of ints or error
*/
public function getAllChildIds($content_id)
{
$query = '
WITH RECURSIVE childs(content_id, child_content_id) AS (
SELECT content_id, child_content_id FROM campus.tbl_contentchild WHERE content_id = ?
UNION ALL
SELECT cc.content_id, cc.child_content_id
FROM campus.tbl_contentchild cc, childs
WHERE cc.content_id = childs.child_content_id)
SELECT DISTINCT child_content_id FROM childs
';
$result = $this->execReadOnlyQuery($query, [$content_id]);
if (isError($result))
return $result;
$ids = [];
if (getData($result))
{
foreach (getData($result) as $row)
$ids[] = (int) $row->child_content_id;
}
return success($ids);
}
/**
* Swap sort value with the neighbour. Direction is 'up' or 'down'.
* @param int $contentchild_id
* @param string $direction 'up' or 'down'
* @return stdClass success or error
*/
public function swapSort($contentchild_id, $direction)
{
$currentResult = $this->load($contentchild_id);
if (isError($currentResult))
return $currentResult;
$current = getData($currentResult)[0];
if ($direction === 'up')
{
$neighbourQuery = '
SELECT contentchild_id, sort
FROM campus.tbl_contentchild
WHERE content_id = ? AND sort < ?
ORDER BY sort DESC
LIMIT 1
';
}
else
{
$neighbourQuery = '
SELECT contentchild_id, sort
FROM campus.tbl_contentchild
WHERE content_id = ? AND sort > ?
ORDER BY sort ASC
LIMIT 1
';
}
$neighbourResult = $this->execReadOnlyQuery($neighbourQuery, [$current->content_id, $current->sort]);
if (isError($neighbourResult))
return $neighbourResult;
$neighbourData = getData($neighbourResult);
// A marker, not a message. The controller owns the wording, because a model has no
// phrases. Returning a phrase key here put the key itself in front of the user.
if (empty($neighbourData))
return error(self::NO_NEIGHBOUR);
$neighbour = $neighbourData[0];
// Transaction wraps the swap; the legacy does not.
$this->db->trans_start();
$this->execQuery(
'UPDATE campus.tbl_contentchild SET sort = ? WHERE contentchild_id = ?',
[$neighbour->sort, $current->contentchild_id]
);
$this->execQuery(
'UPDATE campus.tbl_contentchild SET sort = ? WHERE contentchild_id = ?',
[$current->sort, $neighbour->contentchild_id]
);
$this->db->trans_complete();
if ($this->db->trans_status() === false)
return error('Sort swap failed');
return success(true);
}
/**
* Writes the whole order of one content in one transaction. Serves the drag and drop.
* The list must hold every child of the content exactly once, or the rows left out
* would keep a stale sort value.
* @param int $content_id
* @param array $contentchild_ids in the wanted order
* @return stdClass success(true) or error
*/
public function setSortOrder($content_id, $contentchild_ids)
{
$currentResult = $this->execReadOnlyQuery(
'SELECT contentchild_id FROM campus.tbl_contentchild WHERE content_id = ?',
[$content_id]
);
if (isError($currentResult))
return $currentResult;
$current = [];
foreach ((array) getData($currentResult) as $row)
$current[] = (int) $row->contentchild_id;
$wanted = array_map('intval', array_values($contentchild_ids));
$check = $wanted;
sort($current);
sort($check);
if ($current !== $check)
return error('Sort order does not match the children of the content');
$this->db->trans_start();
foreach ($wanted as $position => $contentchild_id)
{
$this->execQuery(
'UPDATE campus.tbl_contentchild SET sort = ? WHERE contentchild_id = ?',
[$position + 1, $contentchild_id]
);
}
$this->db->trans_complete();
if ($this->db->trans_status() === false)
return error('Sort order update failed');
return success(true);
}
/**
* Every ancestor of the given contents, with the distance to the content it belongs to.
*
* The tree shows a search result out of its place: a title like "Team" says nothing
* about which unit it belongs to. This delivers the way down to it.
*
* A content can hang under more than one parent, so a level can hold more than one
* ancestor. Every one of them comes back, with tiefe 1 for the direct parent. The
* caller decides how to draw that.
*
* tiefe also ends a cycle: a content whose parent chain leads back to itself would
* otherwise recurse forever.
*
* @param array $content_ids contents to walk up from
* @param int $maxTiefe how many levels to climb
* @param string $sprache language of the ancestor titles
* @return stdClass success with array of rows or error
*/
public function getAncestors($content_ids, $maxTiefe, $sprache)
{
if (empty($content_ids))
return success([]);
$platzhalter = implode(', ', array_fill(0, count($content_ids), '?'));
// The aggregate and the title sit in the final select. The recursive term names
// the working table once and carries no subquery, which is what Postgres allows.
$query = '
WITH RECURSIVE auf(start_id, content_id, tiefe) AS (
SELECT cc.child_content_id, cc.content_id, 1
FROM campus.tbl_contentchild cc
WHERE cc.child_content_id IN (' . $platzhalter . ')
UNION ALL
SELECT auf.start_id, cc.content_id, auf.tiefe + 1
FROM campus.tbl_contentchild cc
JOIN auf ON cc.child_content_id = auf.content_id
WHERE auf.tiefe < ?
)
SELECT auf.start_id, auf.content_id, MIN(auf.tiefe) AS tiefe,
(SELECT titel FROM campus.tbl_contentsprache cs
WHERE cs.content_id = auf.content_id AND cs.sprache = ?
ORDER BY cs.version LIMIT 1) AS titel,
-- The caller turns this into the entitled flag, so the path can offer a
-- link only for an ancestor the editor may open.
(SELECT c.oe_kurzbz FROM campus.tbl_content c
WHERE c.content_id = auf.content_id) AS oe_kurzbz
FROM auf
GROUP BY auf.start_id, auf.content_id
ORDER BY auf.start_id, MIN(auf.tiefe) DESC, auf.content_id
';
$params = array_map('intval', array_values($content_ids));
$params[] = (int) $maxTiefe;
$params[] = $sprache;
return $this->execReadOnlyQuery($query, $params);
}
/**
* Removes a content from the child table, as parent and as child.
* @param int $content_id
* @return stdClass
*/
public function deleteByContent($content_id)
{
return $this->execQuery(
'DELETE FROM campus.tbl_contentchild
WHERE content_id = ? OR child_content_id = ?',
[$content_id, $content_id]
);
}
}
@@ -10,6 +10,8 @@ class Contentgruppe_model extends DB_Model
parent::__construct();
$this->dbTable = 'campus.tbl_contentgruppe';
$this->pk = array('gruppe_kurzbz', 'content_id');
// Composite key without a sequence, so insert() must skip insert_id() and its LASTVAL().
$this->hasSequence = false;
}
/**
@@ -18,7 +20,7 @@ class Contentgruppe_model extends DB_Model
*
* @param int $content_id
*
* @return stdClass success(true) wenn eingeschraenkt sonst success(false)
* @return stdClass success(true) wenn locked sonst success(false)
*/
public function islocked($content_id)
{
@@ -30,7 +32,7 @@ class Contentgruppe_model extends DB_Model
}
/**
* Prueft ob ein User die Berechtigung fuer das Anzeigen des
* Prüft ob ein User die Berechtigung fuer das Anzeigen des
* Contents besitzt
*
* @param int $content_id ID des Contents
@@ -56,5 +58,89 @@ class Contentgruppe_model extends DB_Model
return $result;
return success(!!getData($result));
}
/**
* Groups assigned to a content, with bezeichnung.
* @param int $content_id
* @return stdClass success with array of rows or error
*/
public function getGruppen($content_id)
{
$query = '
SELECT cg.gruppe_kurzbz, g.bezeichnung
FROM campus.tbl_contentgruppe cg
JOIN public.tbl_gruppe g USING (gruppe_kurzbz)
WHERE cg.content_id = ?
ORDER BY cg.gruppe_kurzbz
';
return $this->execReadOnlyQuery($query, [$content_id]);
}
/**
* All selectable groups. Returns gruppe_kurzbz and bezeichnung.
* @return stdClass success with array of rows or error
*/
public function getAllGruppen()
{
// content_visible marks a group as selectable for content rights. Generated groups
// carry FALSE. admin.php filters the same way via getgruppe(...,$content_visible=true).
$query = '
SELECT gruppe_kurzbz, bezeichnung
FROM public.tbl_gruppe
WHERE content_visible = TRUE
ORDER BY gruppe_kurzbz
';
return $this->execReadOnlyQuery($query);
}
/**
* Groups for multiple contents in one query. Returns content_id => [gruppe_kurzbz, ...].
* @param array $contentIds array of content_id values
* @return stdClass success with associative array or error
*/
public function getGruppenForContents($contentIds)
{
if (empty($contentIds))
return success([]);
$placeholders = implode(',', array_fill(0, count($contentIds), '?'));
$query = '
SELECT content_id, gruppe_kurzbz
FROM campus.tbl_contentgruppe
WHERE content_id IN (' . $placeholders . ')
ORDER BY content_id, gruppe_kurzbz
';
$result = $this->execReadOnlyQuery($query, array_values($contentIds));
if (isError($result))
return $result;
$map = [];
if (getData($result))
{
foreach (getData($result) as $row)
{
$id = (int) $row->content_id;
if (!isset($map[$id]))
$map[$id] = [];
$map[$id][] = $row->gruppe_kurzbz;
}
}
return success($map);
}
/**
* @param int $content_id
* @return stdClass
*/
public function deleteByContent($content_id)
{
return $this->execQuery(
'DELETE FROM campus.tbl_contentgruppe WHERE content_id = ?',
[$content_id]
);
}
}
@@ -11,4 +11,76 @@ class Contentlog_model extends DB_Model
$this->dbTable = 'campus.tbl_contentlog';
$this->pk = 'contentlog_id';
}
/**
* The open lock entry of a user (ende IS NULL). Returns null if none exists.
* Match the holder and take the newest, otherwise the age belongs to an abandoned entry.
* @param int $contentsprache_id
* @param string $uid holder of the lock
* @return stdClass success with row or null, or error
*/
public function getOpenLock($contentsprache_id, $uid)
{
$query = '
SELECT contentlog_id, uid, contentsprache_id, start
FROM campus.tbl_contentlog
WHERE contentsprache_id = ? AND uid = ? AND ende IS NULL
ORDER BY start DESC
LIMIT 1
';
$result = $this->execReadOnlyQuery($query, [$contentsprache_id, $uid]);
if (isError($result))
return $result;
$data = getData($result);
return success(empty($data) ? null : $data[0]);
}
/**
* Ends every open lock entry of a version. Without a uid it ends the entry of every
* holder, which is what a take-over and a forced release need.
* @param int $contentsprache_id
* @param string|null $uid
* @return stdClass
*/
public function closeOpenEntries($contentsprache_id, $uid = null)
{
$query = 'UPDATE campus.tbl_contentlog SET ende = now()
WHERE contentsprache_id = ? AND ende IS NULL';
$params = [$contentsprache_id];
if ($uid !== null)
{
$query .= ' AND uid = ?';
$params[] = $uid;
}
return $this->execQuery($query, $params);
}
/**
* @param int $contentsprache_id
* @return stdClass
*/
public function deleteByContentsprache($contentsprache_id)
{
return $this->execQuery(
'DELETE FROM campus.tbl_contentlog WHERE contentsprache_id = ?',
[$contentsprache_id]
);
}
/**
* @param int $content_id
* @return stdClass
*/
public function deleteByContent($content_id)
{
return $this->execQuery(
'DELETE FROM campus.tbl_contentlog WHERE contentsprache_id IN
(SELECT contentsprache_id FROM campus.tbl_contentsprache WHERE content_id = ?)',
[$content_id]
);
}
}
@@ -38,5 +38,177 @@ class Contentsprache_model extends DB_Model
return success(!!getData($result));
}
/**
* All versions of a language, ascending by version.
* @param int $content_id
* @param string $sprache
* @return stdClass success with array of rows or error
*/
public function getVersions($content_id, $sprache)
{
$query = '
SELECT contentsprache_id, version, sichtbar, titel,
insertamum, insertvon, updateamum, updatevon, gesperrt_uid
FROM campus.tbl_contentsprache
WHERE content_id = ? AND sprache = ?
ORDER BY version ASC
';
return $this->execReadOnlyQuery($query, [$content_id, $sprache]);
}
/**
* Distinct languages for a content. Returns a flat array of strings.
* @param int $content_id
* @return stdClass success with array of strings or error
*/
public function getLanguages($content_id)
{
$query = '
SELECT DISTINCT sprache
FROM campus.tbl_contentsprache
WHERE content_id = ?
ORDER BY sprache
';
$result = $this->execReadOnlyQuery($query, [$content_id]);
if (isError($result))
return $result;
$languages = [];
if (getData($result))
{
foreach (getData($result) as $row)
$languages[] = $row->sprache;
}
return success($languages);
}
/**
* Highest version number. Returns 0 if no row exists.
* @param int $content_id
* @param string $sprache
* @return stdClass success with int or error
*/
public function getMaxVersion($content_id, $sprache)
{
$query = '
SELECT COALESCE(MAX(version), 0) AS max_version
FROM campus.tbl_contentsprache
WHERE content_id = ? AND sprache = ?
';
$result = $this->execReadOnlyQuery($query, [$content_id, $sprache]);
if (isError($result))
return $result;
return success((int) getData($result)[0]->max_version);
}
/**
* Row count for a content+language. Used for the delete guard.
* @param int $content_id
* @param string $sprache
* @return stdClass success with int or error
*/
public function getNumberOfVersions($content_id, $sprache)
{
$query = '
SELECT COUNT(*) AS cnt
FROM campus.tbl_contentsprache
WHERE content_id = ? AND sprache = ?
';
$result = $this->execReadOnlyQuery($query, [$content_id, $sprache]);
if (isError($result))
return $result;
return success((int) getData($result)[0]->cnt);
}
/**
* One version, all columns. If $version is empty, returns the highest version.
* @param int $content_id
* @param string $sprache
* @param int|null $version
* @return stdClass success with single row or error
*/
public function getOne($content_id, $sprache, $version = null)
{
$params = [$content_id, $sprache];
$versionClause = '';
if (!empty($version))
{
$versionClause = ' AND version = ?';
$params[] = $version;
}
$query = '
SELECT * FROM campus.tbl_contentsprache
WHERE content_id = ? AND sprache = ?' . $versionClause . '
ORDER BY version DESC LIMIT 1
';
$result = $this->execReadOnlyQuery($query, $params);
if (isError($result))
return $result;
$data = getData($result);
if (empty($data))
return error('Version not found');
return success($data[0]);
}
/**
* Clears the lock of a version. Without a uid it clears whoever holds it, which is
* what a forced release needs.
* @param int $contentsprache_id
* @param string|null $uid
* @return stdClass
*/
public function releaseLock($contentsprache_id, $uid = null)
{
$query = 'UPDATE campus.tbl_contentsprache SET gesperrt_uid = NULL
WHERE contentsprache_id = ?';
$params = [$contentsprache_id];
if ($uid !== null)
{
$query .= ' AND gesperrt_uid = ?';
$params[] = $uid;
}
return $this->execQuery($query, $params);
}
/**
* @param int $content_id
* @return stdClass
*/
public function deleteByContent($content_id)
{
return $this->execQuery(
'DELETE FROM campus.tbl_contentsprache WHERE content_id = ?',
[$content_id]
);
}
/**
* @param int $content_id
* @param string $sprache
* @param int $version
* @return stdClass
*/
public function deleteVersion($content_id, $sprache, $version)
{
return $this->execQuery(
'DELETE FROM campus.tbl_contentsprache
WHERE content_id = ? AND sprache = ? AND version = ?',
[$content_id, $sprache, $version]
);
}
}
+237
View File
@@ -30,4 +30,241 @@ class Dms_model extends DB_Model
return $returnArray;
}
/**
* Reads a DMS category and the access facts a listing needs.
*
* Mirrors the rule of cms/dms.php and dms.class.php at category level:
* a category is locked when any category in its parent chain grants a group or
* carries a berechtigung_kurzbz. The caller decides, this method only reports.
*
* @param string $kategorie_kurzbz
* gesperrt and in_gruppe come back as 0 or 1, not as a boolean. A PostgreSQL boolean
* reaches PHP as the string 'f' when the driver conversion does not run, and 'f' is
* truthy. The int cast is correct in both cases.
*
* @param string $uid
* @return object one row, or an empty result when the category does not exist
*/
public function getKategorieZugriff($kategorie_kurzbz, $uid)
{
// tiefe guards against a cyclic parent chain.
$query = '
WITH RECURSIVE chain(kategorie_kurzbz, parent_kategorie_kurzbz, berechtigung_kurzbz, tiefe) AS (
SELECT kategorie_kurzbz, parent_kategorie_kurzbz, berechtigung_kurzbz, 1
FROM campus.tbl_dms_kategorie
WHERE kategorie_kurzbz = ?
UNION ALL
SELECT k.kategorie_kurzbz, k.parent_kategorie_kurzbz, k.berechtigung_kurzbz, c.tiefe + 1
FROM campus.tbl_dms_kategorie k
JOIN chain c ON k.kategorie_kurzbz = c.parent_kategorie_kurzbz
WHERE c.tiefe < 32
)
SELECT
kat.kategorie_kurzbz,
kat.bezeichnung,
kat.beschreibung,
kat.berechtigung_kurzbz,
(
EXISTS (SELECT 1 FROM chain WHERE chain.berechtigung_kurzbz IS NOT NULL)
OR EXISTS (
SELECT 1
FROM campus.tbl_dms_kategorie_gruppe g
JOIN chain ON chain.kategorie_kurzbz = g.kategorie_kurzbz
)
)::int AS gesperrt,
(EXISTS (
SELECT 1
FROM campus.tbl_dms_kategorie_gruppe g
JOIN chain ON chain.kategorie_kurzbz = g.kategorie_kurzbz
JOIN public.tbl_benutzergruppe b ON b.gruppe_kurzbz = g.gruppe_kurzbz
WHERE b.uid = ?
))::int AS in_gruppe
FROM campus.tbl_dms_kategorie kat
WHERE kat.kategorie_kurzbz = ?
';
return $this->execReadOnlyQuery($query, array($kategorie_kurzbz, $uid, $kategorie_kurzbz));
}
/**
* Lists the newest version of every document in one DMS category.
*
* filename stays out of the result. The reader gets the document through
* cms/dms.php?id=<dms_id>, never through the name on disk.
*
* @param string $kategorie_kurzbz
* @return object
*/
public function getKategorieDokumente($kategorie_kurzbz)
{
$query = '
SELECT
d.dms_id,
v.version,
v.name,
v.beschreibung,
v.mimetype,
COALESCE(v.updateamum, v.insertamum) AS geaendert
FROM campus.tbl_dms d
JOIN campus.tbl_dms_version v ON v.dms_id = d.dms_id
WHERE d.kategorie_kurzbz = ?
-- cis_suche marks a document as published to CIS readers. A category also
-- holds working files, and those must not appear on a content page.
AND v.cis_suche
AND v.version = (
SELECT MAX(version) FROM campus.tbl_dms_version WHERE dms_id = d.dms_id
)
-- A project document carries its own rule in dms.class.php and does not
-- belong in a CMS category listing.
AND NOT EXISTS (
SELECT 1 FROM fue.tbl_projekt_dokument pd WHERE pd.dms_id = d.dms_id
)
ORDER BY LOWER(v.name)
';
return $this->execReadOnlyQuery($query, array($kategorie_kurzbz));
}
/**
* Newest version of the named documents, in no particular order.
*
* Unlike getKategorieDokumente() this does NOT filter on cis_suche. An editor who names
* a dms_id has chosen that document on purpose, and the flag only marks a document for
* the CIS search. The category rule still applies, and the caller enforces it.
*
* @param array $ids dms_id values
* @return object
*/
public function getDokumenteByIds($ids)
{
if (empty($ids))
return success(array());
$platzhalter = implode(', ', array_fill(0, count($ids), '?'));
$query = '
SELECT
d.dms_id,
d.kategorie_kurzbz,
v.version,
v.name,
v.beschreibung,
v.mimetype,
COALESCE(v.updateamum, v.insertamum) AS geaendert
FROM campus.tbl_dms d
JOIN campus.tbl_dms_version v ON v.dms_id = d.dms_id
WHERE d.dms_id IN (' . $platzhalter . ')
AND v.version = (
SELECT MAX(version) FROM campus.tbl_dms_version WHERE dms_id = d.dms_id
)
AND NOT EXISTS (
SELECT 1 FROM fue.tbl_projekt_dokument pd WHERE pd.dms_id = d.dms_id
)
';
return $this->execReadOnlyQuery($query, array_values($ids));
}
/**
* Every document of one category, for the editor picker.
*
* Unlike getKategorieDokumente() this does NOT filter on cis_suche, because
* dms-dokumente renders named documents regardless of that flag. The flag travels with
* the row so the picker can mark a document that no reader would find by search.
*
* @param string $kategorie_kurzbz
* @return object
*/
public function getKategorieDokumenteAlle($kategorie_kurzbz)
{
$query = '
SELECT
d.dms_id,
v.name,
v.cis_suche::int AS cis_suche
FROM campus.tbl_dms d
JOIN LATERAL (
SELECT v.name, v.cis_suche
FROM campus.tbl_dms_version v
WHERE v.dms_id = d.dms_id
ORDER BY v.version DESC
LIMIT 1
) v ON TRUE
WHERE d.kategorie_kurzbz = ?
AND NOT EXISTS (
SELECT 1 FROM fue.tbl_projekt_dokument pd WHERE pd.dms_id = d.dms_id
)
ORDER BY LOWER(v.name)
';
return $this->execReadOnlyQuery($query, array($kategorie_kurzbz));
}
/**
* Lists every DMS category with the number of documents it holds.
* Used by the insert dialog of the CMS content component "dms-liste".
*
* @return object
*/
public function getKategorien()
{
$query = '
SELECT
k.kategorie_kurzbz,
k.bezeichnung,
k.oe_kurzbz,
COALESCE(a.anzahl, 0) AS anzahl
FROM campus.tbl_dms_kategorie k
LEFT JOIN (
SELECT d.kategorie_kurzbz, COUNT(*) AS anzahl
FROM campus.tbl_dms d
JOIN (
SELECT DISTINCT ON (dms_id) dms_id, cis_suche
FROM campus.tbl_dms_version
ORDER BY dms_id, version DESC
) v ON v.dms_id = d.dms_id
WHERE v.cis_suche
GROUP BY d.kategorie_kurzbz
) a ON a.kategorie_kurzbz = k.kategorie_kurzbz
ORDER BY LOWER(COALESCE(k.bezeichnung, k.kategorie_kurzbz))
';
return $this->execReadOnlyQuery($query);
}
/**
* The category itself plus its sub categories, down to a depth.
*
* tiefe 0 returns only the category named. The depth bound also ends a cycle: a
* category whose parent chain leads back to itself would otherwise recurse forever.
*
* The caller must still check the entitlement of every category it gets back. A sub
* category carries its own group rule and may be closed while the parent is open.
*
* @param string $kategorie_kurzbz root of the walk
* @param int $tiefe how many levels below the root to include
* @return object success with array of rows or error
*/
public function getMitUnterkategorien($kategorie_kurzbz, $tiefe)
{
$query = '
WITH RECURSIVE unter(kategorie_kurzbz, tiefe) AS (
SELECT kategorie_kurzbz, 0
FROM campus.tbl_dms_kategorie
WHERE kategorie_kurzbz = ?
UNION ALL
SELECT k.kategorie_kurzbz, unter.tiefe + 1
FROM campus.tbl_dms_kategorie k
JOIN unter ON k.parent_kategorie_kurzbz = unter.kategorie_kurzbz
WHERE unter.tiefe < ?
)
SELECT kategorie_kurzbz, MIN(tiefe) AS tiefe
FROM unter
GROUP BY kategorie_kurzbz
ORDER BY MIN(tiefe), kategorie_kurzbz
';
return $this->execReadOnlyQuery($query, [$kategorie_kurzbz, (int) $tiefe]);
}
}
+35 -52
View File
@@ -20,16 +20,14 @@ class News_model extends DB_Model
public function getAll($limit = null)
{
$this->addJoin("campus.tbl_content","content_id");
return $this->execReadOnlyQuery('
SELECT *, TO_CHAR(campus.tbl_news.datum, ?) as datumformatted
FROM campus.tbl_news
JOIN campus.tbl_content content ON content.content_id = campus.tbl_news.content_id
WHERE
WHERE
--text IS NOT NULL AND
datum <= NOW() AND (datum_bis >= NOW()::date OR datum_bis IS NULL) AND
(NOW()-datum) < interval \'' . MAXNEWSALTER . ' days\'
ORDER BY datum DESC, campus.tbl_news.updateamum DESC
datum <= NOW() AND (datum_bis IS NULL OR datum_bis >= now()::date)
ORDER BY datum DESC
LIMIT ' . $this->escape($limit)
, ['DD/MM/YYYY']);
}
@@ -50,28 +48,31 @@ class News_model extends DB_Model
* @param integer | null $semester
* @param string $fachbereich_kurzbz
* @param boolean $sichtbar
* @param integer $maxalter
* @param integer $page
* @param integer $page_size
* @param boolean $active
* @param boolean $all
* @param boolean $mischen
*
* TODO(chris): this is not a good function -> the params are all over the place
*
*/
protected function prepareNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz = null, $sichtbar = true, $page = 1, $page_size = 10, $active = true, $mischen = true, $filterForDegreePrograms = false, $allowedDegreePrograms = [])
protected function prepareNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz = null, $sichtbar = true, $maxalter = 0, $page = 1, $page_size = 10, $all = false, $mischen = true)
{
$this->addSelect(["*", "COUNT(*) OVER() AS row_count"]);
$this->addOrder('datum', 'DESC');
$this->addOrder('tbl_news.updateamum', 'DESC');
$studiengang_kz = trim($studiengang_kz);
$fachbereich_kurzbz = trim($fachbereich_kurzbz);
$where = [];
if ($active) {
$where[] = "(datum <= NOW()::date AND (datum_bis >= NOW()::date OR (datum_bis IS NULL AND (NOW()-datum) < interval '" . MAXNEWSALTER . " days')))";
if (trim($maxalter) != '0') {
$where[] = "(now()-datum) < interval " . $this->db->escape($maxalter) . " days";
}
if (!$all) {
$where[] = "datum <= now()";
$where[] = "(datum_bis >= now()::date OR datum_bis IS NULL)";
}
if ($fachbereich_kurzbz != '*') {
if ($fachbereich_kurzbz == '') {
@@ -81,54 +82,27 @@ class News_model extends DB_Model
}
}
if ($studiengang_kz == '0') {
$where[] = "studiengang_kz = " . $this->db->escape($studiengang_kz);
$sgAndSemesterCondition = [];
if ($semester === NULL)
$where[] = "semester IS NULL";
elseif ($semester === 0)
$where[] = "semester = 0";
} elseif ($studiengang_kz != '') {
$add = $mischen === true ? " OR (studiengang_kz = 0 AND semester IS NULL)" : "";
$where[] = "((studiengang_kz = " . $this->db->escape($studiengang_kz) . " AND semester = " . $this->db->escape($semester) . ") OR (studiengang_kz = " . $this->db->escape($studiengang_kz) . " AND semester = 0) OR (studiengang_kz = 0 AND semester = " . $this->db->escape($semester) . ")" . $add . ")";
if ($studiengang_kz !== '') {
$sgAndSemesterCondition[] = 'studiengang_kz = ' . $this->db->escape($studiengang_kz);
}
if ($semester !== null && $semester !== 0) {
$sgAndSemesterCondition[] = 'semester = ' . $this->db->escape($semester);
}
$targetedSgAndSemesterCondition = [];
if (!empty($sgAndSemesterCondition)) {
$targetedSgAndSemesterCondition[] = '(' . implode(' AND ', $sgAndSemesterCondition) . ')';
}
if ($mischen === true) {
$targetedSgAndSemesterCondition[] = '(studiengang_kz = 0 AND (semester = 0 OR semester IS NULL))';
if ($semester !== null && $semester !== 0) {
$targetedSgAndSemesterCondition[] = '(studiengang_kz = 0 AND semester = ' . $this->db->escape($semester) . ')';
}
}
$finalSgAndSemesterCondition = implode(' OR ', $targetedSgAndSemesterCondition);
if ($finalSgAndSemesterCondition !== '') {
$where[] = "(" . $finalSgAndSemesterCondition . ")";
}
$this->addJoin('campus.tbl_contentsprache cs', 'content_id');
if (is_bool($sichtbar) || $sichtbar === 'true' || $sichtbar === 'false') {
$isSichtbar = filter_var($sichtbar, FILTER_VALIDATE_BOOLEAN);
$where[] = "cs.sichtbar = " . ($isSichtbar ? "true" : "false");
}
$where[] = "cs.sichtbar = " . ($sichtbar ? "true" : "false");
$where[] = "cs.sprache = (CASE WHEN EXISTS(SELECT 1 FROM campus.tbl_contentsprache cs2 WHERE cs2.content_id=" . $this->dbTable . ".content_id AND sprache=" . $this->db->escape($sprache) . ") THEN " . $this->db->escape($sprache) . " ELSE " . $this->db->escape(DEFAULT_LANGUAGE) . " END)";
$where[] = "cs.version = (SELECT MAX(version) FROM campus.tbl_contentsprache cs3 WHERE cs3.content_id=" . $this->dbTable . ".content_id AND cs3.sprache = (CASE WHEN EXISTS(SELECT 1 FROM campus.tbl_contentsprache cs2 WHERE cs2.content_id=" . $this->dbTable . ".content_id AND sprache=" . $this->db->escape($sprache) . ") THEN " . $this->db->escape($sprache) . " ELSE " . $this->db->escape(DEFAULT_LANGUAGE) . " END))";
if ($filterForDegreePrograms) {
if (empty($allowedDegreePrograms)) {
$where[] = "1=0";
} else {
$where[] = "studiengang_kz IN (" . implode(',', array_map([$this->db, 'escape'], $allowedDegreePrograms)) . ")";
}
}
$where = implode(" AND ", $where);
@@ -136,16 +110,25 @@ class News_model extends DB_Model
}
public function getNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz = null, $sichtbar = true, $page = 1, $page_size = 10, $active = true, $mischen = true, $filterForDegreePrograms = false, $allowedDegreePrograms = [])
public function getNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz = null, $sichtbar = true, $maxalter = 0, $page = 1, $page_size = 10, $all = false, $mischen = true)
{
$this->prepareNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz, $sichtbar, $page, $page_size, $active, $mischen, $filterForDegreePrograms, $allowedDegreePrograms);
$this->prepareNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz, $sichtbar, $maxalter, $page, $page_size, $all, $mischen);
// getting the number of rows of the query and adding pagination to the query result
$num_rows = $this->getNumRows(true);
$this->addPagination($page, $page_size, $num_rows);
// preparing the query again because every call to get_compiled_select or cour_all_results will add the from clause to the query
$this->prepareNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz, $sichtbar, $page, $page_size, $active, $mischen, $filterForDegreePrograms, $allowedDegreePrograms);
$this->prepareNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz, $sichtbar, $maxalter, $page, $page_size, $all, $mischen);
return $this->load();
}
public function countNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz = null, $sichtbar = true, $maxalter = 0, $page = 1, $page_size = 10, $all = false, $mischen = true)
{
$this->prepareNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz, $sichtbar, $maxalter, $page, $page_size, $all, $mischen);
return $this->getNumRows();
}
}
@@ -11,4 +11,30 @@ class Template_model extends DB_Model
$this->dbTable = 'campus.tbl_template';
$this->pk = 'template_kurzbz';
}
/**
* True if the template carries a CIS4 stylesheet of its own, which is not true for every template.
*
* @param string $template_kurzbz
* @return stdClass success(bool) or error
*/
public function hasCis4Stylesheet($template_kurzbz)
{
$query = '
SELECT (xslt_xhtml_c4 IS NOT NULL
AND xslt_xhtml_c4::text IS DISTINCT FROM xslt_xhtml::text) AS eigen
FROM campus.tbl_template
WHERE template_kurzbz = ?
';
$result = $this->execReadOnlyQuery($query, [$template_kurzbz]);
if (isError($result))
return $result;
$data = getData($result);
if (empty($data))
return success(false);
return success($data[0]->eigen === 't' || $data[0]->eigen === true);
}
}
@@ -88,30 +88,6 @@ class Prestudent_model extends DB_Model
return $this->execQuery($query, $parametersArray);
}
/**
* Gets the Studiengang and current Ausbildungssemester for every
* Prestudent belonging to the same person as the student.
*
* @param string $student_uid
* @return stdClass
*/
public function getStudiengaengAndAusbildungssemesterByStudentUid($student_uid)
{
$query = 'SELECT p.studiengang_kz,
(
SELECT ps.ausbildungssemester
FROM public.tbl_prestudentstatus ps
WHERE ps.prestudent_id = p.prestudent_id
ORDER BY ps.datum DESC, ps.insertamum DESC, ps.ext_id DESC
LIMIT 1
) AS ausbildungssemester
FROM public.tbl_student s
JOIN public.tbl_prestudent p USING (prestudent_id)
WHERE s.student_uid = ?';
return $this->execQuery($query, array($student_uid));
}
/**
* updateAufnahmegruppe
*/
@@ -100,14 +100,12 @@ 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,53 +52,4 @@ 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);
}
}
@@ -340,20 +340,6 @@ EOSQL;
}
public function getByLeStudiensemester($lehreinheit_id, $studiensemester_kurzbz)
{
$qry = "WITH lehreinheiten AS (
SELECT *
FROM lehre.tbl_lehreinheit
WHERE lehreinheit_id = ?
AND studiensemester_kurzbz = ?
)," . $this->_getLeCTE();
$params = array($lehreinheit_id, $studiensemester_kurzbz);
return $this->execReadOnlyQuery($qry, $params);
}
public function getByLvidStudiensemester($lv_id, $studiensemester_kurzbz, $mitarbeiter_uid = null, $fachbereich_kurzbz = null)
{
$qry = "WITH lehreinheiten AS (
@@ -361,7 +347,35 @@ EOSQL;
FROM lehre.tbl_lehreinheit
WHERE lehrveranstaltung_id = ?
AND studiensemester_kurzbz = ?
)," . $this->_getLeCTE();
),
". $this->_getGruppenCTE() . ",
". $this->_getLektorenCTE() . ",
". $this->_getFachbereichCTE() . ",
". $this->_getTagsCTE() . "
SELECT lehreinheiten.*,
lehreinheiten.lehrform_kurzbz as lv_lehrform_kurzbz,
tbl_lehrveranstaltung.kurzbz as lv_kurzbz,
tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung,
COALESCE(tag_data_agg.tags, '[]'::json) AS tags,
gruppen.gruppen,
mitarbeiter.lektoren,
mitarbeiter.le_planstunden,
mitarbeiter.vorname,
mitarbeiter.nachname,
mitarbeiter.semesterstunden,
fachbereich.bezeichnung as fachbereich,
UPPER(CONCAT(tbl_studiengang.typ,tbl_studiengang.kurzbz)) as studiengang,
semester
FROM lehreinheiten
LEFT JOIN lehre.tbl_lehrveranstaltung ON tbl_lehrveranstaltung.lehrveranstaltung_id = lehreinheiten.lehrfach_id
LEFT JOIN public.tbl_studiengang USING(studiengang_kz)
LEFT JOIN tag_data_agg ON tag_data_agg.lehreinheit_id = lehreinheiten.lehreinheit_id
LEFT JOIN mitarbeiter ON lehreinheiten.lehreinheit_id = mitarbeiter.lehreinheit_id
LEFT JOIN fachbereich ON lehreinheiten.lehreinheit_id = fachbereich.lehreinheit_id
LEFT JOIN gruppen ON lehreinheiten.lehreinheit_id = gruppen.lehreinheit_id
WHERE true
";
$params = array($lv_id, $studiensemester_kurzbz);
@@ -381,83 +395,9 @@ EOSQL;
return $this->execReadOnlyQuery($qry, $params);
}
public function getOffeneStunden($lehreinheit_ids)
private function getLVTmp($stg_kz = null)
{
if (!is_array($lehreinheit_ids))
$lehreinheit_ids = [$lehreinheit_ids];
if (empty($lehreinheit_ids))
return success([]);
$db = new DB_Model();
$placeholders = implode(',', array_fill(0, count($lehreinheit_ids), '?'));
$qry = "WITH eventdauer AS (
SELECT
tbl_kalender_lehreinheit.lehreinheit_id,
tbl_kalender.von,
tbl_kalender.bis,
tbl_kalender.kalender_id
FROM lehre.tbl_lehreinheit
JOIN lehre.tbl_kalender_lehreinheit USING(lehreinheit_id)
JOIN lehre.tbl_kalender USING(kalender_id)
WHERE tbl_lehreinheit.lehreinheit_id IN ($placeholders)
AND NOT EXISTS (SELECT 1 FROM lehre.tbl_kalender nachfolger
WHERE nachfolger.vorgaenger_kalender_id = tbl_kalender.kalender_id)
AND tbl_kalender.status_kurzbz NOT IN ('deleted', 'todelete', 'archived')
GROUP BY tbl_kalender_lehreinheit.lehreinheit_id, tbl_kalender.von, tbl_kalender.bis, tbl_kalender.kalender_id
),
raster AS (
SELECT
eventdauer.lehreinheit_id,
eventdauer.kalender_id,
eventdauer.von,
eventdauer.bis,
vonstunde.stunde AS vonstunde_nr,
bisstunde.stunde AS bisstunde_nr
FROM eventdauer
LEFT JOIN lehre.tbl_stunde vonstunde ON eventdauer.von::time = vonstunde.beginn
LEFT JOIN lehre.tbl_stunde bisstunde ON eventdauer.bis::time = bisstunde.ende
),
unterrichtsdauer AS (
SELECT
raster.lehreinheit_id,
raster.kalender_id,
sum(EXTRACT(EPOCH FROM (LEAST(stunde.ende, raster.bis::time) - GREATEST(stunde.beginn, raster.von::time))) / 60) AS minuten
FROM raster JOIN lehre.tbl_stunde stunde ON stunde.beginn < raster.bis::time AND stunde.ende > raster.von::time
WHERE raster.vonstunde_nr IS NULL OR raster.bisstunde_nr IS NULL
GROUP BY raster.lehreinheit_id, raster.kalender_id
),
verplantestunden AS (
SELECT raster.lehreinheit_id, raster.kalender_id,
CASE
WHEN raster.vonstunde_nr IS NOT NULL AND raster.bisstunde_nr IS NOT NULL
THEN (raster.bisstunde_nr - raster.vonstunde_nr + 1)
ELSE coalesce(unterrichtsdauer.minuten, 0) / 45
END AS verplantestunde
FROM raster LEFT JOIN unterrichtsdauer ON unterrichtsdauer.lehreinheit_id = raster.lehreinheit_id AND unterrichtsdauer.kalender_id = raster.kalender_id
),
verplant_gesamt AS (
SELECT lehreinheit_id, SUM(verplantestunde) AS verplant
FROM verplantestunden
GROUP BY lehreinheit_id
)
SELECT
lema.lehreinheit_id,
lema.mitarbeiter_uid,
lema.planstunden,
ROUND(coalesce(verplant_gesamt.verplant, 0), 2) AS verplant,
ROUND((lema.planstunden - coalesce(verplant_gesamt.verplant, 0)), 2) AS offenestunden
FROM lehre.tbl_lehreinheitmitarbeiter lema
LEFT JOIN verplant_gesamt ON verplant_gesamt.lehreinheit_id = lema.lehreinheit_id
WHERE lema.lehreinheit_id IN ($placeholders)";
return $db->execReadOnlyQuery($qry, array_merge($lehreinheit_ids, $lehreinheit_ids));
}
private function getLVTmp()
{
return "SELECT DISTINCT ON(lehrveranstaltung_id) *,
$qry = "SELECT DISTINCT ON(lehrveranstaltung_id) *,
'' as stundenblockung,
'' as lehreinheit_id,
'' as wochenrythmus,
@@ -468,33 +408,24 @@ EOSQL;
'' as studienplan_beeichnung,
UPPER(CONCAT(vw_lehreinheit.stg_typ, vw_lehreinheit.stg_kurzbz)) as studiengang
FROM campus.vw_lehreinheit
WHERE studiensemester_kurzbz = ? ";
WHERE mitarbeiter_uid = ?
AND studiensemester_kurzbz = ?";
if (!is_null($stg_kz)) {
$qry .= " AND lv_studiengang_kz = ?";
}
return $qry;
}
public function getLvsById($lehrveranstaltung_id, $studiensemester_kurzbz)
{
$where = ' AND lehrveranstaltung_id = ?';
$qry = "WITH lvs AS (" . $this->getLVTmp() . $where .")
SELECT lvs.*
FROM lvs
";
$params = array($studiensemester_kurzbz, $lehrveranstaltung_id);
return $this->execReadOnlyQuery($qry, $params);
}
public function getLvsByEmployee($mitarbeiter_uid, $studiensemester_kurzbz, $stg_kz = null)
{
$where = ' AND mitarbeiter_uid = ?';
if ($stg_kz != null)
$where .= " AND lv_studiengang_kz = ?";
$qry = "WITH lvs AS (" . $this->getLVTmp() . $where .")
$qry = "WITH lvs AS (" . $this->getLVTmp($stg_kz) . ")
SELECT lvs.*
FROM lvs";
$params = array($studiensemester_kurzbz, $mitarbeiter_uid);
FROM lvs
";
$params = array($mitarbeiter_uid, $studiensemester_kurzbz);
if (!is_null($stg_kz))
{
$params[] = $stg_kz;
@@ -701,36 +632,6 @@ EOSQL;
return success('Contract successfully updated.');
}
private function _getLeCTE()
{
return $this->_getGruppenCTE() . ",
". $this->_getLektorenCTE() . ",
". $this->_getFachbereichCTE() . ",
". $this->_getTagsCTE() . "
SELECT lehreinheiten.*,
lehreinheiten.lehrform_kurzbz as lv_lehrform_kurzbz,
tbl_lehrveranstaltung.kurzbz as lv_kurzbz,
tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung,
COALESCE(tag_data_agg.tags, '[]'::json) AS tags,
gruppen.gruppen,
mitarbeiter.lektoren,
mitarbeiter.le_planstunden,
mitarbeiter.vorname,
mitarbeiter.nachname,
mitarbeiter.semesterstunden,
fachbereich.bezeichnung as fachbereich,
UPPER(CONCAT(tbl_studiengang.typ,tbl_studiengang.kurzbz)) as studiengang,
semester
FROM lehreinheiten
LEFT JOIN lehre.tbl_lehrveranstaltung ON tbl_lehrveranstaltung.lehrveranstaltung_id = lehreinheiten.lehrfach_id
LEFT JOIN public.tbl_studiengang USING(studiengang_kz)
LEFT JOIN tag_data_agg ON tag_data_agg.lehreinheit_id = lehreinheiten.lehreinheit_id
LEFT JOIN mitarbeiter ON lehreinheiten.lehreinheit_id = mitarbeiter.lehreinheit_id
LEFT JOIN fachbereich ON lehreinheiten.lehreinheit_id = fachbereich.lehreinheit_id
LEFT JOIN gruppen ON lehreinheiten.lehreinheit_id = gruppen.lehreinheit_id
WHERE true";
}
private function _getGruppenCTE()
{
return "gruppen AS (
@@ -840,69 +741,4 @@ 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]);
}
public function getLehrenheitenBySemester($uid, $semester)
{
$query = "SELECT
tbl_lehreinheit.lehreinheit_id,
lehrfach.kurzbz as lehrfach,
tbl_lehreinheit.lehrform_kurzbz as lehrform,
tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung,
(SELECT kurzbz FROM public.tbl_mitarbeiter
WHERE mitarbeiter_uid=tbl_lehreinheitmitarbeiter.mitarbeiter_uid) as lektor,
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as stg_kurzbz,
tbl_lehrveranstaltung.semester as lv_semester,
tbl_lehreinheit.raumtyp,
tbl_lehreinheit.raumtypalternativ,
tbl_lehreinheit.stundenblockung,
tbl_lehreinheit.wochenrythmus,
tbl_lehreinheitmitarbeiter.semesterstunden as semesterstunden,
tbl_lehreinheit.start_kw,
lvangebot.anmeldefenster_start,
lvangebot.anmeldefenster_ende,
tbl_lehrveranstaltung.lehrveranstaltung_id
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)
LEFT JOIN lehre.tbl_lvangebot as lvangebot ON(tbl_lehrveranstaltung.lehrveranstaltung_id=lvangebot.lehrveranstaltung_id)
WHERE
tbl_lehreinheit.studiensemester_kurzbz = '$semester'
AND mitarbeiter_uid = '$uid'
ORDER BY stg_kurzbz,lv_semester,lv_bezeichnung";
return $this->execReadOnlyQuery($query);
}
public function getGroupsByLehreinheit($lehreinheitId)
{
$query = "SELECT *
FROM lehre.tbl_lehreinheitgruppe
WHERE lehreinheit_id='$lehreinheitId'";
return $this->execReadOnlyQuery($query);
}
}
@@ -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, campus.vw_student_lehrveranstaltung.lehreinheit_id
tbl_student.prestudent_id
FROM
campus.vw_student_lehrveranstaltung
JOIN public.tbl_benutzer USING(uid)
@@ -1379,114 +1379,4 @@ 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_studiengang.studiengang_kz,
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,
lehre.tbl_lehrveranstaltung.orgform_kurzbz as orgform
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]);
}
/**
* All Lehrveranstaltungen of a Studiengang offered in a Studiensemester.
*/
public function getLvForStudiengangInSemester($sem_kurzbz, $studiengang_kz) {
$qry = "SELECT DISTINCT (tbl_lehrveranstaltung.lehrveranstaltung_id),
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as stg_kurzbz,
tbl_studiengang.studiengang_kz,
tbl_lehrveranstaltung.semester as lv_semester,
tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung,
tbl_lehrveranstaltung.orgform_kurzbz as orgform
FROM
lehre.tbl_lehrveranstaltung JOIN public.tbl_studiengang USING(studiengang_kz)
WHERE
tbl_lehrveranstaltung.studiengang_kz = ?
AND tbl_lehrveranstaltung.aktiv = TRUE
AND EXISTS (
SELECT 1 FROM lehre.tbl_lehreinheit le
WHERE le.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
AND le.studiensemester_kurzbz = ?
)
ORDER BY stg_kurzbz,lv_semester,lv_bezeichnung";
return $this->execReadOnlyQuery($qry, array($studiengang_kz, $sem_kurzbz));
}
/**
* Number of Lehreinheit assignments the given mitarbeiter has for a Lehrveranstaltung (i.e. whether
* they teach it), optionally restricted to a Studiensemester. Returns a single row with a `teaches`
* count - check `teaches > 0`. Used to scope teacher access in the Benotungstool to their own LVs.
*/
public function getLektorIsTeachingLva($lva_id, $ma_uid, $studiensemester_kurzbz = null)
{
$params = array($lva_id, $ma_uid);
$query = "SELECT COUNT(*) AS teaches
FROM lehre.tbl_lehreinheitmitarbeiter
JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
WHERE lehrveranstaltung_id = ? AND mitarbeiter_uid = ?";
if ($studiensemester_kurzbz !== null && $studiensemester_kurzbz !== '') {
$query .= " AND tbl_lehreinheit.studiensemester_kurzbz = ?";
$params[] = $studiensemester_kurzbz;
}
return $this->execReadOnlyQuery($query, $params);
}
}
@@ -14,7 +14,7 @@ class Lvgesamtnote_model extends DB_Model
}
/**
* Laedt die Noten - lvgesamtnote (Vorschlag) JOIN tbl.note (zeugnisnote)
* Laedt die Noten
*
* @param integer $lehrveranstaltung_id
* @param string $student_uid
@@ -50,19 +50,4 @@ 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);
}
}
+1 -22
View File
@@ -11,33 +11,12 @@ 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);
}
}
@@ -13,7 +13,7 @@ class Notenschluesselaufteilung_model extends DB_Model
}
/**
* Gives the grade for the points of a course.
* Liefert die Note zu Punkten einer Lehrveranstaltung
*
* @param number $points
* @param integer $lehrveranstaltung_id
@@ -23,18 +23,9 @@ class Notenschluesselaufteilung_model extends DB_Model
*/
public function getNote($points, $lehrveranstaltung_id, $studiensemester_kurzbz)
{
// Without a valid points value you cannot derive a grade. Without this guard the query
// builder makes a bad statement from "punkte <=" => null ('punkte' < 'IS' 'NULL'). The
// request then fails with HTTP 500 instead of an answer that says "no grade".
if (!is_numeric($points))
return success(null);
$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);
@@ -60,172 +60,6 @@ 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_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_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,10 +23,8 @@ 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_lehrveranstaltung.lehrveranstaltung_id,
tbl_sprache.bezeichnung[".$sprache_index."] AS sprache_bezeichnung,
pa.*, tbl_projekttyp.bezeichnung,
pa.studiensemester_kurzbz, pa.lehrveranstaltung_id,
tbl_firma.name AS firma_name,
(
@@ -45,7 +43,6 @@ class Projektarbeit_model extends DB_Model
JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
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,5 +306,4 @@ class Pruefung_model extends DB_Model
return $this->loadWhereCommitteeExamsFailed();
}
}
@@ -515,75 +515,4 @@ class Studierendenantrag_model extends DB_Model
return hasData($this->load());
}
/**
* Loads all todos for a certain studiengangsleiter
*
* @param integer $stg_kz
*
* @return stdClass
*/
public function getOpenAntraegeForStgl($stg_kz)
{
$qry = "
SELECT
studierendenantrag_id,
prestudent_id,
typ,
person.nachname,
person.vorname,
ps.studiengang_kz
FROM campus.tbl_studierendenantrag a
JOIN public.tbl_prestudent ps USING (prestudent_id)
JOIN public.tbl_person person USING (person_id)
WHERE studiengang_kz in ?
AND (
(typ IN ('Abmeldung', 'AbmeldungStgl', 'Unterbrechung')
AND campus.get_status_studierendenantrag(a.studierendenantrag_id) = 'Erstellt')
OR
(typ = 'Wiederholung'
AND campus.get_status_studierendenantrag(a.studierendenantrag_id) = 'Lvszugewiesen')
)
ORDER BY typ;
";
$params = array($stg_kz);
return $this->execQuery($qry, $params);
}
/**
* Lists all open antraege for a certain stg
*
* @param Array $stg_kz
*
* @return stdClass
*/
public function getOpenAntraegeForAss($stg_kz)
{
$qry = "
SELECT
UPPER(stg.typ) || UPPER(stg.kurzbz) || ' ' || stg.bezeichnung AS studiengang,
COUNT(*) AS anzahl
FROM campus.tbl_studierendenantrag
JOIN public.tbl_prestudent p USING (prestudent_id)
JOIN public.tbl_studiengang stg ON p.studiengang_kz=stg.studiengang_kz
JOIN campus.tbl_studierendenantrag_status as s ON campus.get_status_id_studierendenantrag(campus.tbl_studierendenantrag.studierendenantrag_id) = studierendenantrag_status_id
JOIN campus.tbl_studierendenantrag_statustyp as st USING (studierendenantrag_statustyp_kurzbz)
WHERE (
s.studierendenantrag_statustyp_kurzbz NOT IN('Zurueckgezogen', 'Genehmigt', 'Abgelehnt', 'EinspruchAbgelehnt', 'Abgemeldet', 'Pause', 'EmailVersandt')
OR (
s.studierendenantrag_statustyp_kurzbz = 'Genehmigt'
AND tbl_studierendenantrag.typ = 'AbmeldungStgl'
)
)
AND p.studiengang_kz IN ?
GROUP BY studiengang
ORDER BY studiengang
"
;
$params = array($stg_kz);
return $this->execQuery($qry, $params);
}
}
@@ -259,4 +259,37 @@ class Organisationseinheit_model extends DB_Model
return $this->execReadOnlyQuery($qry, array($oe_kurzbz));
}
/**
* The unit itself plus its sub units, down to a depth.
*
* tiefe 0 returns only the unit named, which is what a caller without a depth wants.
* The depth bound also ends a cycle: a unit whose parent chain leads back to itself
* would otherwise recurse forever.
*
* @param string $oe_kurzbz root of the walk
* @param int $tiefe how many levels below the root to include
* @return object success with array of rows or error
*/
public function getMitUntereinheiten($oe_kurzbz, $tiefe)
{
$query = '
WITH RECURSIVE unter(oe_kurzbz, tiefe) AS (
SELECT oe_kurzbz, 0
FROM public.tbl_organisationseinheit
WHERE oe_kurzbz = ?
UNION ALL
SELECT o.oe_kurzbz, unter.tiefe + 1
FROM public.tbl_organisationseinheit o
JOIN unter ON o.oe_parent_kurzbz = unter.oe_kurzbz
WHERE unter.tiefe < ?
)
SELECT oe_kurzbz, MIN(tiefe) AS tiefe
FROM unter
GROUP BY oe_kurzbz
ORDER BY MIN(tiefe), oe_kurzbz
';
return $this->execReadOnlyQuery($query, [$oe_kurzbz, (int) $tiefe]);
}
}
@@ -653,42 +653,31 @@ class Studiengang_model extends DB_Model
}
/**
* Gets the Studiengang information. Without parameters it uses the Studiengang
* of the logged in Student.
*
* @param int|null $studiengang_kz
* @param int|null $semester used for the Jahrgangsvertretung
* @return stdClass
*/
public function getStudiengangInfoForNews($studiengang_kz = null, $semester = null)
public function getStudiengangInfoForNews()
{
$this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
$this->load->model('person/Person_model', 'PersonModel');
$this->load->model('crm/Student_model', 'StudentModel');
if (is_null($studiengang_kz))
{
$student = $this->StudentModel->loadWhere(['student_uid' => getAuthUID()]);
if (isError($student))
return error($student);
if (getData($student)) {
$student = current(getData($student));
$studiengang_kz = $student->studiengang_kz;
$semester = $student->semester;
}
$student = $this->StudentModel->loadWhere(['student_uid' => getAuthUID()]);
if (isError($student))
return error($student);
if (getData($student)) {
$student = current(getData($student));
$studiengang_kz = $student->studiengang_kz;
$semester = $student->semester;
}
// users without a Student record get no information instead of an error
if (is_null($studiengang_kz))
return success(null);
$stg_obj = $this->load($studiengang_kz);
if(isError($stg_obj))
return error($stg_obj);
if(!hasData($stg_obj))
return error('Studiengang nicht gefunden');
$stg_obj = current(getData($stg_obj));
if(getData($stg_obj))
{
$stg_obj = current(getData($stg_obj));
}
$stg_ltg = $this->getLeitungDetailed($stg_obj->studiengang_kz);
if (isError($stg_ltg))
@@ -897,14 +886,6 @@ class Studiengang_model extends DB_Model
return success($ass);
}
public function getDegreePrograms() {
$query = "SELECT *
FROM public.tbl_studiengang
ORDER BY public.tbl_studiengang.kurzbzlang";
return $this->execReadOnlyQuery($query);
}
private function addEmailProperty(&$benutzerfunktionen) {
if(count($benutzerfunktionen) && defined('DOMAIN'))
{
@@ -59,37 +59,6 @@ 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)
{
@@ -292,29 +292,13 @@ class Studiensemester_model extends DB_Model
COALESCE(
(SELECT studiensemester_kurzbz FROM semester_interval_startdate),
(SELECT studiensemester_kurzbz FROM nearest_semester_interval_startdate)
) AS studiensemester_kurzbz,
COALESCE(
(SELECT start FROM semester_interval_startdate),
(SELECT start FROM nearest_semester_interval_startdate)
) as start,
COALESCE(
(SELECT ende FROM semester_interval_startdate),
(SELECT ende FROM nearest_semester_interval_startdate)
) as ende
) AS studiensemester_kurzbz
UNION
SELECT
COALESCE(
(SELECT studiensemester_kurzbz FROM semester_interval_enddate),
(SELECT studiensemester_kurzbz FROM nearest_semester_interval_enddate)
) AS studiensemester_kurzbz,
COALESCE(
(SELECT start FROM semester_interval_enddate),
(SELECT start FROM nearest_semester_interval_enddate)
) as start,
COALESCE(
(SELECT ende FROM semester_interval_enddate),
(SELECT ende FROM nearest_semester_interval_enddate)
) as ende
) AS studiensemester_kurzbz
EOSQL;
@@ -375,30 +359,6 @@ EOSQL;
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
@@ -319,40 +319,6 @@ class Benutzerfunktion_model extends DB_Model
return $record;
}
/**
* Get active Assistenz(en) of the user by UID.
* @param $uid
*/
public function getSTGAssByUID($uid)
{
$query = '
SELECT
uid,
oe_kurzbz,
studiengang_kz,
typ,
tbl_studiengang.bezeichnung
FROM
public.tbl_benutzerfunktion
JOIN public.tbl_studiengang USING (oe_kurzbz)
WHERE
funktion_kurzbz = \'ass\'
AND (datum_von IS NULL OR datum_von <= now())
AND (datum_bis IS NULL OR datum_bis >= now())
AND uid = ?
ORDER BY
oe_kurzbz
';
$parameters_array = array();
if (is_string($uid))
{
$parameters_array[] = $uid;
}
return $this->execQuery($query, $parameters_array);
}
function updateBenutzerfunktion($funktionJson)
{
$funktionJson['updatevon'] = getAuthUID();
@@ -243,6 +243,58 @@ class Person_model extends DB_Model
* @param $uid
* @return array
*/
/**
* Contact data of the named users, for the CMS content components.
*
* The joins follow Benutzerfunktion_model::getBenutzerFunktionenDetailed() and the view
* Cis/Cms/News/Xml/Address/Detailed.php, so a person reads the same everywhere.
*
* foto_sperre is honoured. A person who blocked their photo never gets one on a public
* content page, no matter what the page asks for.
*
* @param array $uids
* @param boolean $mitFoto photos are large, so they are read only on request
* @return object
*/
public function getCmsKontakt($uids, $mitFoto = false)
{
if (empty($uids))
return success(array());
$platzhalter = implode(', ', array_fill(0, count($uids), '?'));
$fotoSpalte = $mitFoto
? 'CASE WHEN p.foto_sperre THEN NULL ELSE p.foto END'
: 'NULL';
$query = '
SELECT
b.uid,
b.alias,
p.vorname,
p.nachname,
p.titelpre,
p.titelpost,
m.telefonklappe,
k.kontakt,
o.planbezeichnung,
' . $fotoSpalte . ' AS foto
FROM public.tbl_benutzer b
JOIN public.tbl_person p ON p.person_id = b.person_id
LEFT JOIN public.tbl_mitarbeiter m ON m.mitarbeiter_uid = b.uid
LEFT JOIN public.tbl_kontakt k
ON k.standort_id = m.standort_id AND k.kontakttyp = ?
LEFT JOIN public.tbl_ort o ON o.ort_kurzbz = m.ort_kurzbz
WHERE b.uid IN (' . $platzhalter . ')
AND b.aktiv
';
$parameter = array('telefon');
foreach ($uids as $uid)
$parameter[] = $uid;
return $this->execReadOnlyQuery($query, $parameter);
}
public function getByUid($uid)
{
$this->addSelect('vorname, nachname, gebdatum, person_id, bpk, matr_nr, foto');
@@ -98,6 +98,31 @@ class Mitarbeiter_model extends DB_Model
return $this->execQuery($qry, $params);
}
/**
* Active staff, names only, for a picker.
*
* getPersonal() returns tbl_person.* including svnr, bpk and
* gebdatum, which is not needed in a CMS endpoint.
*
* @return object
*/
public function getAktivesPersonalNamen()
{
$query = '
SELECT
b.uid,
p.vorname,
p.nachname
FROM public.tbl_benutzer b
JOIN public.tbl_mitarbeiter m ON m.mitarbeiter_uid = b.uid
JOIN public.tbl_person p ON p.person_id = b.person_id
WHERE b.aktiv
ORDER BY LOWER(p.nachname), LOWER(p.vorname)
';
return $this->execReadOnlyQuery($query);
}
/**
* gibt Personen mit Übersicht von Vertragsdaten aus
*
@@ -12,12 +12,6 @@ class Ort_model extends DB_Model
$this->pk = "ort_kurzbz";
}
public function getRoomInfo($ortKurzbz)
{
$this->addSelect("ort_kurzbz, ausstattung");
return $this->OrtModel->loadWhere(array("ort_kurzbz" => $ortKurzbz));
}
public function getAll($raumtyp_kurzbz)
{
$this->addOrder("ort_kurzbz");
@@ -24,7 +24,7 @@ class Reservierung_model extends DB_Model
$lvplan_reservierungen_query = "SELECT r.* , stund.beginn, stund.ende,
CASE
WHEN r.gruppe_kurzbz IS NOT NULL THEN r.gruppe_kurzbz
ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(r.semester AS varchar),'/'),COALESCE(CAST(r.verband AS varchar),'/'),COALESCE(CAST(r.gruppe AS varchar),'/'))
ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(r.semester AS varchar),'/'),COALESCE(CAST(r.verband AS varchar),'/'))
END as gruppen_kuerzel
FROM campus.vw_reservierung r
JOIN public.tbl_studiengang studg ON studg.studiengang_kz=r.studiengang_kz
@@ -39,7 +39,7 @@ class Reservierung_model extends DB_Model
$raum_reservierungen_query = "SELECT res.*, beginn, ende,
CASE
WHEN res.gruppe_kurzbz IS NOT NULL THEN res.gruppe_kurzbz
ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(res.semester AS varchar),'/'),COALESCE(CAST(res.verband AS varchar),'/'),COALESCE(CAST(res.gruppe AS varchar),'/'))
ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(res.semester AS varchar),'/'),COALESCE(CAST(res.verband AS varchar),'/'))
END as gruppen_kuerzel
FROM campus.vw_reservierung res
JOIN public.tbl_studiengang studg ON studg.studiengang_kz=res.studiengang_kz
@@ -50,12 +50,11 @@ class Reservierung_model extends DB_Model
$query_result = $this->execReadOnlyQuery("
SELECT
DISTINCT(insertvon),
'reservierung' as type, beginn, ende, datum, array_agg(DISTINCT reservierung_id) AS reservierung_id,
'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
@@ -63,7 +62,7 @@ class Reservierung_model extends DB_Model
" . $subquery . "
) AS subquery
GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung, insertvon
GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung
ORDER BY datum, beginn
", is_null($ort_kurzbz) ? [$uid ?? getAuthUID(), $uid ?? getAuthUID(), $start_date, $end_date] : [$ort_kurzbz, $start_date, $end_date]);
@@ -83,7 +82,7 @@ class Reservierung_model extends DB_Model
$raum_reservierungen_query = "SELECT res.*, beginn, ende,
CASE
WHEN res.gruppe_kurzbz IS NOT NULL THEN res.gruppe_kurzbz
ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(res.semester AS varchar),'/'),COALESCE(CAST(res.verband AS varchar),'/'),COALESCE(CAST(res.gruppe AS varchar),'/'))
ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(res.semester AS varchar),'/'),COALESCE(CAST(res.verband AS varchar),'/'))
END as gruppen_kuerzel
FROM campus.vw_reservierung res
JOIN public.tbl_studiengang studg ON studg.studiengang_kz=res.studiengang_kz
@@ -95,12 +94,11 @@ class Reservierung_model extends DB_Model
$query_result = $this->execReadOnlyQuery("
SELECT
DISTINCT(insertvon),
'reservierung' as type, beginn, ende, datum, array_agg(DISTINCT reservierung_id) AS reservierung_id,
'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
@@ -108,7 +106,7 @@ class Reservierung_model extends DB_Model
" . $subquery . "
) AS subquery
GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung, insertvon
GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung
ORDER BY datum, beginn
", [$uid ?? getAuthUID(), $start_date, $end_date]);
@@ -148,40 +146,4 @@ class Reservierung_model extends DB_Model
return $this->execReadOnlyQuery($qry, [$uid, $datum, $stunde]);
}
public function deleteReservation($reservierung_id) {
$uid = getAuthUID();
$result = $this->load($reservierung_id);
if (isError($result))
return $result;
if (!hasData($result))
return error('Reservierung nicht gefunden');
$row = $result->retval[0];
if ($row->uid !== $uid && $row->insertvon !== $uid)
return error('Keine Berechtigung');
return $this->delete($reservierung_id);
}
public function getMyReservation() {
$uid = getAuthUID();
$date = date("Y-m-d",time());
$qry = "SELECT vw_reservierung.*,
trim(COALESCE(vw_mitarbeiter.titelpre,'')||' '||COALESCE(vw_mitarbeiter.vorname,'')||' '||COALESCE(vw_mitarbeiter.nachname,'')||' '||COALESCE(vw_mitarbeiter.titelpost,'')) as reserviert_fuer,
trim(COALESCE(reserviert_von.titelpre,'')||' '||COALESCE(reserviert_von.vorname,'')||' '||COALESCE(reserviert_von.nachname,'')||' '||COALESCE(reserviert_von.titelpost,'')) as reserviert_von
FROM campus.vw_reservierung
JOIN campus.vw_mitarbeiter ON vw_reservierung.uid=vw_mitarbeiter.uid
LEFT JOIN campus.vw_mitarbeiter reserviert_von ON vw_reservierung.insertvon=reserviert_von.uid
WHERE datum >= ?
AND (vw_reservierung.uid= ? OR vw_reservierung.insertvon= ? )
ORDER BY datum, titel, ort_kurzbz, stunde";
return $this->execReadOnlyQuery($qry, [$date, $uid, $uid]);
}
}
+17 -282
View File
@@ -12,8 +12,6 @@ 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.
*
@@ -64,270 +62,6 @@ 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;
}
/**
* getZeitsperren of Mitarbeiter for the next days
* @ days: count of the intervall of the next days
* @return array
*/
public function getZeitsperrenForNextDays($days, $uid=null)
{
$von = date('Y-m-d');
$bis = date('Y-m-d', strtotime("+{$days} days"));
$paramsArray = [$von, $bis, $bis, $von];
if($uid)
$paramsArray[] = $uid;
$qry = "select
nachname,
vorname,
uid,
vondatum,
bisdatum,
vertretung_uid,
erreichbarkeit_kurzbz,
mv.kurzbz
from
campus.vw_mitarbeiter
join campus.tbl_zeitsperre on
(uid = mitarbeiter_uid)
left join public.tbl_mitarbeiter mv on campus.tbl_zeitsperre.vertretung_uid = mv.mitarbeiter_uid
where
((? <= bisdatum
and ? >= bisdatum)
or (?>= vondatum
and ? <= vondatum))";
if($uid)
$qry .= " and uid = ? ";
$qry .= "
order by nachname
";
$result = $this->execQuery($qry, $paramsArray);
return $result;
}
public function getMitarbeiterWithZeitsperren($von, $bis, $fix=false, $lector=false, $oe=null, $ass=false, $stg=false)
{
$paramsArray = [$von, $bis, $bis, $von];
if($oe)
{
$paramsArray[] = $oe;
}
if($stg)
{
$paramsArray[] = $stg;
}
$qry = "
SELECT
m.nachname,
m.vorname,
m.uid,
z.vondatum,
z.bisdatum,
z.vertretung_uid,
z.erreichbarkeit_kurzbz,
m.lektor,
m.fixangestellt,
mv.kurzbz
FROM campus.vw_mitarbeiter m
join public.tbl_benutzer bn on bn.uid = m.uid";
if($oe || $ass || $stg)
{
$qry .= " JOIN public.tbl_benutzerfunktion bf on bf.uid = bn.uid";
}
if($stg)
{
$qry .= " JOIN public.tbl_studiengang stg using (oe_kurzbz)";
}
$qry.= "
LEFT JOIN campus.tbl_zeitsperre z
ON m.uid = z.mitarbeiter_uid
AND (
(? <= z.bisdatum AND ? >= z.bisdatum)
OR
(? >= z.vondatum AND ? <= z.vondatum)
)
left join public.tbl_mitarbeiter mv on z.vertretung_uid = mv.mitarbeiter_uid
where bn.aktiv = true
";
if($fix) {
$qry.= " AND m.fixangestellt = true";
}
if($lector) {
$qry.= " AND m.lektor = true";
}
if ($oe)
{
$qry.= "
AND bf.funktion_kurzbz ='oezuordnung'
AND bf.oe_kurzbz= ?
AND (bf.datum_von is null or bf.datum_von <=now())
AND (bf.datum_bis is null or bf.datum_bis >= now())
";
}
if ($ass)
{
$qry.= "
AND bf.funktion_kurzbz ='ass'
AND (bf.datum_von is null or bf.datum_von <=now())
AND (bf.datum_bis is null or bf.datum_bis >= now())
";
}
if ($stg)
{
$qry.= "
AND stg.studiengang_kz = ?
";
}
$qry .= " order by nachname, vorname, uid";
$result = $this->execQuery($qry, $paramsArray);
return $result;
}
public function checkIfZeitsperreExists($uid, $datum, $stunde)
{
$this->db->select("*");
@@ -337,29 +71,30 @@ class Zeitsperre_model extends DB_Model
$this->db->group_start();
$this->db->where('vondatum <', $datum);
$this->db->or_group_start();
$this->db->where('vondatum', $datum);
$this->db->group_start();
$this->db->where('vonstunde <=', $stunde);
$this->db->or_where('vonstunde IS NULL', null, false);
$this->db->group_end();
$this->db->group_end();
$this->db->where('vondatum <', $datum);
$this->db->or_group_start();
$this->db->where('vondatum', $datum);
$this->db->group_start();
$this->db->where('vonstunde <=', $stunde);
$this->db->or_where('vonstunde IS NULL', null, false);
$this->db->group_end();
$this->db->group_end();
$this->db->group_end();
$this->db->group_start();
$this->db->where('bisdatum >', $datum);
$this->db->or_group_start();
$this->db->where('bisdatum', $datum);
$this->db->group_start();
$this->db->where('bisstunde >=', $stunde);
$this->db->or_where('bisstunde IS NULL', null, false);
$this->db->group_end();
$this->db->where('bisdatum >', $datum);
$this->db->or_group_start();
$this->db->where('bisdatum', $datum);
$this->db->group_start();
$this->db->where('bisstunde >=', $stunde);
$this->db->or_where('bisstunde IS NULL', null, false);
$this->db->group_end();
$this->db->group_end();
$this->db->group_end();
$this->db->group_end();
return $this->load();
}
}
@@ -11,20 +11,4 @@ class Zeitsperretyp_model extends DB_Model
$this->dbTable = 'campus.tbl_zeitsperretyp';
$this->pk = 'zeitsperretyp_kurzbz';
}
public function getTypenZeitsperren($excludedTypes = [])
{
$whereNotClause = "";
if (count($excludedTypes)) {
$excludedTypes = array_map(
function ($type) {
return "'$type'";
},
$excludedTypes
);
$excludedTypes = implode(", ", $excludedTypes);
$whereNotClause = "WHERE NOT zeitsperretyp_kurzbz IN ($excludedTypes)";
}
return $this->execQuery("SELECT * FROM $this->dbTable $whereNotClause ORDER BY beschreibung ASC");
}
}
@@ -12,4 +12,72 @@ class Webservicelog_model extends DB_Model
$this->dbTable = 'system.tbl_webservicelog';
$this->pk = 'webservicelog_id';
}
/**
* The most viewed contents since a date, with the title of each one.
* request_id is a varchar, so the join casts the content_id to text. A cast of
* request_id to a number would fail on a row that holds no number.
* The join also drops a log row whose content is deleted.
* @param string|null $since timestamp in the form Y-m-d H:i:s, null for the whole log
* @param string $sprache language of the title
* @param int $limit length of the ranking
* @return stdClass success with array of rows or error
*/
public function getContentClickCounts($since, $sprache, $limit)
{
$params = [$sprache, 'content'];
$sinceCondition = '';
if ($since !== null)
{
$sinceCondition = ' AND w.execute_time >= ?';
$params[] = $since;
}
$params[] = $limit;
$query = '
SELECT c.content_id, COUNT(*) AS hits,
(SELECT titel FROM campus.tbl_contentsprache
WHERE content_id = c.content_id AND sprache = ?
ORDER BY version LIMIT 1) AS titel
FROM system.tbl_webservicelog w
JOIN campus.tbl_content c ON c.content_id::text = w.request_id
WHERE w.webservicetyp_kurzbz = ?' . $sinceCondition . '
GROUP BY c.content_id
ORDER BY COUNT(*) DESC, c.content_id
LIMIT ?
';
return $this->execReadOnlyQuery($query, $params);
}
/**
* Views of one content since a date. A content outside the ranking still needs its
* own number.
* @param int $content_id
* @param string|null $since timestamp in the form Y-m-d H:i:s, null for the whole log
* @return stdClass success with int or error
*/
public function getContentClickCount($content_id, $since)
{
$query = '
SELECT COUNT(*) AS hits
FROM system.tbl_webservicelog
WHERE webservicetyp_kurzbz = ? AND request_id = ?
';
$params = ['content', (string) $content_id];
if ($since !== null)
{
$query .= ' AND execute_time >= ?';
$params[] = $since;
}
$result = $this->execReadOnlyQuery($query, $params);
if (isError($result))
return $result;
return success((int) getData($result)[0]->hits);
}
}
@@ -10,7 +10,6 @@ $includesArray = array(
'primevue3' => true,
'skipID' => '#fhccontent',
'vuedatepicker11' => true,
'tinymce5' => true,
'customCSSs' => array(
'public/css/components/verticalsplit.css',
'public/css/components/searchbar/searchbar.css',
@@ -22,14 +21,10 @@ $includesArray = array(
'public/css/components/FilterComponent.css',
'public/css/components/Profil.css',
'public/css/components/FormUnderline.css',
'public/css/components/inViewTooltip.css',
'public/css/components/abgabetool/abgabe.css',
'public/css/Cis4/Cms.css',
'public/css/Cis4/Studium.css',
'public/css/components/vue-datepicker.css',
'public/css/Cis4/Benotungstool.css',
'public/css/components/Zeitsperrenma.css', //70747
'public/css/Cis4/Zeitsperren.css', //77941
'public/css/Cis4/Contentcomponents.css',
'public/css/Cis4/Studium.css'
),
'customJSs' => array(
'vendor/npm-asset/primevue/accordion/accordion.min.js',
@@ -43,15 +38,11 @@ $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'
),
);
@@ -59,8 +50,6 @@ $includesArray = array(
$this->load->view('templates/CISVUE-Header', $includesArray);
?>
<div id="fhccontent" class="h-100" route=<?php echo $route ?>>
<router-view
:permissions="<?= htmlspecialchars(json_encode($permissions ?? [])); ?>"
></router-view>
<router-view></router-view>
</div>
<?php $this->load->view('templates/CISVUE-Footer', $includesArray); ?>
+35
View File
@@ -0,0 +1,35 @@
<?php
$includesArray = array(
'title' => 'CMS',
'axios027' => true,
'bootstrap5' => true,
'fontawesome6' => true,
'vue3' => true,
'primevue3' => true,
'tabulator6' => true,
'tinymce5' => true,
'phrases' => array('global', 'ui', 'cms'),
'customCSSs' => [
'public/css/components/verticalsplit.css',
'public/css/components/horizontalsplit.css',
'public/css/Cms.css'
],
'customJSs' => [
'vendor/npm-asset/primevue/tree/tree.min.js',
'vendor/npm-asset/primevue/checkbox/checkbox.min.js',
'vendor/npm-asset/primevue/textarea/textarea.min.js',
'vendor/moment/luxonjs/luxon.min.js'
],
'customJSModules' => ['public/js/apps/Cms.js']
);
$this->load->view('templates/FHC-Header', $includesArray);
?>
<div id="main">
<router-view
cms-root="<?= site_url('cms'); ?>"
auth-uid="<?= getAuthUID(); ?>"
default-language="<?= defined('DEFAULT_LANGUAGE') ? DEFAULT_LANGUAGE : 'German'; ?>"
<?= !empty($clickstats) ? 'clickstats' : ''; ?>
></router-view>
</div>
<?php $this->load->view('templates/FHC-Footer', $includesArray); ?>
+2 -2
View File
@@ -12,13 +12,13 @@ $includesArray = array(
'tinymce5' => true,
'phrases' => array(
'global',
'ui'
'ui',
),
'customCSSs' => [
'public/css/components/vue-datepicker.css',
'public/css/components/primevue.css',
'public/css/Vertragsverwaltung.css',
'public/css/components/Detailheader.css',
'public/css/components/Detailheader.css'
],
'customJSs' => [
#'vendor/npm-asset/primevue/tree/tree.min.js',
@@ -1,5 +1,7 @@
<?php
$includesArray = array(
$this->load->view(
'templates/FHC-Header',
array(
'title' => 'Lehrauftrag bestellen',
'jquery3' => true,
'jqueryui1' => true,
@@ -10,15 +12,8 @@ $includesArray = array(
'dialoglib' => true,
'navigationwidget' => true,
'addons' => true,
)
);
$this->load->view(
'templates/FHC-Header',
$includesArray
);
?>
<?php echo $this->widgetlib->widget('NavigationWidget'); ?>
@@ -38,12 +33,4 @@ $this->load->view(
</div>
</div>
<?php
$this->load->view(
'templates/FHC-Footer',
$includesArray
);
?>
<?php $this->load->view('templates/FHC-Footer'); ?>
@@ -1,6 +1,7 @@
<?php
$includesArray = array(
$this->load->view(
'templates/FHC-Header',
array(
'title' => 'Lehrauftrag bestellen',
'jquery3' => true,
'bootstrap3' => true,
@@ -8,13 +9,8 @@ $includesArray = array(
'sbadmintemplate3' => true,
'ajaxlib' => true,
'navigationwidget' => true,
)
);
$this->load->view(
'templates/FHC-Header',
$includesArray
);
?>
<?php echo $this->widgetlib->widget('NavigationWidget'); ?>
@@ -38,11 +34,4 @@ $this->load->view(
</div>
</div>
<?php
$this->load->view(
'templates/FHC-Footer',
$includesArray
);
?>
<?php $this->load->view('templates/FHC-Footer'); ?>
@@ -1,101 +1,89 @@
<?php
$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'
),
'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',
$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'
),
'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'
)
),
'lehre' => array(
'lehrauftraegeAnnehmen',
'lehrauftraegeAnnehmenText',
'lehrauftraegeAnnehmenKlickStatusicon',
'lehrauftraegeAnnehmenLehrauftraegeWaehlen',
'lehrauftraegeAnnehmenMitKlickAnnehmen',
'lehrauftraegeNichtAuswaehlbar',
'lehrauftraegeNichtAuswaehlbarTextBeiAnnahme',
'filterAlleBeiAnnahme',
'filterErteiltBeiAnnahme',
'filterAngenommen'
)
),
'customJSs' => array(
'public/js/bootstrapper.js',
'public/js/lehre/lehrauftrag/acceptLehrauftrag.js'
'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
);
}
?>
@@ -242,17 +230,5 @@ if (defined("CIS4")) {
</div><!-- end page-wrapper -->
<br>
<?php
if (defined("CIS4")) {
$this->load->view(
'templates/CISVUE-Footer',
$includesArray
);
} else {
$this->load->view(
'templates/FHC-Footer',
$includesArray
);
}
?>
<?php $this->load->view('templates/FHC-Footer'); ?>
@@ -1,101 +1,98 @@
<?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',
$includesArray
'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'
)
)
);
?>
@@ -211,13 +208,5 @@ $this->load->view(
</div><!-- end page-wrapper -->
<br>
<?php
$this->load->view(
'templates/FHC-Footer',
$includesArray
);
?>
<?php $this->load->view('templates/FHC-Footer'); ?>
@@ -1,101 +1,98 @@
<?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',
$includesArray
'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'
)
)
);
?>
@@ -212,11 +209,5 @@ $this->load->view(
</div><!-- end page-wrapper -->
<br>
<?php
$this->load->view(
'templates/FHC-Footer',
$includesArray
);
?>
<?php $this->load->view('templates/FHC-Footer'); ?>
+25 -36
View File
@@ -9,8 +9,22 @@ $sitesettings = array(
'ajaxlib' => true,
'sbadmintemplate3' => true,
'phrases' => array(
'abschlusspruefung',
'ui'
'abschlusspruefung' => array(
'freigegebenAm',
'pruefungGespeichert',
'pruefungSpeichernFehler',
'abschlussbeurteilungLeer',
'beginnzeitLeer',
'beginnzeitFormatError',
'endezeitLeer',
'endezeitFormatError',
'endezeitBeforeError',
'verfNotice'
),
'ui' => array(
'stunde',
'minute'
)
),
'customCSSs' => array(
'public/css/sbadmin2/admintemplate_contentonly.css',
@@ -23,18 +37,10 @@ $sitesettings = array(
)
);
if(defined('CIS4')){
$this->load->view(
'templates/CISVUE-Header',
$sitesettings
);
}else{
$this->load->view(
'templates/FHC-Header',
$sitesettings
);
}
$this->load->view(
'templates/FHC-Header',
$sitesettings
);
?>
<div id="wrapper">
<div id="page-wrapper">
@@ -155,14 +161,6 @@ if(defined('CIS4')){
<?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')); ?>
@@ -203,9 +201,7 @@ if(defined('CIS4')){
<div class="col-lg-12 text-right">
<p>
<?php $freigegeben = isset($abschlusspruefung->freigabedatum); ?>
<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>
<button id="saveProtocolBtn" class="btn btn-default"<?php echo $freigegeben ? " disabled" : "" ?>><?php echo $this->p->t('ui', 'speichern') ?></button>
</p>
</div>
</div>
@@ -240,14 +236,7 @@ if(defined('CIS4')){
</div>
</div>
<?php
if (defined('CIS4')) {
$this->load->view(
'templates/CISVUE-Footer',
$sitesettings
);
} else {
$this->load->view(
'templates/FHC-Footer',
$sitesettings
);
}
$this->load->view(
'templates/FHC-Footer',
$sitesettings
);
@@ -1,45 +1,33 @@
<?php
$includesArray = array(
'title' => 'Prüfungsprotokoll',
'jquery3' => true,
'jqueryui1' => true,
'jquerycheckboxes1' => true,
'bootstrap3' => true,
'fontawesome4' => true,
'tablesorter2' => true,
'tabulator5' => true,
'tabulator5JQuery' => true,
'cis'=>true,
'ajaxlib' => true,
'dialoglib' => true,
'tablewidget' => true,
'phrases' => array(
'ui' => array(
'keineDatenVorhanden',
'heute',
'letzteWoche',
'alle',
'zeitraum'
)
),
'customCSSs' => array('public/css/sbadmin2/tablesort_bootstrap.css'),
'customJSs' => array('public/js/bootstrapper.js')
$this->load->view(
'templates/FHC-Header',
array(
'title' => 'Prüfungsprotokoll',
'jquery3' => true,
'jqueryui1' => true,
'jquerycheckboxes1' => true,
'bootstrap3' => true,
'fontawesome4' => true,
'tablesorter2' => true,
'tabulator5' => true,
'tabulator5JQuery' => true,
'cis'=>true,
'ajaxlib' => true,
'dialoglib' => true,
'tablewidget' => true,
'phrases' => array(
'ui' => array(
'keineDatenVorhanden',
'heute',
'letzteWoche',
'alle',
'zeitraum'
)
),
'customCSSs' => array('public/css/sbadmin2/tablesort_bootstrap.css'),
'customJSs' => array('public/js/bootstrapper.js')
)
);
if(defined("CIS4") && CIS4)
{
$this->load->view(
'templates/CISVUE-Header',
$includesArray
);
}
else
{
$this->load->view(
'templates/FHC-Header',
$includesArray
);
}
?>
<div id="page-wrapper">
@@ -78,15 +66,4 @@
</div>
</div>
<?php
if (defined("CIS4") && CIS4) {
$this->load->view(
'templates/CISVUE-Footer',
$includesArray
);
} else {
$this->load->view(
'templates/FHC-Footer',
$includesArray
);
}
<?php $this->load->view('templates/FHC-Footer'); ?>
@@ -5,11 +5,6 @@ $includesArray = array(
'bootstrap5' => true,
'fontawesome6' => true,
'axios027' => true,
'primevue3' => true,
'vuedatepicker11' => true,
'customJSs' => array_merge([
'vendor/moment/luxonjs/luxon.min.js'
], $customJSs ?? []),
'customJSModules' => array_merge([
'public/js/apps/Cis/Menu.js'
], $customJSModules ?? []),
+8 -18
View File
@@ -7,11 +7,6 @@ $includesArray = array(
'fontawesome6' => true,
'axios027' => true,
'primevue3' => true,
'vuedatepicker11' => true,
'printCssPath' => 'public/css/Cis4/print.css',
'customJSs' => array_merge([
'vendor/moment/luxonjs/luxon.min.js'
], $customJSs ?? []),
'customJSModules' => array_merge([
'public/js/apps/Cis/Menu.js'
], $customJSModules ?? []),
@@ -26,24 +21,19 @@ $this->load->view('templates/FHC-Header', $includesArray);
?>
<script type="text/javascript">
document.body.classList.add("cis4");
if (window.self !== window.top)
document.body.classList.add("in-frame");
</script>
<!-- used by cis4 native pages, teleported menu needs data attributes-->
<header id="cis-header" class="navbar-dark"
data-root-url="<?= site_url(''); ?>"
data-logo-url="<?= base_url($this->config->item('theme_logo')); ?>"
data-avatar-url="<?= site_url('Cis/Pub/bild/person/' . getAuthPersonId()); ?>"
data-logout-url="<?= site_url('Cis/Auth/logout'); ?>">
<!-- used by legacy cis pages & content pages which use component props -->
<cis-menu
root-url="<?= site_url(''); ?>"
logo-url="<?= base_url($this->config->item('theme_logo')); ?>"
avatar-url="<?= site_url('Cis/Pub/bild/person/' . getAuthPersonId()); ?>"
<header id="cis-header" class="navbar-dark">
<cis-menu
root-url="<?= site_url(''); ?>"
logo-url="<?= base_url($this->config->item('theme_logo')); ?>"
avatar-url="<?= site_url('Cis/Pub/bild/person/' . getAuthPersonId()); ?>"
logout-url="<?= site_url('Cis/Auth/logout'); ?>"
:searchbaroptions="searchbaroptions"
:searchfunction="searchfunction"
></cis-menu>
</header>
<main id="cis-main" class="flex-grow-1 p-4 pt-2" style="min-width: 0;">
<main id="cis-main" class="flex-grow-1 p-4 pt-2" style="min-width: 0;">
@@ -12,8 +12,6 @@
$customJSs = isset($customJSs) ? $customJSs : null;
$customJSModules = isset($customJSModules) ? $customJSModules : null;
$skipID = isset($skipID) ? $skipID : null;
$printCssPath = isset($printCssPath) ? $printCssPath : null;
?>
<!-- Header start -->
@@ -140,9 +138,6 @@
generateCSSsIncludeIfExtensionCssExists('tags.css');
}
// Print CSS
if ($printCssPath) generatePrintCSS($printCssPath);
$extapphelper = ExtendableAppsHelper::getInstance();
$extapphelper->init($customCSSs, $customJSs, $customJSModules);
+1 -1
View File
@@ -1,4 +1,4 @@
<div class="navbar-default sidebar top-0" role="navigation">
<div class="navbar-default sidebar" role="navigation">
<div class="sidebar-nav navbar-collapse">
<ul class="nav" id="side-menu"></ul>
</div>
@@ -1824,7 +1824,7 @@ $htmlstring .= "
</table>
";
if (isset($anzahlChanged) && $anzahlChanged > 0)
if ($anzahlChanged > 0)
echo '<div id="notenWarning" class="warning">' . $p->t('benotungstool/geaenderteNotenVorhanden') . '</div>';
echo $htmlstring;
+14 -47
View File
@@ -159,39 +159,6 @@ if (!$berechtigung->isBerechtigt('admin')
// let's merge
$format_title->setAlign('merge');
// Importkonfiguration aus dem CI-Config (application/config/noten.php) lesen, damit nur die
// Spalten des jeweils aktiven Importtyps hervorgehoben (grau) werden.
$CIS_GESAMTNOTE_NOTENIMPORT = false;
$CIS_GESAMTNOTE_PRUEFUNGSIMPORT = true;
$notenConfigFile = __DIR__.'/../../../application/config/noten.php';
if(is_file($notenConfigFile))
{
if(!defined('BASEPATH')) define('BASEPATH', true);
$config = array();
include($notenConfigFile);
if(isset($config['CIS_GESAMTNOTE_NOTENIMPORT'])) $CIS_GESAMTNOTE_NOTENIMPORT = $config['CIS_GESAMTNOTE_NOTENIMPORT'];
if(isset($config['CIS_GESAMTNOTE_PRUEFUNGSIMPORT'])) $CIS_GESAMTNOTE_PRUEFUNGSIMPORT = $config['CIS_GESAMTNOTE_PRUEFUNGSIMPORT'];
unset($config);
}
// plain (non-highlighted) variants used when an import type is disabled
$format_plain =& $workbook->addFormat();
$format_plain->setAlign('left');
$format_plain->setNumFormat(49);
$format_plainright =& $workbook->addFormat();
$format_plainright->setAlign('right');
$format_plainright->setNumFormat(49);
// highlight only the cells belonging to an enabled import flow:
// - Notenimport -> Personenkennzeichen + Note (main block)
// - Prüfungsimport -> Personenkennzeichen + Datum + Note (Termin2/Termin3 blocks)
$fmtNoteImportPkz = $CIS_GESAMTNOTE_NOTENIMPORT ? $format_highlight : $format_plain;
$fmtNoteImportNote = $CIS_GESAMTNOTE_NOTENIMPORT ? $format_highlightright : $format_plainright;
$fmtPruefImportPkz = $CIS_GESAMTNOTE_PRUEFUNGSIMPORT ? $format_highlight : $format_plain;
$fmtPruefImportDate = $CIS_GESAMTNOTE_PRUEFUNGSIMPORT ? $format_highlightright_date : $format_plainright;
$fmtPruefImportNote = $CIS_GESAMTNOTE_PRUEFUNGSIMPORT ? $format_highlightright : $format_plainright;
$worksheet->write(0,0,$p->t('anwesenheitsliste/notenliste')." ".($sprache=='English'?$lvobj->bezeichnung_english:$lvobj->bezeichnung),$format_bold);
$stg_obj = new studiengang($stg);
@@ -385,13 +352,13 @@ if (!$berechtigung->isBerechtigt('admin')
$worksheet->write($lines,3,$elem->vorname);
}
$worksheet->writeString($lines,4,$elem->semester.$elem->verband.$elem->gruppe);
$worksheet->write($lines,5,trim($elem->matrikelnr),$fmtNoteImportPkz);
$worksheet->write($lines,6, $note, $fmtNoteImportNote);
$worksheet->write($lines,5,trim($elem->matrikelnr),$format_highlight);
$worksheet->write($lines,6, $note, $format_highlightright);
// Nachprüfung
if (defined('CIS_GESAMTNOTE_PRUEFUNG_TERMIN2') && CIS_GESAMTNOTE_PRUEFUNG_TERMIN2)
{
$worksheet->write($lines,8, trim($elem->matrikelnr), $fmtPruefImportPkz);
$worksheet->write($lines,8, trim($elem->matrikelnr), $format_highlight);
$pr = new Pruefung();
$pr->getPruefungen($elem->uid, "Termin2", $lvid, $stsem);
$output2 = $pr->result;
@@ -399,23 +366,23 @@ if (!$berechtigung->isBerechtigt('admin')
if ($output2)
{
$resultPr = $output2[0];
$worksheet->write($lines,9, date('d.m.Y', strtotime($resultPr->datum)), $fmtPruefImportDate);
$worksheet->write($lines,9, date('d.m.Y', strtotime($resultPr->datum)), $format_highlightright_date);
if(defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE==true)
$worksheet->write($lines,10, $resultPr->punkte, $fmtPruefImportNote);
$worksheet->write($lines,10, $resultPr->punkte, $format_highlightright);
else
$worksheet->write($lines,10, $resultPr->note, $fmtPruefImportNote);
$worksheet->write($lines,10, $resultPr->note, $format_highlightright);
}
else
{
$worksheet->write($lines,9, '', $fmtPruefImportDate);
$worksheet->write($lines,10, '', $fmtPruefImportNote);
$worksheet->write($lines,9, '', $format_highlightright_date);
$worksheet->write($lines,10, '', $format_highlightright);
}
}
// Nachprüfung
if (defined('CIS_GESAMTNOTE_PRUEFUNG_TERMIN3') && CIS_GESAMTNOTE_PRUEFUNG_TERMIN3)
{
$worksheet->write($lines,12, trim($elem->matrikelnr), $fmtPruefImportPkz);
$worksheet->write($lines,12, trim($elem->matrikelnr), $format_highlight);
$pr = new Pruefung();
$pr->getPruefungen($elem->uid, "Termin3", $lvid, $stsem);
$output3 = $pr->result;
@@ -423,16 +390,16 @@ if (!$berechtigung->isBerechtigt('admin')
if ($output3)
{
$resultPr = $output3[0];
$worksheet->write($lines,13, date('d.m.Y', strtotime($resultPr->datum)), $fmtPruefImportDate);
$worksheet->write($lines,13, date('d.m.Y', strtotime($resultPr->datum)), $format_highlightright_date);
if(defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE==true)
$worksheet->write($lines,14, $resultPr->punkte, $fmtPruefImportNote);
$worksheet->write($lines,14, $resultPr->punkte, $format_highlightright);
else
$worksheet->write($lines,14, $resultPr->note, $fmtPruefImportNote);
$worksheet->write($lines,14, $resultPr->note, $format_highlightright);
}
else
{
$worksheet->write($lines,13, '', $fmtPruefImportDate);
$worksheet->write($lines,14, '', $fmtPruefImportNote);
$worksheet->write($lines,13, '', $format_highlightright_date);
$worksheet->write($lines,14, '', $format_highlightright);
}
}

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