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
Select Git revision

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
  • erik.hillmann/studip
20 results
Select Git revision
Show changes
Commits on Source (394)
Showing
with 352 additions and 426 deletions
...@@ -3,7 +3,17 @@ ...@@ -3,7 +3,17 @@
# MYSQL_PASSWORD="" # MYSQL_PASSWORD=""
# MYSQL_DATABASE="" # 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_CACHING_ENABLE=""
# STUDIP_CACHE_IS_SESSION_STORAGE="" # STUDIP_CACHE_IS_SESSION_STORAGE=""
......
...@@ -67,8 +67,11 @@ stages: ...@@ -67,8 +67,11 @@ stages:
.definitions: .definitions:
mariadb-service: &mariadb-service mariadb-service: &mariadb-service
- name: mariadb - name: mariadb:10.2.7
command: [ "--sql_mode=","--character-set-client=utf8","--character-set-server=utf8","--collation-server=utf8_unicode_ci"] 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: build-composer:
stage: build stage: build
...@@ -108,7 +111,8 @@ lint-php: ...@@ -108,7 +111,8 @@ lint-php:
- COMPOSER_MEMORY_LIMIT=-1 - COMPOSER_MEMORY_LIMIT=-1
composer exec phplint composer exec phplint
-- --
--log-json=$PHPLINT_JSON_REPORT --output=$PHPLINT_JSON_REPORT
--format=json
--cache=$CACHE_LOCATION --cache=$CACHE_LOCATION
after_script: after_script:
- ./.gitlab/scripts/convert-phplint-report $PHPLINT_JSON_REPORT > $PHPLINT_CODE_QUALITY_REPORT - ./.gitlab/scripts/convert-phplint-report $PHPLINT_JSON_REPORT > $PHPLINT_CODE_QUALITY_REPORT
...@@ -138,7 +142,8 @@ lint-php-8.3: ...@@ -138,7 +142,8 @@ lint-php-8.3:
- COMPOSER_MEMORY_LIMIT=-1 - COMPOSER_MEMORY_LIMIT=-1
composer exec phplint composer exec phplint
-- --
--log-json=$PHPLINT_JSON_REPORT --output=$PHPLINT_JSON_REPORT
--format=json
--cache=$CACHE_LOCATION --cache=$CACHE_LOCATION
after_script: after_script:
- ./.gitlab/scripts/convert-phplint-report $PHPLINT_JSON_REPORT > $PHPLINT_CODE_QUALITY_REPORT - ./.gitlab/scripts/convert-phplint-report $PHPLINT_JSON_REPORT > $PHPLINT_CODE_QUALITY_REPORT
...@@ -273,11 +278,12 @@ test-functional: ...@@ -273,11 +278,12 @@ test-functional:
variables: variables:
FUNCTIONAL_XML_REPORT: $REPORT_DIR/functional-report.xml FUNCTIONAL_XML_REPORT: $REPORT_DIR/functional-report.xml
FUNCTIONAL_CODE_QUALITY_REPORT: $REPORT_DIR/functional-codequality.json FUNCTIONAL_CODE_QUALITY_REPORT: $REPORT_DIR/functional-codequality.json
MYSQL_HOST: mysql
cache: cache:
<<: *composer-cache <<: *composer-cache
policy: pull policy: pull
services: services:
- *mariadb-service - *mysql-service
allow_failure: false allow_failure: false
interruptible: true interruptible: true
before_script: 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 # 20.12.2024 v 5.5.3
https://gitlab.studip.de/studip/studip/-/issues?milestone_title=Stud.IP+5.5.3&state=all https://gitlab.studip.de/studip/studip/-/issues?milestone_title=Stud.IP+5.5.3&state=all
......
...@@ -38,7 +38,7 @@ PROJECT_NAME = Stud.IP ...@@ -38,7 +38,7 @@ PROJECT_NAME = Stud.IP
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 6.0 PROJECT_NUMBER = 6.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a
......
...@@ -2,8 +2,8 @@ Die Installation von Stud.IP ist halb so schlimm: ...@@ -2,8 +2,8 @@ Die Installation von Stud.IP ist halb so schlimm:
es müssen lediglich ein paar Dateien kopiert werden und ein paar es müssen lediglich ein paar Dateien kopiert werden und ein paar
Programme laufen ;-) Programme laufen ;-)
Vorausgesetzt wird ein Webserver wie Apache2 oder nginx mit PHP-7.4 Modulen und Vorausgesetzt wird ein Webserver wie Apache2 oder nginx mit PHP-8.1 Modulen und
eine MySQL 5.7.6 Datenbank. eine MySQL 8 oder MariaDB 10.2.2 Datenbank.
Was genau zu tun ist, steht in Was genau zu tun ist, steht in
> doc/de/studip-installation-guide-de-401.pdf > doc/de/studip-installation-guide-de-401.pdf
......
...@@ -87,12 +87,12 @@ optimize-icons: npm ...@@ -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 find public/assets/images/icons/blue -type f | xargs -P0 npx svgo -q --config=config/svgo.config.js
icons: optimize-icons 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/#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/" {} > {}' | 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/#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/" {} > {}' | 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/#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/" {} > {}' | 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/#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/" {} > {}' | 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/#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/" {} > {}' | 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/#ffad00/g" {} > {}' | sed 's#icons/blue#icons/yellow#2' | sh
# default rules for gettext handling # default rules for gettext handling
js-%.pot: $(VUE_SOURCES) js-%.pot: $(VUE_SOURCES)
......
# Stud.IP v6.0 # Stud.IP v6.1
**15.03.2024** **dd.mm.jjjj**
## Neue Features ## Neue Features
- 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))
## Breaking changes ## Breaking changes
- Mindestanforderung an PHP auf 8.1 angehoben ([TIC #3805](https://gitlab.studip.de/studip/studip/issues/3805)) -
- Im Rahmen von [Issue #3788](https://gitlab.studip.de/studip/studip/-/issues/3788) wurden die Zusätze an allen Icons entfernt. Dadurch kann es sein, dass manche Plugins nicht mehr erscheinen. Diese müssen dann auf eine Variante ohne Zusätze umgestellt werden.
- Die Funktion `get_config()` wurde entfernt. Stattdessen muss die Methode `Config::get()->getValue('CONFIG_KEY')` bzw. der Shortcut `Config::get()->CONFIG_KEY` verwendet werden. ([Issue #2797](https://gitlab.studip.de/studip/studip/-/issues/2797))
- Die Funktion `smile()` wurde entfernt. Sie kann ersatzlos entfernt werden. ([Issue #3158](https://gitlab.studip.de/studip/studip/-/issues/3158))
- Die Funktion `transformBeforeSave()` wurde entfernt. Sie kann ersatzlos entfernt werden. ([Issue #3159](https://gitlab.studip.de/studip/studip/-/issues/3159))
- Die schon lange nicht mehr genutzten Methoden zum Setzen, Auslesen und Enfernen von Schmuckgrafiken von Bildern für die Sidebar wurde entfernt. Die Methoden `Sidebar::setImage()`, `Sidebar::getImage()` sowie `Sidebar::removeImage()` müssen ersatzlos entfernt werden. ([Issue #3157](https://gitlab.studip.de/studip/studip/-/issues/3157))
- Der zweite Parameter für die Methode `Navigation::setImage()` wurde entfernt. Der Parameter schien sich auf das Bild zu beziehen, hat aber Attribute an dem Link gesetzt. Stattdessen muss die Methode `Navigation::setLinkAttributes()` verwendet werden. ([Issue #3578](https://gitlab.studip.de/studip/studip/-/issues/3578))
- Die Unterstützung für LESS-Stylsheets in Plugins wurde entfernt. Als Alternative wird SCSS unterstützt. ([Issue #2720](https://gitlab.studip.de/studip/studip/-/issues/2720))
- Die Funktionen `studip_json_encode()` und `studip_json_decode()` wurden entfernt. Stattdessen müssen die Methode `json_encode()` und `json_decode()` verwendet werden. ([Issue #3814](https://gitlab.studip.de/studip/studip/-/issues/3814))
- Die `MembersModel.php` wurde entfernt ([Issue #3811](https://gitlab.studip.de/studip/studip/-/issues/3811))
- Die `admission.inc.php` wurde entfernt. ([Issue #3812](https://gitlab.studip.de/studip/studip/-/issues/3812))
- Die folgenden Funktionen wurden aus der Datei `lib/functions.php` entfernt: `re_sort_dozenten()`, `re_sort_tutoren()` und `get_next_position()` ([Issue #4002](https://gitlab.studip.de/studip/studip/-/issues/4002))
- Die Methoden `CronjobScheduler::scheduleOnce()` sowie `CronjobTask::scheduleOnce()` wurden ersatzlos entfernt. ([Issue #4078](https://gitlab.studip.de/studip/studip/-/issues/4078))
- Die folgenden Klassen wurden innerhalb von Stud.IP verschoben. Da sie über den Autoloader geladen werden, kann jedes manuelle Einbinden ersatzlos entfernt werden. ([Issue #4105](https://gitlab.studip.de/studip/studip/-/issues/4105))
- `AuthenticatedController`
- `PluginController`
- `StudipController`
- `StudipControllerPropertiesTrait`
- `StudipResponse`
- Im Rahmen von [Issue #4118](https://gitlab.studip.de/studip/studip/-/issues/4118) wurde `write_excel` ausgebaut. Als Alternative kann `phpoffice/phpspreadsheet` verwendet werden.
- Im Rahmen von [TIC #3701](https://gitlab.studip.de/studip/studip/-/issues/3701) wurden die Klassen für den Cache umbenannt. Alle Vorkommnisse sollten ersetzt werden:
- `StudipCacheFactory` -> `Studip\Cache\Factory`
- Die Bibliothek `opis/json-schema` wurde auf Version 2.3.0 aktualisiert ([Issue #4173](https://gitlab.studip.de/studip/studip/-/issues/4173)). Dadurch ergeben sich die folgenden Änderungen für Komponenten aus Courseware (siehe auch [Migration Guide](https://opis.io/json-schema/2.x/php-migration.html#validator)):
- Instanzen von `Courseware\ContainerTypes\BlockType` müssen die Methode `getJsonSchema` anpassen. Der Return Type ist nun `string` und es muss der Inhalt der Schema-Datei zurückgegeben werden ohne Aufruf von `Schema::fromJsonString()`.
- Instanzen von `Courseware\ContainerTypes\ContainerType` müssen die Methode `getJsonSchema` anpassen. Der Return Type ist nun `string` und es muss der Inhalt der Schema-Datei zurückgegeben werden ohne Aufruf von `Schema::fromJsonString()`.
- Die von Stud.IP verwendete Template-Bibliothek "Flexi Templates" wurde vollständig in den Kern integriert.
Obwohl die Umstellung abwärtskompatibel sein sollte, sollten die Klassen folgendermassen ersetzt werden:
- `Flexi_TemplateFactory` > `Flexi\Factory`
- `Flexi_Template` > `Flexi\Template`
- `Flexi_PhpTemplate` > `Flexi\PhpTemplate`
- `Flexi_TemplateNotFoundException` > `Flexi\TemplateNotFoundException`
Sollte ein Plugin manuell Flexi einbinden, so wird dies zu einem Fehler führen. Jegliches Einbinden von Dateien
unterhalb von `vendor/flexi` muss ersatzlos entfernt werden.
- Die folgenden Funktionen wurden entfernt ([Issue #4179](https://gitlab.studip.de/studip/studip/-/issues/4179))
- `getWeekdays($short = true)`
- `veranstaltung_beginn($seminar_id = '', $return_mode = '')`
- `veranstaltung_beginn_from_metadata($reg_irreg, $sem_begin, $start_woche, $start_termin,$turnus_data, $return_mode='int')`
- `get_sem_name ($time)`
- `get_sem_num ($time)`
- `get_sem_num_sem_browse ()`
- `get_semester($seminar_id)`
- `delete_date($termin_id, $topic_delete = TRUE, $folder_move = TRUE, $sem_id=0)`
- `delete_range_of_dates($range_id, $topics = FALSE)`
- `isSchedule ($sem_id, $presence_dates_only = TRUE, $clearcache = FALSE)`
- `isMetadateCorrespondingDate ($termin_id, $begin = '', $end = '', $seminar_id='')`
- `getPresenceTypes()`
- Die Klasse `AuxLockRules` wurde ausgebaut. ([Issue #4187](https://gitlab.studip.de/studip/studip/-/issues/4187))
- Die Klasse `ProfileModel` wurde gelöscht. Die darin enthaltenen Methoden wurden in den `Profile_Controller` verschoben. ([Issue #4185]https://gitlab.studip.de/studip/studip/-/issues/4185))
- Die Klasse `StudipTransformFormat` wurde ausgebaut ([Issue #4188](https://gitlab.studip.de/studip/studip/-/issues/4188))
- Die REST-API (`public/api.php`) wurde zu Stud.IP 5.0 deprecated und nun mit Stud.IP 6.0 entfernt. Als Ersatz steht die JSONAPI zur Verfügung. ([Issue #2798](https://gitlab.studip.de/studip/studip/-/issues/2798))
- Die Klassen `CalendarView`, `CalendarScheduleModel` sowie alle davon abhängigen Klassen wurden entfernt. ([Issue #4421](https://gitlab.studip.de/studip/studip/-/issues/4421))
- Anstelle von `CalendarView` sollte `\Studip\Fullcalendar` verwendet werden.
- Das Datenbankschema des Stundenplans wurde geändert. ([Issue #4421](https://gitlab.studip.de/studip/studip/-/issues/4421))
- Die Evaluationen wurden ausgebaut. Stattdessen sollte man nun die neuen Fragebögen verwenden ([Issue #3787]https://gitlab.studip.de/studip/studip/-/issues/3787)
- Die Klassen `DbView`, `DbSnapshot` und die zugehörigen Dateien in `lib/dbviews` wurden ausgebaut. ([Issue #4390](https://gitlab.studip.de/studip/studip/-/issues/4390))
- Als Ersatz dienen Datenbankabfragen mittels der `DBManager`-Klasse oder mittels `SimpleORMap`-Modellen.
- Es wurden zwei neue CLI-Kommandos hinzugefügt, womit man Klassenrümpfe für SORM-Models und Migrationen erstellen kann. Bei den Migrationen wird die Versionsnummer für die jeweilige `domain` automatisch ermittelt.
- `cli/studip make:model` und `cli/studip make:migration`.
- Es wurde ein neues CLI-Kommando hinzugefügt, womit man auf einfache Weise ein Plugin-Grundgerüst erstellen kann.
- `cli/studip make:plugin`
- Die Klasse `Seminar`, sowie die Klassen in `lib/raumzeit` wurden ausgebaut. ([Issue #3209](https://gitlab.studip.de/studip/studip/-/issues/3209))
- Als Ersatz für viele Methoden der Seminar-Klasse dienen die Klassen `Course`, `CourseDate` und `SeminarCycleDate`, sowie die neue `CourseDateList`-Klasse.
- Die Klassen `TreeAbstract`, `TreeView` und `SemBrowse` wurden ausgebaut. ([Issue #4392](https://gitlab.studip.de/studip/studip/-/issues/4392))
- Zur Anzeige von Baumstrukturen können als Ersatz die Implementierungen des `StudipTreeNode`-Interfaces genutzt werden.
- Die Zuordnung von Veranstaltungen zu Semestern anhand von Timestamps wurde entfernt. In der Datenbank wurden die Spalten `start_time` und `duration_time` der Tabelle `seminare` entfernt. ([Issue #4391]https://gitlab.studip.de/studip/studip/-/issues/4391))
- Plugins, die Veranstaltungen anhand von Timestamps laden oder anderweitig verwenden, müssen angepasst werden!
- Das Mapping von Veranstaltungen zu Semestern findet nun ausschließlich anhand der Semester-ID über die Verknüpfungstabelle `semester_courses` statt.
- Die ELearning-Schnittstelle wurde ausgebaut. In diesem Rahmen wurden auch die Methoden `printhead()` und `printcontent()` ersatzlos entfernt. ([Issue #4109](https://gitlab.studip.de/studip/studip/-/issues/4109))
## Security related issues ## Security related issues
......
RELEASE 6.0.alpha RELEASE 6.1.alpha
...@@ -31,7 +31,9 @@ class Admin_AdditionalController extends AuthenticatedController ...@@ -31,7 +31,9 @@ class Admin_AdditionalController extends AuthenticatedController
"Veranstaltung zu verändern.")); "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 ...@@ -44,11 +44,11 @@ class Admin_AutoinsertController extends AuthenticatedController
if (Request::submitted('suchen')) { if (Request::submitted('suchen')) {
if (Request::get('sem_search')) { if (Request::get('sem_search')) {
$this->sem_search = 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(); $search = new SeminarSearch();
$this->seminar_search = $search->getResults $this->seminar_search = $search->getResults(
(Request::get('sem_search'), $this->sem_search,
['search_sem_sem' => Request::option('sem_select')] ['search_sem_sem' => $this->sem_select]
); );
if (count($this->seminar_search) == 0) { if (count($this->seminar_search) == 0) {
PageLayout::postInfo(_('Es wurden keine Veranstaltungen gefunden.')); PageLayout::postInfo(_('Es wurden keine Veranstaltungen gefunden.'));
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
* @since 3.1 * @since 3.1
*/ */
require_once 'lib/meine_seminare_func.inc.php';
require_once 'lib/object.inc.php'; require_once 'lib/object.inc.php';
require_once 'lib/archiv.inc.php'; //for lastActivity in getCourses() method require_once 'lib/archiv.inc.php'; //for lastActivity in getCourses() method
...@@ -298,7 +297,6 @@ class Admin_CoursesController extends AuthenticatedController ...@@ -298,7 +297,6 @@ class Admin_CoursesController extends AuthenticatedController
PageLayout::setHelpKeyword('Basis.Veranstaltungen'); PageLayout::setHelpKeyword('Basis.Veranstaltungen');
PageLayout::setTitle(_('Verwaltung von Veranstaltungen und Einrichtungen')); PageLayout::setTitle(_('Verwaltung von Veranstaltungen und Einrichtungen'));
// Add admission functions. // Add admission functions.
PageLayout::addScript('studip-admission.js');
$this->max_show_courses = Config::get()->MAX_SHOW_ADMIN_COURSES; $this->max_show_courses = Config::get()->MAX_SHOW_ADMIN_COURSES;
} }
...@@ -483,7 +481,7 @@ class Admin_CoursesController extends AuthenticatedController ...@@ -483,7 +481,7 @@ class Admin_CoursesController extends AuthenticatedController
$data['buttons_bottom'] = (string) \Studip\Button::createAccept( $data['buttons_bottom'] = (string) \Studip\Button::createAccept(
_('Teilnehmendenexport'), 'batch_export_members', _('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' 'data-dialog' => 'size=big'
]); ]);
break; break;
...@@ -843,7 +841,7 @@ class Admin_CoursesController extends AuthenticatedController ...@@ -843,7 +841,7 @@ class Admin_CoursesController extends AuthenticatedController
$d['action'] = $template->render(); $d['action'] = $template->render();
break; break;
case 22: //Masssenexport Teilnehmendendaten case 22: //Masssenexport Teilnehmendendaten
$template = $tf->open('admin/courses/batch_export_members'); $template = $tf->open('admin/courses/export_members');
$template->course = $course; $template->course = $course;
$d['action'] = $template->render(); $d['action'] = $template->render();
break; break;
...@@ -1332,6 +1330,73 @@ class Admin_CoursesController extends AuthenticatedController ...@@ -1332,6 +1330,73 @@ class Admin_CoursesController extends AuthenticatedController
$this->notice = $course->config->COURSE_ADMIN_NOTICE; $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 * Return a specifically action or all available actions
...@@ -1442,10 +1507,10 @@ class Admin_CoursesController extends AuthenticatedController ...@@ -1442,10 +1507,10 @@ class Admin_CoursesController extends AuthenticatedController
22 => [ 22 => [
'name' => _('Teilnehmendenexport'), 'name' => _('Teilnehmendenexport'),
'title' => _('Teilnehmendenexport'), 'title' => _('Teilnehmendenexport'),
'url' => 'dispatch.php/admin/user/batch_export_members', 'url' => 'dispatch.php/admin/courses/batch_export_members',
'dialogform' => true, 'dialogform' => true,
'multimode' => true, 'multimode' => true,
'partial' => 'batch_export_members.php' 'partial' => 'export_members.php'
], ],
23 => [ 23 => [
......
...@@ -156,7 +156,13 @@ class Admin_DatafieldsController extends AuthenticatedController ...@@ -156,7 +156,13 @@ class Admin_DatafieldsController extends AuthenticatedController
} elseif ($type === 'studycourse') { } elseif ($type === 'studycourse') {
$datafield->object_class = Request::option('object_class'); $datafield->object_class = Request::option('object_class');
} else { } 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->edit_perms = Request::get('edit_perms');
$datafield->view_perms = Request::get('visibility_perms'); $datafield->view_perms = Request::get('visibility_perms');
...@@ -188,6 +194,7 @@ class Admin_DatafieldsController extends AuthenticatedController ...@@ -188,6 +194,7 @@ class Admin_DatafieldsController extends AuthenticatedController
$this->institutes = Institute::getMyInstitutes(); $this->institutes = Institute::getMyInstitutes();
if (!$this->object_typ) { if (!$this->object_typ) {
$this->render_action('type_select'); $this->render_action('type_select');
return;
} }
if (Request::isXhr() && $this->type_name) { if (Request::isXhr() && $this->type_name) {
......
...@@ -122,7 +122,7 @@ class Admin_IliasInterfaceController extends AuthenticatedController ...@@ -122,7 +122,7 @@ class Admin_IliasInterfaceController extends AuthenticatedController
$this->valid_url = false; $this->valid_url = false;
$this->ilias_version = ''; $this->ilias_version = '';
$this->ilias_version_date = ''; $this->ilias_version_date = '';
$this->clients = []; $this->ilias_clients = [];
if ($index === 'new') { if ($index === 'new') {
// default values // default values
$this->ilias_config = [ $this->ilias_config = [
...@@ -130,6 +130,8 @@ class Admin_IliasInterfaceController extends AuthenticatedController ...@@ -130,6 +130,8 @@ class Admin_IliasInterfaceController extends AuthenticatedController
'name' => '', 'name' => '',
'version' => '', 'version' => '',
'url' => _('https://<URL zur ILIAS-Installation>'), 'url' => _('https://<URL zur ILIAS-Installation>'),
'http_connection_timeout' => 1,
'http_request_timeout' => 3,
'client' => '', 'client' => '',
'ldap_enable' => '', 'ldap_enable' => '',
'reconnect_accounts' => false, 'reconnect_accounts' => false,
...@@ -168,9 +170,11 @@ class Admin_IliasInterfaceController extends AuthenticatedController ...@@ -168,9 +170,11 @@ class Admin_IliasInterfaceController extends AuthenticatedController
// get ILIAS server info // get ILIAS server info
if (Request::get('ilias_url')) { if (Request::get('ilias_url')) {
$info = ConnectedIlias::getIliasInfo(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->valid_url = true;
$this->ilias_config['url'] = Request::get('ilias_url'); $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']) { if ($info['version']) {
$this->ilias_version = $info['version']; $this->ilias_version = $info['version'];
$this->ilias_version_date = $info['version_date']; $this->ilias_version_date = $info['version_date'];
...@@ -220,6 +224,8 @@ class Admin_IliasInterfaceController extends AuthenticatedController ...@@ -220,6 +224,8 @@ class Admin_IliasInterfaceController extends AuthenticatedController
if (Request::get('ilias_name')) { if (Request::get('ilias_name')) {
$this->ilias_config['name'] = Request::get('ilias_name'); $this->ilias_config['name'] = Request::get('ilias_name');
$this->ilias_config['url'] = Request::get('ilias_url'); $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']); $info = ConnectedIlias::getIliasInfo($this->ilias_config['url']);
if (count($info)) { if (count($info)) {
...@@ -241,14 +247,15 @@ class Admin_IliasInterfaceController extends AuthenticatedController ...@@ -241,14 +247,15 @@ class Admin_IliasInterfaceController extends AuthenticatedController
*/ */
public function edit_content_action($index) public function edit_content_action($index)
{ {
$this->ilias_config = $this->ilias_configs[$index];
$this->ilias_index = $index; $this->ilias_index = $index;
$this->ilias_datafields = []; $this->ilias_datafields = [];
$connected_ilias = new ConnectedIlias($index); $connected_ilias = new ConnectedIlias($index);
$this->ilias_config = $connected_ilias->ilias_config;
if ($admin_id = $connected_ilias->soap_client->lookupUser($this->ilias_config['admin'])) { if ($admin_id = $connected_ilias->soap_client->lookupUser($this->ilias_config['admin'])) {
$user = $connected_ilias->soap_client->getUser($admin_id); $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']; $this->ilias_datafields = $user['udfs'];
} }
} }
...@@ -260,10 +267,10 @@ class Admin_IliasInterfaceController extends AuthenticatedController ...@@ -260,10 +267,10 @@ class Admin_IliasInterfaceController extends AuthenticatedController
*/ */
public function edit_permissions_action($index) public function edit_permissions_action($index)
{ {
$this->ilias_config = $this->ilias_configs[$index];
$this->ilias_index = $index; $this->ilias_index = $index;
$connected_ilias = new ConnectedIlias($index); $connected_ilias = new ConnectedIlias($index);
$this->ilias_config = $connected_ilias->ilias_config;
$this->global_roles = $connected_ilias->soap_client->getRoles('global', -1); $this->global_roles = $connected_ilias->soap_client->getRoles('global', -1);
} }
...@@ -298,6 +305,8 @@ class Admin_IliasInterfaceController extends AuthenticatedController ...@@ -298,6 +305,8 @@ class Admin_IliasInterfaceController extends AuthenticatedController
$this->ilias_configs[$index]['version'] = Request::get('ilias_version'); $this->ilias_configs[$index]['version'] = Request::get('ilias_version');
} }
$this->ilias_configs[$index]['url'] = Request::get('ilias_url'); $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')) { if (Request::getInstance()->offsetExists('ilias_client')) {
$this->ilias_configs[$index]['client'] = Request::get('ilias_client'); $this->ilias_configs[$index]['client'] = Request::get('ilias_client');
} }
...@@ -496,28 +505,31 @@ class Admin_IliasInterfaceController extends AuthenticatedController ...@@ -496,28 +505,31 @@ class Admin_IliasInterfaceController extends AuthenticatedController
*/ */
public function soap_methods_action($index) public function soap_methods_action($index)
{ {
if ($this->ilias_configs[$index]['is_active']) { $ilias = new ConnectedIlias($index);
$ilias = new ConnectedIlias($index);
$this->soap_methods = $ilias->getSoapMethods(); $this->soap_methods = $ilias->getSoapMethods();
ksort($this->soap_methods); ksort($this->soap_methods);
$this->ilias_index = $index; $this->ilias_index = $index;
if (Request::get('ilias_soap_method')) { if (Request::get('ilias_soap_method')) {
$this->ilias_soap_method = 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) { foreach ($this->soap_methods[Request::get('ilias_soap_method')] as $param) {
switch ($param) { switch ($param) {
case "sid" : $this->params[$param] = $ilias->soap_client->getSID(); case "sid" : $this->params[$param] = $ilias->soap_client->getSID();
break; break;
case "user_id" : $this->params[$param] = $ilias->user->getId(); case "user_id" : $this->params[$param] = is_object($ilias->user) ? $ilias->user->getId() : '';
break; break;
}
} }
} elseif (Request::get('ilias_call')) { }
$params = []; } elseif (Request::get('ilias_call')) {
foreach ($this->soap_methods[Request::get('ilias_call')] as $param) { $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); $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 ...@@ -60,12 +60,13 @@ class Admin_OverlappingController extends AuthenticatedController
$GLOBALS['user']->id $GLOBALS['user']->id
]) ])
); );
$_SESSION['MVV_OVL_SELECTION_ID'] = $selection_id; $_SESSION['MVV_OVL_SELECTION_ID'] = $selection_id;
$this->selection_id = ''; $this->selection_id = '';
if (count($selections)) { if (count($selections)) {
$this->base_version = StgteilVersion::find($selections->first()->base_version_id); $this->base_version = StgteilVersion::find($selections->first()->base_version_id);
$this->fachsems = explode(',', $selections->first()->fachsems); $this->fachsems = $selections->first()->fachsems === '' ? [] : explode(',', $selections->first()->fachsems);
$this->semtypes = explode(',', $selections->first()->semtypes); $this->semtypes = $selections->first()->semtypes === '' ? [] : explode(',', $selections->first()->semtypes);
$this->comp_versions = StgteilVersion::findMany($selections->pluck('comp_version_id')); $this->comp_versions = StgteilVersion::findMany($selections->pluck('comp_version_id'));
$this->selection_id = $selections->first()->selection_id; $this->selection_id = $selections->first()->selection_id;
if (Request::int('show_hidden') !== null) { if (Request::int('show_hidden') !== null) {
...@@ -84,6 +85,78 @@ class Admin_OverlappingController extends AuthenticatedController ...@@ -84,6 +85,78 @@ class Admin_OverlappingController extends AuthenticatedController
$this->selection_id, $this->selection_id,
empty($_SESSION['MVV_OVL_HIDDEN']) 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 ...@@ -120,61 +193,59 @@ class Admin_OverlappingController extends AuthenticatedController
$this->fachsems = Request::intArray('fachsems'); $this->fachsems = Request::intArray('fachsems');
$this->semtypes = Request::intArray('semtypes'); $this->semtypes = Request::intArray('semtypes');
if (Request::submitted('compare')) { $selection_id = MvvOverlappingSelection::createSelectionId(
$selection_id = MvvOverlappingSelection::createSelectionId( $this->base_version,
$this->base_version, $this->comp_versions,
$this->comp_versions, $this->fachsems,
$this->fachsems, $this->semtypes,
$this->semtypes, $this->selected_semester->id
$this->selected_semester->id );
);
// refresh conflicts // refresh conflicts
MvvOverlappingConflict::deleteBySelection($selection_id); MvvOverlappingConflict::deleteBySelection($selection_id);
foreach ($this->comp_versions as $comp_version) { foreach ($this->comp_versions as $comp_version) {
$selection[$comp_version->id] = MvvOverlappingSelection::findOneBySQL( $selection[$comp_version->id] = MvvOverlappingSelection::findOneBySQL(
'`selection_id` = ? AND `comp_version_id` = ?', [ '`selection_id` = ? AND `comp_version_id` = ?', [
$selection_id, $selection_id,
$comp_version->id $comp_version->id
]); ]);
if (!$selection[$comp_version->id]) { if (!$selection[$comp_version->id]) {
$selection[$comp_version->id] = new MvvOverlappingSelection(); $selection[$comp_version->id] = new MvvOverlappingSelection();
$selection[$comp_version->id]->semester_id = $this->selected_semester->id; $selection[$comp_version->id]->semester_id = $this->selected_semester->id;
$selection[$comp_version->id]->selection_id = $selection_id; $selection[$comp_version->id]->selection_id = $selection_id;
$selection[$comp_version->id]->base_version_id = $this->base_version->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]->comp_version_id = $comp_version->id;
$selection[$comp_version->id]->setFachsemester($this->fachsems); $selection[$comp_version->id]->setFachsemester($this->fachsems);
$selection[$comp_version->id]->setCourseTypes($this->semtypes); $selection[$comp_version->id]->setCourseTypes($this->semtypes);
$selection[$comp_version->id]->user_id = $GLOBALS['user']->id; $selection[$comp_version->id]->user_id = $GLOBALS['user']->id;
$selection[$comp_version->id]->store(); $selection[$comp_version->id]->store();
}
$selection[$comp_version->id]->storeConflicts();
} }
$conflicts = MvvOverlappingSelection::getConflictsBySelection($selection_id); $selection[$comp_version->id]->storeConflicts();
$visible_conflicts = MvvOverlappingSelection::getConflictsBySelection($selection_id, true); }
if (count($conflicts)) { $conflicts = MvvOverlappingSelection::getConflictsBySelection($selection_id);
if (count($conflicts) != count($visible_conflicts)) { $visible_conflicts = MvvOverlappingSelection::getConflictsBySelection($selection_id, true);
PageLayout::postSuccess( if (count($conflicts)) {
sprintf( if (count($conflicts) !== count($visible_conflicts)) {
ngettext('1 Konflikt gefunden (1 ausgeblendet)', PageLayout::postSuccess(
'%s Konflikte gefunden (%s ausgeblendet).', count($conflicts)), sprintf(
count($conflicts), ngettext('1 Konflikt gefunden (1 ausgeblendet)',
count($conflicts) - count($visible_conflicts) '%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)
)
);
}
} else { } 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 { } else {
PageLayout::postError('Die Basis-Version muss angegeben werden!'); PageLayout::postError('Die Basis-Version muss angegeben werden!');
......
...@@ -108,6 +108,7 @@ class Admin_StatusgroupsController extends AuthenticatedController ...@@ -108,6 +108,7 @@ class Admin_StatusgroupsController extends AuthenticatedController
AND auth_user_md5.visible <> 'never' AND auth_user_md5.visible <> 'never'
ORDER BY Vorname, Nachname"; ORDER BY Vorname, Nachname";
$this->searchType = new SQLSearch($query, _('Teilnehmende/n suchen'), 'username'); $this->searchType = new SQLSearch($query, _('Teilnehmende/n suchen'), 'username');
$this->addQuickfilter = count($this->groups) * count($this->membersOfInstitute) < 500;
} }
/** /**
......
...@@ -16,10 +16,6 @@ ...@@ -16,10 +16,6 @@
* @since 2.1 * @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 'vendor/email_message/blackhole_message.php';
require_once 'lib/statusgruppe.inc.php'; require_once 'lib/statusgruppe.inc.php';
...@@ -1662,88 +1658,6 @@ class Admin_UserController extends AuthenticatedController ...@@ -1662,88 +1658,6 @@ class Admin_UserController extends AuthenticatedController
$this->redirect($this->show_user_coursesURL($user)); $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 * Init sidebar
*/ */
......
...@@ -34,8 +34,6 @@ class Admission_CoursesetController extends AuthenticatedController ...@@ -34,8 +34,6 @@ class Admission_CoursesetController extends AuthenticatedController
throw new AccessDeniedException(); throw new AccessDeniedException();
} }
PageLayout::addScript('studip-admission.js');
$views = new ActionsWidget(); $views = new ActionsWidget();
$views->addLink( $views->addLink(
_('Anmeldeset anlegen'), _('Anmeldeset anlegen'),
...@@ -89,7 +87,7 @@ class Admission_CoursesetController extends AuthenticatedController ...@@ -89,7 +87,7 @@ class Admission_CoursesetController extends AuthenticatedController
if (!isset($this->myInstitutes['all']['count']) || $this->myInstitutes['all']['count'] < 100) { if (!isset($this->myInstitutes['all']['count']) || $this->myInstitutes['all']['count'] < 100) {
$this->current_institut_id = 'all'; $this->current_institut_id = 'all';
} else { } 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); $chunks = explode('_', $this->current_institut_id);
...@@ -293,7 +291,8 @@ class Admission_CoursesetController extends AuthenticatedController ...@@ -293,7 +291,8 @@ class Admission_CoursesetController extends AuthenticatedController
$this->myUserlists $this->myUserlists
) )
), ),
'institute-search' => (string) $this->isearch 'institute-search' => (string) $this->isearch,
'instant-course-set-view' => $this->instant_course_set_view
]; ];
if ($this->courseset) { if ($this->courseset) {
...@@ -374,9 +373,9 @@ class Admission_CoursesetController extends AuthenticatedController ...@@ -374,9 +373,9 @@ class Admission_CoursesetController extends AuthenticatedController
} }
PageLayout::postSuccess(sprintf(_('Das Anmeldeset: %s wurde gespeichert'), htmlReady($courseset->getName()))); PageLayout::postSuccess(sprintf(_('Das Anmeldeset: %s wurde gespeichert'), htmlReady($courseset->getName())));
if ($this->instant_course_set_view) { if ($this->instant_course_set_view) {
$this->redirect($this->url_for('course/admission')); $this->relocate('course/admission');
} else { } 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 ...@@ -398,7 +397,7 @@ class Admission_CoursesetController extends AuthenticatedController
$this->courseset->delete(); $this->courseset->delete();
} }
$this->redirect($this->url_for('admission/courseset')); $this->relocate('admission/courseset');
} }
/** /**
...@@ -607,7 +606,7 @@ class Admission_CoursesetController extends AuthenticatedController ...@@ -607,7 +606,7 @@ class Admission_CoursesetController extends AuthenticatedController
if ($ok) { if ($ok) {
PageLayout::postSuccess(_('Die zugeordneten Veranstaltungen wurden konfiguriert.')); 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; 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 ...@@ -27,7 +27,6 @@ class Admission_RuleadministrationController extends AuthenticatedController
$GLOBALS['perm']->check('root'); $GLOBALS['perm']->check('root');
Navigation::activateItem('/admin/config/admissionrules'); Navigation::activateItem('/admin/config/admissionrules');
PageLayout::addScript('studip-admission.js');
$sidebar = Sidebar::Get(); $sidebar = Sidebar::Get();
......
...@@ -26,7 +26,6 @@ class Admission_UserlistController extends AuthenticatedController ...@@ -26,7 +26,6 @@ class Admission_UserlistController extends AuthenticatedController
PageLayout::setTitle(_('Personenlisten')); PageLayout::setTitle(_('Personenlisten'));
Navigation::activateItem('/browse/coursesets/userlists'); Navigation::activateItem('/browse/coursesets/userlists');
PageLayout::addScript('studip-admission.js');
Sidebar::get()->addWidget(new ActionsWidget())->addLink( Sidebar::get()->addWidget(new ActionsWidget())->addLink(
_('Personenliste anlegen'), _('Personenliste anlegen'),
......