Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • alexander.vorwerk/studip
  • hochschule-wismar/stud-ip
  • tleilax/studip
  • marcus/studip
  • manschwa/studip
  • eberhardt/studip
  • uol/studip
  • pluta/studip
  • thienel/extern-uni-b
  • studip/studip
  • strohm/studip
  • uni-osnabrueck/studip
  • FloB/studip
  • universit-t-rostock/studip
  • Robinyyy/studip
  • jakob.diel/studip
  • HyperSpeeed/studip
  • ann/studip
  • nod3zer0/stud-ip-siple-saml-php-plugin
19 results
Show changes
Commits on Source (297)
Showing
with 408 additions and 358 deletions
......@@ -3,7 +3,17 @@
# MYSQL_PASSWORD=""
# MYSQL_DATABASE=""
# DEBUG_BAR="1" // Enable to display the debug bar in development mode
# Enable the next line to display the debug bar in development mode
# DEBUG_BAR=1
# Enable the following to allow opening files from exception displays in your
# editor. Beware: You need to provide a full path for prefix your files since
# the exception only displays the relative path.
#
# Variables being substituted: %{file} and %{line}
#
# EDITOR_URL="phpstorm://open?file=<path-to-your-studip>/%{file}&line=%{line}"
# EDITOR_URL="vscode://file/<path-to-your-studip>/%{file}:%{line}:0
# STUDIP_CACHING_ENABLE=""
# STUDIP_CACHE_IS_SESSION_STORAGE=""
......
......@@ -67,8 +67,11 @@ stages:
.definitions:
mariadb-service: &mariadb-service
- name: mariadb
command: [ "--sql_mode=","--character-set-client=utf8","--character-set-server=utf8","--collation-server=utf8_unicode_ci"]
- name: mariadb:10.2.7
command: [ "--sql_mode=","--character-set-client=utf8","--character-set-server=utf8","--collation-server=utf8_unicode_ci"]
mysql-service: &mysql-service
- name: mysql:8.0
command: [ "--sql_mode=","--character-set-server=utf8mb4","--collation-server=utf8mb4_general_ci"]
build-composer:
stage: build
......@@ -273,11 +276,12 @@ test-functional:
variables:
FUNCTIONAL_XML_REPORT: $REPORT_DIR/functional-report.xml
FUNCTIONAL_CODE_QUALITY_REPORT: $REPORT_DIR/functional-codequality.json
MYSQL_HOST: mysql
cache:
<<: *composer-cache
policy: pull
services:
- *mariadb-service
- *mysql-service
allow_failure: false
interruptible: true
before_script:
......
# 28.02.2025 v 5.5.4
https://gitlab.studip.de/studip/studip/-/issues?milestone_title=Stud.IP+5.5.4&state=all
- Pipeline für 5.5 lintet nicht gegen PHP 7.4 [#5134]
- Courseware: Beim Kopieren und Importieren wird die Einstellung "Titelseite" nicht übernommen [#5138]
- Inhaltsverzeichnis-Block zeigt Seite in Kachelansicht nicht an [#5158]
- Löschen eines Accounts erzeugt fehlerhafte Nachrichten [#5161]
- Wiki: Anlegen einer Seite mit Name ".*" löscht den Inhalt aller Seiten, die ".*" enthalten [#5207]
- Call to a member function each() on null in /srv/studip/lib/models/ConsultationEvent.php:37 [#5266]
- Wiki: "Mach die Welt ein Stückchen schlauer" ist unschlau [#5273]
- Austragen einer Person aus einer Kontaktgruppe führt zu einem Fehler [#5319]
# 28.02.2025 v 5.4.7
https://gitlab.studip.de/studip/studip/-/issues?milestone_title=Stud.IP+5.4.7&state=all
- Veranstaltungsverwaltung: Fehlerhafte Darstellung der Studienbereiche [#2863]
- Veranstaltungsverwaltung: Reiter steht in der DB an falscher Position [#4417]
- Blubber in Veranstaltungen zeigt alle globalen und persönlichen Blubberthreads [#4546]
- Undefined key "ref_id" beim Sprung vom Arbeitsplatz ins mit PHP8 [#4939]
- Studiengruppe Werkzeuge-Seite Tabellarische Ansicht kaputt [#4972]
- Falsche Feldnamen in 5.4.6_tree_changes.php [#5129]
- Courseware: Beim Importieren wird zu jeder Seite ein leerer Abschnitt hinzugefügt [#5139]
- Schnittstelle Ilias: In Ilias gelöschte Nutzer führen zu Inkonsistenz [#5145]
- Zusatzangaben: Widget "Veranstaltungen" wird auch Dozenten angezeigt [#5154]
- Rollenzuweisung für Ankündigungen funktioniert nicht mehr richtig. [#5178]
- QR-Code skaliert nicht horizontal [#5190]
- Raumanfragen: Option für Rückmeldung an alle Lehrenden soll wieder immer verfügbar sein (nochmal) [#5264]
- Courseware: Fehler bei der Auswahl von Farben [#5295]
- JSON-API liefert auch bei gesetztem Accept-Language Header nur den Originalstring [#5306]
# 28.02.2025 v 5.3.10
https://gitlab.studip.de/studip/studip/-/issues?milestone_title=Stud.IP+5.3.10&state=all
- InfoIcon im Dialog wird direkt angezeigt [#773]
- SimpleORMap liest falsche Default-Werte aus dem Schema [#4462]
- PHP8 - Warnungen in den Hilfetouren [#4667]
- Teilnehmende: "Diese Seite für Studierende verbergen" funktioniert nicht mehr [#5009]
- Timeout für HTTP Requests in Ilias-Schnittstelle implementieren [#5014]
- Courseware Aufgaben: Fehlende Funktion im Store [#5019]
- Fehler beim Speichern von I18N-Datenfeldern [#5031]
- Im fromSORM fehlt Eingabename des `templates/forms/select_input.php` [#5080]
- Semesterdarstellung ist bei der Anzeige der Veranstaltungen einer Einrichtung verschoben [#5119]
- BIESt 831 taucht beim AdvancedBasicDataWizard auf [#5121]
- User::name not found [#5122]
- LVGroupsWizardStep läuft auf eine Exception [#5123]
- Leerzeichen in Footer von Text Mails [#5128]
- Log-Events schneiden Pluginnamen ab [#5135]
- Änderung des Inhaltstyps macht Aufgabe unbrauchbar [#5144]
- Externe Seiten: Konfiguration kann mit PHP 8 nicht mehr gespeichert werden [#5184]
- Notwendigkeit von »r« (read) und »w« (write) bei den Einstellungen der Zeitgesteuerten Ordner [#5194]
- Beim Löschen von Konten werden die Einladungen in Studiengruppen nicht gelöscht [#5195]
- Inkonsistente Bezeichnungen der Ordnertypen [#5197]
- Fragebögen: Icon in Startzeitpunkt/Endzeitpunkt bewegt sich beim Öffnen des Dialogs [#5202]
- MVV: Bearbeitungskontext geht nach Bearbeitung einer Fachsemsterzuordnung verloren [#5203]
- Fragebogen: Einstellung "Pflichtfrage" funktioniert bei Freitextfrage nicht [#5204]
- Elemente auf "Privatspäre" unter Profil > Einstellungen sind nicht übersetzt [#5205]
- I18N-Datenfelder auf der MitarbeiterInnen-Seite einer Einrichtung können zur Anzeige von "default_value" führen [#5208]
- Inkonsistentes Verhalten von Datenfeldern [#5213]
- Weitere PHP8-Warnungen [#5216]
- Systemplugins sollten vor allen anderen Plugins geladen werden [#5241]
- Globale Suche nach Veranstaltungen berücksichtigt SEM_VISIBILITY_PERM nicht [#5250]
- Methode User::search() liefert ggf. Einträge ohne User-ID zurück [#5279]
- Funktionen/Gruppen: Multipersonsearch führt zu Speichermangel [#5282]
# 20.12.2024 v 5.5.3
https://gitlab.studip.de/studip/studip/-/issues?milestone_title=Stud.IP+5.5.3&state=all
......
......@@ -87,12 +87,12 @@ optimize-icons: npm
find public/assets/images/icons/blue -type f | xargs -P0 npx svgo -q --config=config/svgo.config.js
icons: optimize-icons
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#000000/" {} > {}' | sed 's#icons/blue#icons/black#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#00962d/" {} > {}' | sed 's#icons/blue#icons/green#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#6e6e6e/" {} > {}' | sed 's#icons/blue#icons/grey#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#cb1800/" {} > {}' | sed 's#icons/blue#icons/red#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#ffffff/" {} > {}' | sed 's#icons/blue#icons/white#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#ffad00/" {} > {}' | sed 's#icons/blue#icons/yellow#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#000000/g" {} > {}' | sed 's#icons/blue#icons/black#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#00962d/g" {} > {}' | sed 's#icons/blue#icons/green#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#6e6e6e/g" {} > {}' | sed 's#icons/blue#icons/grey#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#cb1800/g" {} > {}' | sed 's#icons/blue#icons/red#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#ffffff/g" {} > {}' | sed 's#icons/blue#icons/white#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#ffad00/g" {} > {}' | sed 's#icons/blue#icons/yellow#2' | sh
# default rules for gettext handling
js-%.pot: $(VUE_SOURCES)
......
......@@ -4,11 +4,74 @@
## Neue Features
### System
- Der Stud.IP-Cache ist nun kompatibel zu PSR-6. ([TIC #3701](https://gitlab.studip.de/studip/studip/-/issues/3701))
- Das `User`-Model hat die Methode `hasPermissionLevel()` erhalten, um einfach abfragen zu können, ob eine Person einen bestimmten Berechtigungsstatus hat. ([Issue #3453](https://gitlab.studip.de/studip/studip/-/issues/3453))
- In der Standort-Verwaltung können nun nicht nur Ferien sondern auch Feiertage konfiguriert werden. Dies erlaubt das Markieren von Feiertagen als gesetzliche Feiertage, da diese je nach Bundesland variieren können. ([Issue #2795](https://gitlab.studip.de/studip/studip/-/issues/2795))
- Die Nutzungsbedingungen sind nun nicht mehr als statische HTML-Dateien hinterlegt, sondern können analog zu Impressum, Datenschutz- und Barrierefreiheitserklärung direkt über die Oberfläche bearbeitet werden. Initial ist diese Seite aber im Entwurfsmodus und daher für Nicht-Roots unsichtbar. Damit andere Personen beim ersten Login diese Nutzungsbedingungen sehen und ihnen zustimmen können, muss der Entwurfsmodus für diese Seite abgeschaltet werden. ([TIC #4433](https://gitlab.studip.de/studip/studip/-/issues/4433))
### Layout
- Die Login-Seite wurde überarbeitet
- Das Design von Stud.IP wurde modernisiert
- Neue Avatarbilder
### Courseware
- Rechte und Sichtbarkeit überarbeitet
- Verbesserter Übersichtsdialog
- Rechte und Sichtbarkeit können für ein ganzes Lernmaterial gesetzt werden
- [Issue #3442](https://gitlab.studip.de/studip/studip/-/issues/3442)
- Peer-Review für Aufgaben
- Lehrende können Peer-Review-Prozess für Aufgaben einstellen
- Lernende sehen Aufgabenlösungen ein, geben Feedback und Bewertung dazu ab
- [Issue #2484](https://gitlab.studip.de/studip/studip/-/issues/2484)
- Blubber-Block
- Blubber Diskussionen können jetzt als Block eingebunden werden
- Lernmaterialübersicht im Inhaltsverzeichnis
- Schafft einen Überblick über weitere Lernmaterialien in der Veranstaltungen ohne den Kontext verlassen zu müssen
- Neues Layout für den Merksatz-Block
### Vips Plugin wird als Aufgaben-Werkzeug in Kern integriert
- Mit Vips lassen sich Selbsttests, Übungen und Klausuren erstellen
- Lernende können Vips Aufgabenblätter in Stud.IP bearbeiten und erhalten dort auch ihre Ergebnisse
- Für eine Vielzahl von Aufgabentypen besteht die Möglichkeit einer Autokorrektur
- [Issue #4258](https://gitlab.studip.de/studip/studip/-/issues/4258)
- [Plugin](https://develop.studip.de/studip/plugins.php/pluginmarket/presenting/details/81097da5ef66a002998b75d5eeece1f0)
### Neues Benachrichtigungssystem
- Vereinheitlichung wie Stud.IP mit Nutzenden kommuniziert
- Messageboxen, Companionmeldungen und Notifications werden vereinheitlicht
- [Issue #660](https://gitlab.studip.de/studip/studip/-/issues/660)
### Garuda Plugin in Kern integriert
- Mit Garuda können Zielgruppen definiert werden, um diesen Sammelnachrichten zukommen zu lassen
- [Issue #3326](https://gitlab.studip.de/studip/studip/-/issues/3326)
- [Plugin](https://develop.studip.de/studip/plugins.php/pluginmarket/presenting/details/d24cb47ee246033c325496b832e64147)
### Bilderpool
- Neuer Archiv Upload
- Ermöglicht das Einpflegen von einer Vielzahl von Bilder mit nur einem Upload
- Metadaten werden in dem Archiv als CSV-Datei mitgegeben und müssen nicht mehr einzeln eingegeben werden
- Stud.IP Bildersammlung kann mit nur einem Upload integriert werden
- Die Stud.IP Bildersammlung finden Sie [hier](https://gitlab.studip.de/studip/bilderpool)
### Anzeige von ILIAS-Kursen auf "Mein Arbeitsplatz"
- Bei aktivierter ILIAS-Schnittstelle können Lehrende ihre Stud.IP Veranstaltungen mit ILIAS-Kursen verknüpfen
- Lernende erhalten eine Übersicht über ihre ILIAS-Kurse im Stud.IP Arbeitsplatz
### Assistent für Roots nach Updates
- Nach einem Update auf eine neue Stud.IP Version werden Nutzenden mit Root-Rechten die neuen Features der Version präsentiert
### Studiengruppen
- Um Studiengruppen sichtbarer und interessanter zu machen wurde eine Reihe von Verbesserungen vorgenommen
- Neues Widget für die Startseite
- Möglichkeit Studiengruppen für eine Veranstaltungen vorzuschlagen
- [Issue #3616](https://gitlab.studip.de/studip/studip/-/issues/3616)
## Breaking changes
- Mindestanforderung an PHP auf 8.1 angehoben ([TIC #3805](https://gitlab.studip.de/studip/studip/issues/3805))
......
......@@ -31,7 +31,9 @@ class Admin_AdditionalController extends AuthenticatedController
"Veranstaltung zu verändern."));
}
Sidebar::get()->addWidget(new CourseManagementSelectWidget());
if ($GLOBALS['perm']->have_studip_perm('admin', $this->course->id)) {
Sidebar::get()->addWidget(new CourseManagementSelectWidget());
}
}
/**
......
......@@ -44,11 +44,11 @@ class Admin_AutoinsertController extends AuthenticatedController
if (Request::submitted('suchen')) {
if (Request::get('sem_search')) {
$this->sem_search = Request::get('sem_search');
$this->sem_select = Request::option('sem_select');
$this->sem_select = Request::option('sem_select') ?: null;
$search = new SeminarSearch();
$this->seminar_search = $search->getResults
(Request::get('sem_search'),
['search_sem_sem' => Request::option('sem_select')]
$this->seminar_search = $search->getResults(
$this->sem_search,
['search_sem_sem' => $this->sem_select]
);
if (count($this->seminar_search) == 0) {
PageLayout::postInfo(_('Es wurden keine Veranstaltungen gefunden.'));
......
......@@ -23,7 +23,6 @@
* @since 3.1
*/
require_once 'lib/meine_seminare_func.inc.php';
require_once 'lib/object.inc.php';
require_once 'lib/archiv.inc.php'; //for lastActivity in getCourses() method
......@@ -298,7 +297,6 @@ class Admin_CoursesController extends AuthenticatedController
PageLayout::setHelpKeyword('Basis.Veranstaltungen');
PageLayout::setTitle(_('Verwaltung von Veranstaltungen und Einrichtungen'));
// Add admission functions.
PageLayout::addScript('studip-admission.js');
$this->max_show_courses = Config::get()->MAX_SHOW_ADMIN_COURSES;
}
......@@ -483,7 +481,7 @@ class Admin_CoursesController extends AuthenticatedController
$data['buttons_bottom'] = (string) \Studip\Button::createAccept(
_('Teilnehmendenexport'), 'batch_export_members',
[
'formaction' => URLHelper::getURL('dispatch.php/admin/user/batch_export_members'),
'formaction' => URLHelper::getURL('dispatch.php/admin/courses/batch_export_members'),
'data-dialog' => 'size=big'
]);
break;
......@@ -843,7 +841,7 @@ class Admin_CoursesController extends AuthenticatedController
$d['action'] = $template->render();
break;
case 22: //Masssenexport Teilnehmendendaten
$template = $tf->open('admin/courses/batch_export_members');
$template = $tf->open('admin/courses/export_members');
$template->course = $course;
$d['action'] = $template->render();
break;
......@@ -1332,6 +1330,73 @@ class Admin_CoursesController extends AuthenticatedController
$this->notice = $course->config->COURSE_ADMIN_NOTICE;
}
public function batch_export_members_action()
{
PageLayout::setTitle(_('Teilnehmendendaten exportieren'));
$courseIds = Request::optionArray('export_members');
$order = Config::get()->IMPORTANT_SEMNUMBER
? "ORDER BY `VeranstaltungsNummer`, `Name`"
: "ORDER BY `Name`";
$this->courses = Course::findMany($courseIds, $order);
// check if at least one course was selected (this can only happen from admin courses overview):
if (count($this->courses) === 0) {
PageLayout::postWarning('Es wurde keine Veranstaltung gewählt.');
$this->relocate('admin/courses');
}
}
/*
* Export member data of all selected courses
*/
public function do_batch_export_action()
{
if (Request::submitted('xlsx')) {
$export_format = 'xlsx';
} else if (Request::submitted('csv')) {
$export_format = 'csv';
} else {
PageLayout::postError('Nicht unterstütztes Exportformat.');
$this->relocate('admin/courses');
}
$tmp_folder = $GLOBALS['TMP_PATH'] . '/temp_folder_' . md5(uniqid());
mkdir($tmp_folder);
$courses = Course::findMany(Request::optionArray('courses'));
$header = [
_('Status'),
_('Anrede'),
_('Titel'),
_('Vorname'),
_('Nachname'),
_('Titel nachgestellt'),
_('Benutzername'),
_('Adresse'),
_('Telefonnr.'),
_('E-Mail'),
_('Anmeldedatum'),
_('Matrikelnummer'),
_('Studiengänge'),
_('Position'),
];
foreach ($courses as $course) {
if ($GLOBALS['perm']->have_studip_perm('dozent', $course->id)) {
$members = $course->getMembersData();
$filename = FileManager::cleanFileName('Teilnehmendenexport ' . $course->Name . '.' . $export_format);
$filepath = $tmp_folder . '/'. $filename;
$this->render_spreadsheet($header, $members, $export_format, $filename, $filepath);
}
}
$archive_file_path = $GLOBALS['TMP_PATH'] . '/archiv.zip';
$archive_filename = 'Export_Teilnehmendendaten.zip';
FileArchiveManager::createArchiveFromPhysicalFolder($tmp_folder, $archive_file_path);
rmdirr($tmp_folder);
$this->render_temporary_file($archive_file_path, $archive_filename, 'application/zip');
}
/**
* Return a specifically action or all available actions
......@@ -1442,10 +1507,10 @@ class Admin_CoursesController extends AuthenticatedController
22 => [
'name' => _('Teilnehmendenexport'),
'title' => _('Teilnehmendenexport'),
'url' => 'dispatch.php/admin/user/batch_export_members',
'url' => 'dispatch.php/admin/courses/batch_export_members',
'dialogform' => true,
'multimode' => true,
'partial' => 'batch_export_members.php'
'partial' => 'export_members.php'
],
23 => [
......
......@@ -156,7 +156,13 @@ class Admin_DatafieldsController extends AuthenticatedController
} elseif ($type === 'studycourse') {
$datafield->object_class = Request::option('object_class');
} else {
$datafield->object_class = array_sum(Request::getArray('object_class')) ?: null;
$object_class = Request::getArray('object_class');
if (empty($object_class) || (count($object_class) === 1 && $object_class[0] === 'NULL')) {
$object_class = null;
} else {
$object_class = array_sum($object_class);
}
$datafield->object_class = $object_class;
}
$datafield->edit_perms = Request::get('edit_perms');
$datafield->view_perms = Request::get('visibility_perms');
......@@ -188,6 +194,7 @@ class Admin_DatafieldsController extends AuthenticatedController
$this->institutes = Institute::getMyInstitutes();
if (!$this->object_typ) {
$this->render_action('type_select');
return;
}
if (Request::isXhr() && $this->type_name) {
......
......@@ -122,7 +122,7 @@ class Admin_IliasInterfaceController extends AuthenticatedController
$this->valid_url = false;
$this->ilias_version = '';
$this->ilias_version_date = '';
$this->clients = [];
$this->ilias_clients = [];
if ($index === 'new') {
// default values
$this->ilias_config = [
......@@ -130,6 +130,8 @@ class Admin_IliasInterfaceController extends AuthenticatedController
'name' => '',
'version' => '',
'url' => _('https://<URL zur ILIAS-Installation>'),
'http_connection_timeout' => 1,
'http_request_timeout' => 3,
'client' => '',
'ldap_enable' => '',
'reconnect_accounts' => false,
......@@ -168,9 +170,11 @@ class Admin_IliasInterfaceController extends AuthenticatedController
// get ILIAS server info
if (Request::get('ilias_url')) {
$info = ConnectedIlias::getIliasInfo(Request::get('ilias_url'));
if (count($info)) {
if (is_array($info) && count($info)) {
$this->valid_url = true;
$this->ilias_config['url'] = Request::get('ilias_url');
$this->ilias_config['http_connection_timeout'] = (int) Request::get('ilias_http_connection_timeout');
$this->ilias_config['http_request_timeout'] = (int) Request::get('ilias_http_request_timeout');
if ($info['version']) {
$this->ilias_version = $info['version'];
$this->ilias_version_date = $info['version_date'];
......@@ -220,6 +224,8 @@ class Admin_IliasInterfaceController extends AuthenticatedController
if (Request::get('ilias_name')) {
$this->ilias_config['name'] = Request::get('ilias_name');
$this->ilias_config['url'] = Request::get('ilias_url');
$this->ilias_config['http_connection_timeout'] = (int) Request::get('ilias_http_connection_timeout');
$this->ilias_config['http_request_timeout'] = (int) Request::get('ilias_http_request_timeout');
}
$info = ConnectedIlias::getIliasInfo($this->ilias_config['url']);
if (count($info)) {
......@@ -241,14 +247,15 @@ class Admin_IliasInterfaceController extends AuthenticatedController
*/
public function edit_content_action($index)
{
$this->ilias_config = $this->ilias_configs[$index];
$this->ilias_index = $index;
$this->ilias_datafields = [];
$connected_ilias = new ConnectedIlias($index);
$this->ilias_config = $connected_ilias->ilias_config;
if ($admin_id = $connected_ilias->soap_client->lookupUser($this->ilias_config['admin'])) {
$user = $connected_ilias->soap_client->getUser($admin_id);
if (array_key_exists('udfs', $user)) {
if (!empty($user) && array_key_exists('udfs', $user)) {
$this->ilias_datafields = $user['udfs'];
}
}
......@@ -260,10 +267,10 @@ class Admin_IliasInterfaceController extends AuthenticatedController
*/
public function edit_permissions_action($index)
{
$this->ilias_config = $this->ilias_configs[$index];
$this->ilias_index = $index;
$connected_ilias = new ConnectedIlias($index);
$this->ilias_config = $connected_ilias->ilias_config;
$this->global_roles = $connected_ilias->soap_client->getRoles('global', -1);
}
......@@ -298,6 +305,8 @@ class Admin_IliasInterfaceController extends AuthenticatedController
$this->ilias_configs[$index]['version'] = Request::get('ilias_version');
}
$this->ilias_configs[$index]['url'] = Request::get('ilias_url');
$this->ilias_configs[$index]['http_connection_timeout'] = (int) Request::get('ilias_http_connection_timeout');
$this->ilias_configs[$index]['http_request_timeout'] = (int) Request::get('ilias_http_request_timeout');
if (Request::getInstance()->offsetExists('ilias_client')) {
$this->ilias_configs[$index]['client'] = Request::get('ilias_client');
}
......@@ -496,28 +505,31 @@ class Admin_IliasInterfaceController extends AuthenticatedController
*/
public function soap_methods_action($index)
{
if ($this->ilias_configs[$index]['is_active']) {
$ilias = new ConnectedIlias($index);
$this->soap_methods = $ilias->getSoapMethods();
ksort($this->soap_methods);
$this->ilias_index = $index;
if (Request::get('ilias_soap_method')) {
$this->ilias_soap_method = Request::get('ilias_soap_method');
foreach ($this->soap_methods[Request::get('ilias_soap_method')] as $param) {
switch ($param) {
case "sid" : $this->params[$param] = $ilias->soap_client->getSID();
break;
case "user_id" : $this->params[$param] = $ilias->user->getId();
break;
}
$ilias = new ConnectedIlias($index);
$this->soap_methods = $ilias->getSoapMethods();
ksort($this->soap_methods);
$this->ilias_index = $index;
if (Request::get('ilias_soap_method')) {
$this->ilias_soap_method = Request::get('ilias_soap_method');
foreach ($this->soap_methods[Request::get('ilias_soap_method')] as $param) {
switch ($param) {
case "sid" : $this->params[$param] = $ilias->soap_client->getSID();
break;
case "user_id" : $this->params[$param] = is_object($ilias->user) ? $ilias->user->getId() : '';
break;
}
} elseif (Request::get('ilias_call')) {
$params = [];
foreach ($this->soap_methods[Request::get('ilias_call')] as $param) {
}
} elseif (Request::get('ilias_call')) {
$params = [];
foreach ($this->soap_methods[Request::get('ilias_call')] as $param) {
if ($param === 'user_ids') {
$params[$param] = [Request::get('ilias_soap_param_'.$param)];
} else {
$params[$param] = Request::get('ilias_soap_param_'.$param);
}
$this->result = $ilias->soap_client->call(Request::get('ilias_call'), $params);
}
$this->result = $ilias->soap_client->call(Request::get('ilias_call'), $params);
}
}
}
......@@ -60,12 +60,13 @@ class Admin_OverlappingController extends AuthenticatedController
$GLOBALS['user']->id
])
);
$_SESSION['MVV_OVL_SELECTION_ID'] = $selection_id;
$this->selection_id = '';
if (count($selections)) {
$this->base_version = StgteilVersion::find($selections->first()->base_version_id);
$this->fachsems = explode(',', $selections->first()->fachsems);
$this->semtypes = explode(',', $selections->first()->semtypes);
$this->fachsems = $selections->first()->fachsems === '' ? [] : explode(',', $selections->first()->fachsems);
$this->semtypes = $selections->first()->semtypes === '' ? [] : explode(',', $selections->first()->semtypes);
$this->comp_versions = StgteilVersion::findMany($selections->pluck('comp_version_id'));
$this->selection_id = $selections->first()->selection_id;
if (Request::int('show_hidden') !== null) {
......@@ -84,6 +85,78 @@ class Admin_OverlappingController extends AuthenticatedController
$this->selection_id,
empty($_SESSION['MVV_OVL_HIDDEN'])
);
$version_options = [];
foreach ($this->getStgteilVersions() as $base_version) {
$version_options[$base_version->id] = $base_version->getDisplayName();
}
$this->form = \Studip\Forms\Form::create();
$this->fieldset = new \Studip\Forms\Fieldset(_('Auswahl'));
$this->fieldset->addInput(
new \Studip\Forms\SelectInput(
'base_version',
_('Studiengangteil'),
$this->base_version_id,
[
'options' => $version_options
]
)
)->setRequired();
$this->fieldset->addInput(
new \Studip\Forms\MultiselectInput(
'comp_versions',
_('Vergleichs-Studiengangteile'),
$this->comp_versions_ids,
[
'options' => $version_options
]
)
);
$fsem_options = [];
for ($fsem = 1; $fsem < 7; $fsem++) {
$fsem_options[$fsem] = sprintf(_('%s Fachsemester'),
$fsem . ModuleManagementModel::getLocaleOrdinalNumberSuffix($fsem));
}
$this->fieldset->addInput(
new \Studip\Forms\MultiselectInput(
'fachsems',
_('Fachsemester'),
$this->fachsems,
[
'options' => $fsem_options
]
)
);
$sem_class_options = [];
foreach ($GLOBALS['SEM_CLASS'] as $class_id => $class) {
if ($class['studygroup_mode']) continue;
foreach ($class->getSemTypes() as $id => $type) {
$sem_class_options[$id] = sprintf('%s (%s)', $type['name'], $class['name']);
}
}
$this->fieldset->addInput(
new \Studip\Forms\MultiselectInput(
'semtypes',
_('Veranstaltungstypen'),
$this->semtypes,
[
'options' => $sem_class_options
]
)
);
$this->fieldset->addInput(
new \Studip\Forms\CheckboxInput(
'show_hidden',
_('Ausgeblendete Veranstaltungen anzeigen'),
$_SESSION['MVV_OVL_HIDDEN'] ?? '0'
)
);
$this->form->addPart($this->fieldset);
$this->form->setURL($this->check())
->setCollapsable(true)
->setDataSecure(false)
->setSaveButtonText(_('Vergleichen'))
->setCancelButtonText(_('Zurücksetzen'));
}
/**
......@@ -120,61 +193,59 @@ class Admin_OverlappingController extends AuthenticatedController
$this->fachsems = Request::intArray('fachsems');
$this->semtypes = Request::intArray('semtypes');
if (Request::submitted('compare')) {
$selection_id = MvvOverlappingSelection::createSelectionId(
$this->base_version,
$this->comp_versions,
$this->fachsems,
$this->semtypes,
$this->selected_semester->id
);
$selection_id = MvvOverlappingSelection::createSelectionId(
$this->base_version,
$this->comp_versions,
$this->fachsems,
$this->semtypes,
$this->selected_semester->id
);
// refresh conflicts
MvvOverlappingConflict::deleteBySelection($selection_id);
// refresh conflicts
MvvOverlappingConflict::deleteBySelection($selection_id);
foreach ($this->comp_versions as $comp_version) {
$selection[$comp_version->id] = MvvOverlappingSelection::findOneBySQL(
'`selection_id` = ? AND `comp_version_id` = ?', [
$selection_id,
$comp_version->id
]);
if (!$selection[$comp_version->id]) {
$selection[$comp_version->id] = new MvvOverlappingSelection();
$selection[$comp_version->id]->semester_id = $this->selected_semester->id;
$selection[$comp_version->id]->selection_id = $selection_id;
$selection[$comp_version->id]->base_version_id = $this->base_version->id;
$selection[$comp_version->id]->comp_version_id = $comp_version->id;
$selection[$comp_version->id]->setFachsemester($this->fachsems);
$selection[$comp_version->id]->setCourseTypes($this->semtypes);
$selection[$comp_version->id]->user_id = $GLOBALS['user']->id;
$selection[$comp_version->id]->store();
}
$selection[$comp_version->id]->storeConflicts();
foreach ($this->comp_versions as $comp_version) {
$selection[$comp_version->id] = MvvOverlappingSelection::findOneBySQL(
'`selection_id` = ? AND `comp_version_id` = ?', [
$selection_id,
$comp_version->id
]);
if (!$selection[$comp_version->id]) {
$selection[$comp_version->id] = new MvvOverlappingSelection();
$selection[$comp_version->id]->semester_id = $this->selected_semester->id;
$selection[$comp_version->id]->selection_id = $selection_id;
$selection[$comp_version->id]->base_version_id = $this->base_version->id;
$selection[$comp_version->id]->comp_version_id = $comp_version->id;
$selection[$comp_version->id]->setFachsemester($this->fachsems);
$selection[$comp_version->id]->setCourseTypes($this->semtypes);
$selection[$comp_version->id]->user_id = $GLOBALS['user']->id;
$selection[$comp_version->id]->store();
}
$conflicts = MvvOverlappingSelection::getConflictsBySelection($selection_id);
$visible_conflicts = MvvOverlappingSelection::getConflictsBySelection($selection_id, true);
if (count($conflicts)) {
if (count($conflicts) != count($visible_conflicts)) {
PageLayout::postSuccess(
sprintf(
ngettext('1 Konflikt gefunden (1 ausgeblendet)',
'%s Konflikte gefunden (%s ausgeblendet).', count($conflicts)),
count($conflicts),
count($conflicts) - count($visible_conflicts)
)
);
} else {
PageLayout::postSuccess(
sprintf(
ngettext('1 Konflikt gefunden.',
'%s Konflikte gefunden.', count($conflicts)),
count($conflicts)
)
);
}
$selection[$comp_version->id]->storeConflicts();
}
$conflicts = MvvOverlappingSelection::getConflictsBySelection($selection_id);
$visible_conflicts = MvvOverlappingSelection::getConflictsBySelection($selection_id, true);
if (count($conflicts)) {
if (count($conflicts) !== count($visible_conflicts)) {
PageLayout::postSuccess(
sprintf(
ngettext('1 Konflikt gefunden (1 ausgeblendet)',
'%s Konflikte gefunden (%s ausgeblendet).', count($conflicts)),
count($conflicts),
count($conflicts) - count($visible_conflicts)
)
);
} else {
PageLayout::postSuccess(_('Keine Konflikte gefunden.'));
PageLayout::postSuccess(
sprintf(
ngettext('1 Konflikt gefunden.',
'%s Konflikte gefunden.', count($conflicts)),
count($conflicts)
)
);
}
} else {
PageLayout::postSuccess(_('Keine Konflikte gefunden.'));
}
} else {
PageLayout::postError('Die Basis-Version muss angegeben werden!');
......
......@@ -108,6 +108,7 @@ class Admin_StatusgroupsController extends AuthenticatedController
AND auth_user_md5.visible <> 'never'
ORDER BY Vorname, Nachname";
$this->searchType = new SQLSearch($query, _('Teilnehmende/n suchen'), 'username');
$this->addQuickfilter = count($this->groups) * count($this->membersOfInstitute) < 500;
}
/**
......
......@@ -16,10 +16,6 @@
* @since 2.1
*/
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Csv;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
require_once 'vendor/email_message/blackhole_message.php';
require_once 'lib/statusgruppe.inc.php';
......@@ -1662,88 +1658,6 @@ class Admin_UserController extends AuthenticatedController
$this->redirect($this->show_user_coursesURL($user));
}
public function batch_export_members_action()
{
PageLayout::setTitle(_('Teilnehmendendaten exportieren'));
$courseIds = Request::optionArray('export_members');
$order = Config::get()->IMPORTANT_SEMNUMBER
? "ORDER BY `VeranstaltungsNummer`, `Name`"
: "ORDER BY `Name`";
$this->courses = array_filter(
Course::findMany($courseIds, $order),
function (Course $course): bool {
/*
* Check if sem_tree entries are allowed and may be changed and remove all courses
* where this is not the case.
*/
return !LockRules::Check($course->id, 'sem_tree', 'sem')
&& $course->getSemClass()['bereiche'];
}
);
// check if at least one course was selected (this can only happen from admin courses overview):
if (count($this->courses) === 0) {
PageLayout::postWarning('Es wurde keine Veranstaltung gewählt.');
$this->relocate('admin/courses');
}
}
/*
* Export member data of all selected courses
*/
public function do_batch_export_action()
{
CSRFProtection::verifyUnsafeRequest();
if (Request::submitted('xlsx')) {
$export_format = 'xlsx';
} else if (Request::submitted('csv')) {
$export_format = 'csv';
} else {
PageLayout::postError('Nicht unterstütztes Exportformat.');
$this->relocate('admin/courses');
}
$tmp_folder = $GLOBALS['TMP_PATH'] . '/temp_folder_' . md5(uniqid());
mkdir($tmp_folder);
$courses = Course::findMany(Request::optionArray('courses'));
$header = [
_('Status'),
_('Anrede'),
_('Titel'),
_('Vorname'),
_('Nachname'),
_('Titel nachgestellt'),
_('Benutzername'),
_('Adresse'),
_('Telefonnr.'),
_('E-Mail'),
_('Anmeldedatum'),
_('Matrikelnummer'),
_('Studiengänge'),
_('Position'),
];
foreach ($courses as $course) {
$members = $course->getMembersData();
$filename = FileManager::cleanFileName('Teilnehmendenexport ' . $course->Name . '.' . $export_format);
$filepath = $tmp_folder . '/'. $filename;
$this->render_spreadsheet($header, $members, $export_format, $filename, $filepath);
}
$archive_file_path = $GLOBALS['TMP_PATH'] . '/archiv.zip';
$archive_filename = 'Export_Teilnehmendendaten.zip';
FileArchiveManager::createArchiveFromPhysicalFolder($tmp_folder, $archive_file_path);
rmdirr($tmp_folder);
$this->render_temporary_file($archive_file_path, $archive_filename, 'application/zip');
}
/**
* Init sidebar
*/
......
......@@ -34,8 +34,6 @@ class Admission_CoursesetController extends AuthenticatedController
throw new AccessDeniedException();
}
PageLayout::addScript('studip-admission.js');
$views = new ActionsWidget();
$views->addLink(
_('Anmeldeset anlegen'),
......@@ -89,7 +87,7 @@ class Admission_CoursesetController extends AuthenticatedController
if (!isset($this->myInstitutes['all']['count']) || $this->myInstitutes['all']['count'] < 100) {
$this->current_institut_id = 'all';
} else {
list($this->current_institut_id) = reset($this->myInstitutes);
$this->current_institut_id = array_key_first($this->myInstitutes) ?? 'all';
}
}
$chunks = explode('_', $this->current_institut_id);
......@@ -293,7 +291,8 @@ class Admission_CoursesetController extends AuthenticatedController
$this->myUserlists
)
),
'institute-search' => (string) $this->isearch
'institute-search' => (string) $this->isearch,
'instant-course-set-view' => $this->instant_course_set_view
];
if ($this->courseset) {
......@@ -374,9 +373,9 @@ class Admission_CoursesetController extends AuthenticatedController
}
PageLayout::postSuccess(sprintf(_('Das Anmeldeset: %s wurde gespeichert'), htmlReady($courseset->getName())));
if ($this->instant_course_set_view) {
$this->redirect($this->url_for('course/admission'));
$this->relocate('course/admission');
} else {
$this->redirect($this->url_for('admission/courseset/configure', $courseset->getId()));
$this->relocate('admission/courseset/configure', $courseset->getId());
}
}
}
......@@ -398,7 +397,7 @@ class Admission_CoursesetController extends AuthenticatedController
$this->courseset->delete();
}
$this->redirect($this->url_for('admission/courseset'));
$this->relocate('admission/courseset');
}
/**
......@@ -607,7 +606,7 @@ class Admission_CoursesetController extends AuthenticatedController
if ($ok) {
PageLayout::postSuccess(_('Die zugeordneten Veranstaltungen wurden konfiguriert.'));
}
$this->redirect($this->url_for('admission/courseset/configure/' . $courseset->getId()));
$this->relocate('admission/courseset/configure/' . $courseset->getId());
return;
}
}
......
<?php
/**
* Admission_RuleController - Admission rules
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* @author Thomas Hackl <thomas.hackl@uni-passau.de>
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
* @category Stud.IP
* @since 3.0
*/
class Admission_RuleController extends AuthenticatedController
{
/**
* @see AuthenticatedController::before_filter
*/
public function before_filter(&$action, &$args)
{
parent::before_filter($action, $args);
if ($GLOBALS['perm']->have_perm('admin') || ($GLOBALS['perm']->have_perm('dozent') && Config::get()->ALLOW_DOZENT_COURSESET_ADMIN)) {
Navigation::activateItem('/browse/coursesets');
}
PageLayout::setTitle(_('Anmeldesets'));
}
/**
* Gets the template for the rule configuration form.
*
* @param String $ruleType Class name of the rule to configure.
* @param String $ruleId Optional ID of an existing rule.
*/
public function configure_action($ruleType = '', $ruleId = '')
{
$this->ruleTypes = AdmissionRule::getAvailableAdmissionRules();
UserFilterField::getAvailableFilterFields();
$this->ruleType = $ruleType;
// Check if rule data has been given via request.
if (Request::getArray('rules')) {
$rule_siblings = [];
foreach (Request::getManyObjects('rules', 'AdmissionRule') as $rule) {
if ($ruleType == get_class($rule) && $rule->getId() == Request::get('ruleId')) {
$this->rule = $rule;
} else {
$rule_siblings[$rule->getId()] = $rule;
}
}
if (!$this->rule && in_array($ruleType, array_keys($this->ruleTypes))) {
$this->rule = new $ruleType($ruleId);
}
$this->rule->setSiblings($rule_siblings);
} elseif (Request::get('rule')) {
$rule = Request::getObject('rule', 'AdmissionRule');
if ($ruleType == get_class($rule)) {
$this->rule = $rule;
}
} elseif (in_array($ruleType, array_keys($this->ruleTypes))) {
$this->rule = new $ruleType($ruleId);
}
if ($this->rule) {
$this->ruleTemplate = $this->rule->getTemplate();
}
}
/**
* Shows a form for selecting which rule type to use.
*
* @param String $cs_id ID of a courseset the rule shall belong to.
*/
public function select_type_action($cs_id = '')
{
$this->ruleTypes = AdmissionRule::getAvailableAdmissionRules();
$this->courseset = new CourseSet($cs_id);
$this->courseset->clearAdmissionRules();
foreach (Request::getManyObjects('rules', 'AdmissionRule') as $rule) {
$this->courseset->addAdmissionRule($rule);
}
}
/**
* Saves the given rule.
*
* @param String $ruleType The class name of the configured rule.
* @param String $ruleId ID of the rule to save, or empty if this is a new rule.
*/
public function save_action($ruleType, $ruleId = '')
{
CSRFProtection::verifyUnsafeRequest();
$this->rule = $this->loadRule($ruleType, $ruleId);
$requestData = Request::getInstance();
// Check for start and end date and parse the String values to timestamps.
if (!empty($requestData['start_date'])) {
$parsed = date_parse($requestData['start_date']);
$timestamp = mktime($parsed['hour'], $parsed['minute'], 0,
$parsed['month'], $parsed['day'], $parsed['year']);
$requestData['start_time'] = $timestamp;
}
if (!empty($requestData['end_date'])) {
$parsed = date_parse($requestData['end_date']);
$timestamp = mktime($parsed['hour'], $parsed['minute'], 0,
$parsed['month'], $parsed['day'], $parsed['year']);
$requestData['end_time'] = $timestamp;
}
$this->rule->setAllData($requestData);
}
/**
* Validates if the values given in the current request are sufficient to
* configure a rule of the given type.
*
* @param String $ruleType Class name of the rule to check.
* @param String $ruleId ID of the rule to save, or empty if this is a new rule.
*/
public function validate_action($ruleType, $ruleId = '')
{
$rule = $this->loadRule($ruleType, $ruleId);
$this->errors = $rule->validate(Request::getInstance());
}
/**
* Loads a rule by string and ensures that it is a subclass of the abstract
* admission rule.
*
* @param string $rule_type
* @param string $rule_id
* @return AdmissionRule
*/
private function loadRule(string $rule_type, string $rule_id = ''): AdmissionRule
{
static $initialized = false;
if (!$initialized) {
// This is neccessary so that all admission rules are correctly
// loaded and known to the system
AdmissionRule::getAvailableAdmissionRules();
$initialized = true;
}
if (!is_a($rule_type, AdmissionRule::class, true)) {
throw new InvalidArgumentException('Rule type must be a subclass of ' . AdmissionRule::class);
}
return new $rule_type($rule_id);
}
}
......@@ -27,7 +27,6 @@ class Admission_RuleadministrationController extends AuthenticatedController
$GLOBALS['perm']->check('root');
Navigation::activateItem('/admin/config/admissionrules');
PageLayout::addScript('studip-admission.js');
$sidebar = Sidebar::Get();
......
......@@ -26,7 +26,6 @@ class Admission_UserlistController extends AuthenticatedController
PageLayout::setTitle(_('Personenlisten'));
Navigation::activateItem('/browse/coursesets/userlists');
PageLayout::addScript('studip-admission.js');
Sidebar::get()->addWidget(new ActionsWidget())->addLink(
_('Personenliste anlegen'),
......
......@@ -52,18 +52,8 @@ class Api_Oauth2_AuthorizeController extends OAuth2Controller
$authToken = randomString(32);
$this->freezeSessionVars($authRequest, $authToken);
// show login form if not logged in
$authPlugin = Config::get()->getValue('API_OAUTH_AUTH_PLUGIN');
if ('nobody' === $GLOBALS['user']->id && 'Standard' !== $authPlugin && !Request::option('sso')) {
$queryParams = $psrRequest->getQueryParams();
$queryParams['sso'] = strtolower($authPlugin);
$this->redirect($this->url_for('api/oauth2/authorize', $queryParams));
return;
} else {
if ('nobody' === $GLOBALS['user']->id) {
throw new LoginException();
}
if ('nobody' === $GLOBALS['user']->id) {
throw new LoginException();
}
$this->client = $client;
......
......@@ -8,6 +8,8 @@ abstract class OAuth2Controller extends StudipController
{
use NegotiatesWithPsr7;
protected $with_session = true;
/**
* @return void
*/
......
......@@ -773,7 +773,7 @@ class Calendar_CalendarController extends AuthenticatedController
}
}
PageLayout::postSuccess(_('Die Zuordnung von Veranstaltungen zum Kalender wurde aktualisiert.'));
$this->redirect('calendar/schedule/index');
$this->redirect('calendar/calendar/index');
}
}
......