diff --git a/changelog.md b/changelog.md
index d83da2c..926613d 100644
--- a/changelog.md
+++ b/changelog.md
@@ -16,6 +16,7 @@ All important changes to this plugin will be documented in this file.
### Fixed
- Fixed a crash when saving LP settings for a worksheet with a deadline, caused by a malformed entry in the MUMIE grade sync response
+- The multi-problem selector now uses SSO when the configured problem selector and the selected MUMIE server share the same origin, matching the single-problem selector's behavior
## [v5.0] - 2026-06-02
### Changed
diff --git a/classes/class.ilMumieTaskSSOService.php b/classes/class.ilMumieTaskSSOService.php
index a8ee6f3..dddd39c 100644
--- a/classes/class.ilMumieTaskSSOService.php
+++ b/classes/class.ilMumieTaskSSOService.php
@@ -143,7 +143,7 @@ private function getHTMLCode($taskObj, $ssotoken, $hashed_user, $height = 600):
*/
private const LECTURER_SUFFIX = '@lecturer@';
- public function getProblemSelectorLaunchForm(string $serverUrl, string $problemLang, string $origin): string
+ public function getProblemSelectorLaunchForm(string $serverUrl, string $problemLang, string $origin, bool $multiSelect = false): string
{
global $DIC;
$admin_settings = ilMumieTaskAdminSettings::getInstance();
@@ -159,6 +159,7 @@ public function getProblemSelectorLaunchForm(string $serverUrl, string $problemL
$tpl->setVariable('SERVER_URL', htmlspecialchars($serverUrl));
$tpl->setVariable('PROBLEM_LANG', htmlspecialchars($problemLang));
$tpl->setVariable('ORIGIN', htmlspecialchars($origin));
+ $tpl->setVariable('MULTI_SELECT', $multiSelect ? 'true' : 'false');
return $tpl->get();
}
diff --git a/classes/class.ilObjMumieTaskGUI.php b/classes/class.ilObjMumieTaskGUI.php
index 1531cdf..430ad0d 100644
--- a/classes/class.ilObjMumieTaskGUI.php
+++ b/classes/class.ilObjMumieTaskGUI.php
@@ -87,6 +87,7 @@ public function launchProblemSelector(): void
(string) ($_GET['serverUrl'] ?? ''),
(string) ($_GET['problemLang'] ?? ''),
(string) ($_GET['origin'] ?? ''),
+ filter_var($_GET['multiSelect'] ?? false, FILTER_VALIDATE_BOOLEAN),
);
exit;
}
@@ -257,6 +258,14 @@ public function submitMumieTask()
ilMumieTaskLPStatus::updateGrades($this->object, $force_grade_update);
ilMumieTaskGradeOverrideService::deleteGradeOverridesForTask($this->object);
}
+
+ if ($mumieTask->isDummy()) {
+ // Only multi-select problems were chosen, so this placeholder never became a real task itself.
+ $this->removeObjectAndRedirectToParent();
+
+ return;
+ }
+
$tpl->setOnScreenMessage('success', $this->i18N->txt('msg_suc_saved'), true);
$DIC->ctrl()->redirect($this, 'editProperties');
@@ -459,6 +468,20 @@ public function getStandardCmd(): string
* @throws ilInvalidTreeStructureException
*/
public function cancelDummy(): void
+ {
+ $this->removeObjectAndRedirectToParent();
+ }
+
+ /**
+ * Delete this object and return to the parent container's repository listing.
+ *
+ * @throws ilRepositoryException
+ * @throws ilCtrlException
+ * @throws ilObjectNotFoundException
+ * @throws ilDatabaseException
+ * @throws ilInvalidTreeStructureException
+ */
+ private function removeObjectAndRedirectToParent(): void
{
global $DIC;
$tree = $DIC->repositoryTree();
diff --git a/classes/forms/class.ilMumieTaskFormGUI.php b/classes/forms/class.ilMumieTaskFormGUI.php
index 802713e..1799e20 100644
--- a/classes/forms/class.ilMumieTaskFormGUI.php
+++ b/classes/forms/class.ilMumieTaskFormGUI.php
@@ -154,11 +154,15 @@ public function checkInput(): bool
return $ok;
}
+ $multi_problems_input = $this->getInput('xmum_multi_problems');
+
if (null == $task && $is_dummy) {
- $ok = false;
- $this->problem_display_item->setAlert($this->i18N->globalTxt('required_field'));
+ if (empty($multi_problems_input)) {
+ $ok = false;
+ $this->problem_display_item->setAlert($this->i18N->globalTxt('required_field'));
- return $ok;
+ return $ok;
+ }
} elseif (null == $task) {
$ok = false;
$this->problem_display_item->setAlert($this->i18N->txt('frm_tsk_problem_not_found'));
@@ -166,7 +170,6 @@ public function checkInput(): bool
return $ok;
}
- $multi_problems_input = $this->getInput('xmum_multi_problems');
if (!empty($multi_problems_input) && !ilMumieTaskMultiUploadProcessor::isValid($multi_problems_input)) {
$ok = false;
$this->dropzone_item->setAlert($this->i18N->txt('frm_tsk_problems_not_found'));
diff --git a/classes/models/class.ilMumieTaskServerStructure.php b/classes/models/class.ilMumieTaskServerStructure.php
index f867979..3b05ba2 100644
--- a/classes/models/class.ilMumieTaskServerStructure.php
+++ b/classes/models/class.ilMumieTaskServerStructure.php
@@ -81,7 +81,7 @@ public function getCoursebyName($name)
{
foreach ($this->courses as $course) {
foreach ($course->getName() as $translation) {
- if ($translation->value == $name) {
+ if (0 === strcasecmp($translation->value, $name)) {
return $course;
}
}
diff --git a/classes/tasks/class.ilMumieTaskMultiUploadProcessor.php b/classes/tasks/class.ilMumieTaskMultiUploadProcessor.php
index d84ba58..df7200b 100644
--- a/classes/tasks/class.ilMumieTaskMultiUploadProcessor.php
+++ b/classes/tasks/class.ilMumieTaskMultiUploadProcessor.php
@@ -9,6 +9,8 @@
*/
class ilMumieTaskMultiUploadProcessor
{
+ private const MAX_TASKS_PER_UPLOAD = 50;
+
public static function process(ilObjMumieTask $base_task, string $tasks_json)
{
global $DIC;
@@ -25,6 +27,10 @@ public static function isValid(string $tasks_json): bool
try {
$task_dtos = self::parseTaskDTOs($tasks_json);
+ if (0 === count($task_dtos) || count($task_dtos) > self::MAX_TASKS_PER_UPLOAD) {
+ return false;
+ }
+
return !in_array(
false,
array_map(function ($task_dto) {
@@ -49,14 +55,16 @@ private static function parseTaskDTOs(string $tasks_json): array
private static function generateMumieTask(ilMumieTaskTaskDTO $task_dto, ilObjMumieTask $base_task)
{
$new_task = self::generateEmptyMumieTask($base_task->getParentRef(), $base_task->getType());
+ $server = ilMumieTaskServer::fromUrl($task_dto->getServer());
$new_task->setTitle($task_dto->getName());
- $new_task->setServer($task_dto->getServer());
+ $new_task->setServer($server->getUrlPrefix());
$new_task->setMumieCourse($task_dto->getCourse());
$new_task->setTaskurl($task_dto->getLink());
$new_task->setLanguage($task_dto->getLanguage());
$new_task->setLaunchcontainer($base_task->getLaunchcontainer());
$new_task->setMumieCoursefile($task_dto->getPathToCoursefile());
+ $new_task->setWorksheet($task_dto->getWorksheet());
$new_task->setDeadline($base_task->getDeadline());
$new_task->setTimelimit($base_task->getTimelimit());
$new_task->setOnline($base_task->getOnline());
@@ -78,9 +86,17 @@ private static function generateEmptyMumieTask($parent_ref, $type): ilObjMumieTa
private static function isValidProblem(ilMumieTaskTaskDTO $task_dto): bool
{
+ if (str_starts_with($task_dto->getLink(), ilObjMumieTask::WORKSHEET_PREFIX)) {
+ // Worksheets are not part of the course's regular task structure and can't be looked up there.
+ return true;
+ }
+
$server = ilMumieTaskServer::fromUrl($task_dto->getServer());
$server->buildStructure();
$course = $server->getCoursebyName($task_dto->getCourse());
+ if (null === $course) {
+ return false;
+ }
$task = $course->getTaskByLink($task_dto->getLink());
return !is_null($task);
diff --git a/classes/tasks/class.ilMumieTaskTaskDTO.php b/classes/tasks/class.ilMumieTaskTaskDTO.php
index 3aae47f..35ffd53 100644
--- a/classes/tasks/class.ilMumieTaskTaskDTO.php
+++ b/classes/tasks/class.ilMumieTaskTaskDTO.php
@@ -39,6 +39,10 @@ class ilMumieTaskTaskDTO
* @var string
*/
private $link;
+ /**
+ * @var object|null
+ */
+ private $worksheet;
public function __construct(string $task_json)
{
@@ -49,6 +53,7 @@ public function __construct(string $task_json)
$this->path_to_coursefile = $task->path_to_coursefile;
$this->language = $task->language;
$this->link = $task->link;
+ $this->worksheet = $task->worksheet ?? null;
}
public function getServer(): string
@@ -83,4 +88,9 @@ public function getLink()
{
return $this->link;
}
+
+ public function getWorksheet(): string
+ {
+ return null !== $this->worksheet ? json_encode($this->worksheet) : '';
+ }
}
diff --git a/js/ilMumieTaskDropzone.js b/js/ilMumieTaskDropzone.js
index 3e8d245..34bd5f4 100644
--- a/js/ilMumieTaskDropzone.js
+++ b/js/ilMumieTaskDropzone.js
@@ -16,7 +16,10 @@
})
dropzone.ondrop = (event) => {
dropzone.classList.remove(DRAG_OVER_CLASS);
- const taskJsonString = event.dataTransfer.getData("mumie/jsonArray");
+ applyTasksJson(event.dataTransfer.getData("mumie/jsonArray"));
+ }
+
+ function applyTasksJson(taskJsonString) {
multiProblemInputElem.setAttribute("value", taskJsonString);
problemListController.setData(taskJsonString);
}
@@ -62,6 +65,10 @@
}
}
})();
+
+ window.ilMumieTaskDropzone = {
+ setData: applyTasksJson,
+ };
})
}
)(jQuery)
\ No newline at end of file
diff --git a/js/ilMumieTaskForm.js b/js/ilMumieTaskForm.js
index 68e0bfd..45a9529 100644
--- a/js/ilMumieTaskForm.js
+++ b/js/ilMumieTaskForm.js
@@ -141,7 +141,23 @@
if (event.origin !== lmsSelectorUrl) {
return;
}
- const importObj = JSON.parse(event.data);
+ let importObj;
+ try {
+ importObj = JSON.parse(event.data);
+ } catch (error) {
+ sendFailure(error.message);
+ return;
+ }
+ if (Array.isArray(importObj)) {
+ try {
+ window.ilMumieTaskDropzone.setData(JSON.stringify(importObj.map(task => JSON.stringify(task))));
+ sendSuccess();
+ window.focus();
+ } catch (error) {
+ sendFailure(error.message);
+ }
+ return;
+ }
const worksheet = importObj.worksheet ?? null;
try {
langController.setLanguage(importObj.language);
@@ -206,11 +222,28 @@
multiProblemSelectorButton.onclick = function(e) {
e.preventDefault();
+ const selectedServerUrl = serverController.getSelectedServer().url_prefix;
+ const problemLang = langController.getSelectedLanguage();
+ const origin = window.location.origin;
+
+ if (shouldUseSSO(selectedServerUrl)) {
+ problemSelectorWindow = window.open(
+ problemSelectorSsoUrl
+ + '&serverUrl=' + encodeURIComponent(selectedServerUrl)
+ + '&problemLang=' + problemLang
+ + '&origin=' + encodeURIComponent(origin)
+ + '&multiSelect=true'
+ , '_blank',
+ 'toolbar=0,location=0,menubar=0'
+ );
+ return;
+ }
+
problemSelectorWindow = window.open(
lmsSelectorUrl
+ '/lms-problem-selector?'
+ "serverUrl="
- + encodeURIComponent(serverController.getSelectedServer().url_prefix),
+ + encodeURIComponent(selectedServerUrl),
"_blank",
'toolbar=0,location=0,menubar=0'
);
diff --git a/lang/ilias_de.lang b/lang/ilias_de.lang
index 29bfc8d..841c899 100644
--- a/lang/ilias_de.lang
+++ b/lang/ilias_de.lang
@@ -37,10 +37,10 @@ properties#:#Einstellungen
mumie_server#:#MUMIE-Server
mumie_server_desc#:#Bitte wählen sie einen MUMIE-Server um eine aktuelle Liste an verfügbaren Kursen und MumieTasks zu erhalten.
mumie_course#:#MUMIE-Kurs
-mumie_select_problem#:#Wählen Sie ein MUMIE-Problem
-mumie_problem#:#MUMIE-Problem
-mumie_problem_desc#:#Ein MUMIE-Problem ist eine benotete Übung auf der MUMIE-Plattform
-frm_tsk_problem_not_found#:#Dieses Problem konnte auf dem ausgewählten Server nicht gefunden werden.
+mumie_select_problem#:#Wählen Sie ein MUMIE-Problem oder Worksheet
+mumie_problem#:#MUMIE-Problem oder Worksheet
+mumie_problem_desc#:#Ein MUMIE-Problem oder Worksheet ist eine benotete Übung auf der MUMIE-Plattform
+frm_tsk_problem_not_found#:#Dieses Problem oder Worksheet konnte auf dem ausgewählten Server nicht gefunden werden.
launchcontainer#:#Start-Container
launchcontainer_desc#:#Bitte wählen Sie, ob diese Aktivität in einem neuen Browser-Tab geöffnet oder in die Ilias-Umgebung eingebettet werden soll.
window#:#Neues Fenster
@@ -121,12 +121,12 @@ btn_remove_grade_override#:#Auswahl zurücksetzen
grade_override_desc#:#Neue Abgaben werden automatisch in ILIAS übernommen, sofern die Abgabefrist noch nicht abgelaufen ist. Um eine andere Abgabe für die Bewertung zu verwenden, können Sie auf die entsprechende Schaltfläche in der Tabelle klicken.
Eine manuell ausgewählte Bewertung wird auch durch zukünftige Abgaben des Studierenden nicht ersetzt werden.
student_name#:#Name
deadline_extension_desc#:#Hier können Sie Studierenden individuelle Fristverlängerungen gewähren. Falls Sie bereits manuell eine andere Bewertung ausgewählt haben, hat diese Fristverlängerung keine Auswirkung.
-frm_multi_problem_header#:#Zusätzliche MUMIE-Probleme erzeugen
-form_drag_mt_here#:#Ziehe Sie die MUMIE-Problems hierhinein
-multi_create_success#:#Es wurden %s weitere MUMIE-Tasks über Drag&Drop erstellt!
-mumie_problems#:#MUMIE-Problems
+frm_multi_problem_header#:#Zusätzliche MUMIE-Probleme oder Worksheets erzeugen
+form_drag_mt_here#:#Ziehe Sie die MUMIE-Problems oder Worksheets hierhinein
+multi_create_success#:#Es wurden %s weitere MUMIE-Tasks erstellt!
+mumie_problems#:#MUMIE-Problems / Worksheets
open_dnd_prb_selector#:#Mehrfach-Aufgabenauswahl öffnen
-dnd_prb_selector_desc#:#Hier klicken, um die Mehrfach-Aufgabenauswahl zu öffnen. Wählen sie einfach die Aufgaben aus, die Sie importieren möchten, und ziehen sie diese in das untere Feld.
-frm_tsk_problems_not_found#:#Ein oder mehrere Problems konnten nicht auf dem Server gefunden werden
+dnd_prb_selector_desc#:#Hier klicken, um die Mehrfach-Aufgabenauswahl zu öffnen. Wählen Sie einfach die Aufgaben aus, die Sie importieren möchten.
+frm_tsk_problems_not_found#:#Ein oder mehrere der ausgewählten Elemente konnten nicht auf dem Server gefunden werden
dropzone_description#:#Sobald Sie auf Speichern klicken, wird Ilias automatisch MUMIE-Tasks für die ausgewählten Aufgaben erstellen. Dabei werden die Einstellungen für Start-Container, online und Abgabefrist von der derzeit ausgewählten MUMIE-Task übernommen.
-multi_problem_list_description#:#Sie haben die folgenden MUMIE-Problems ausgewählt
\ No newline at end of file
+multi_problem_list_description#:#Sie haben die folgenden MUMIE-Problems und Worksheets ausgewählt
\ No newline at end of file
diff --git a/lang/ilias_en.lang b/lang/ilias_en.lang
index f788373..667878a 100644
--- a/lang/ilias_en.lang
+++ b/lang/ilias_en.lang
@@ -37,10 +37,10 @@ properties#:#Settings
mumie_server#:#MUMIE server
mumie_server_desc#:#Please select a MUMIE server to get an updated list of available tasks and courses.
mumie_course#:#MUMIE course
-mumie_select_problem#:#Select a MUMIE Problem
-mumie_problem#:#MUMIE problem
-mumie_problem_desc#:#A MUMIE problem is a single graded exercise on MUMIE
-frm_tsk_problem_not_found#:#This problem doesn't exist in the selected course
+mumie_select_problem#:#Select a MUMIE Problem or Worksheet
+mumie_problem#:#MUMIE problem or worksheet
+mumie_problem_desc#:#A MUMIE problem or worksheet is a single graded exercise on MUMIE
+frm_tsk_problem_not_found#:#This problem or worksheet doesn't exist in the selected course
launchcontainer#:#Launch container
launchcontainer_desc#:#Please select whether the activity should be opened in a new browser tab or embedded into the ILIAS environment
window#:#New window
@@ -60,7 +60,7 @@ frm_sync_lp#:#Grade synchronization
frm_sync_lp_desc#:#Choose whether to automatically load grades from the MUMIE server into ILIAS for this MUMIE Task
frm_privategradepool#:#Share grades with other courses
frm_privategradepool_desc#:#Choose whether to share grades with other ILIAS repositories.
-
If sharing is enabled, points that were earned for MUMIE problems in other repositories will be automatically synchronized with this repository\'s gradebook.
+
If sharing is enabled, points that were earned for MUMIE problems or worksheets in other repositories will be automatically synchronized with this repository\'s gradebook.
If not, this repository will neither be able to import nor to export grades.
frm_privategradepool_decided#:#Note:
This decision was final and affects all other MUMIE Tasks in this course.
frm_privategradepool_undecided#:#Warning:
This decision is final and affects all other MUMIE Tasks in this course.
@@ -78,7 +78,7 @@ frm_online_info#:#Make this MUMIE Task visible and usable to all users with read
frm_online_disabled_warning#:#This MUMIE Task cannot be set online because a decision is still pending. Please go to grading settings.
frm_online_disabled_worksheet_warning#:#This worksheet cannot be set online until a deadline or time limit is set. Please go to grading settings.
open_prb_selector#:#Open problem selector
-open_prb_selector_desc#:#Click here to open a more detailed selection screen. You can filter all problems by category and enter search terms.
+open_prb_selector_desc#:#Click here to open a more detailed selection screen. You can filter all problems and worksheets by category and enter search terms.
frm_list_grade#:#Grade
frm_user_overview_list_grade_desc#:#
By default the newest submission before the deadline is used. If you want to use a different submission, you can click on the corresponding button to overwrite the current grade.
frm_user_overview_list_change_grade#:#Change Grade
@@ -121,12 +121,12 @@ btn_remove_grade_override#:#Reset selection
grade_override_desc#:#Grades are automatically updated in ILIAS to the latest submitted answer within the deadline. If you want to use a different submission, you can click on the corresponding button to overwrite the current grade.
A manually selected grade will not be replaced by new submissions.
student_name#:#Name
deadline_extension_desc#:#Here you can grant a deadline extension to the student. If you have already manually selected a grade for them, this setting will not have any effect.
-frm_multi_problem_header#:#Create additional MUMIE Problems
-form_drag_mt_here#:#Drag MUMIE Problems here
-multi_create_success#:#Successfully created %s additional MUMIE Tasks via drag & drop!
-mumie_problems#:#MUMIE Problems
+frm_multi_problem_header#:#Create additional MUMIE Problems or Worksheets
+form_drag_mt_here#:#Drag MUMIE Problems or Worksheets here
+multi_create_success#:#Successfully created %s additional MUMIE Tasks!
+mumie_problems#:#MUMIE Problems / Worksheets
open_dnd_prb_selector#:#Open multi problem selector
-dnd_prb_selector_desc#:#Clicking here will open the multi problem selection screen. Simply choose the problems you want to import and drag them in the field below.
-frm_tsk_problems_not_found#:#One or more problems could not be found.
-dropzone_description#:#Once you click Save, Ilias will automatically create new MUMIE-Tasks for the selected problems. The settings for Launch container, online and Deadline will be copied from this currently opened MUMIE Task.
-multi_problem_list_description#:#You have selected the following MUMIE Problems
\ No newline at end of file
+dnd_prb_selector_desc#:#Clicking here will open the multi problem selection screen. Simply choose the problems or worksheets you want to import.
+frm_tsk_problems_not_found#:#One or more of the selected items could not be found.
+dropzone_description#:#Once you click Save, Ilias will automatically create new MUMIE-Tasks for the selected problems and worksheets. The settings for Launch container, online and Deadline will be copied from this currently opened MUMIE Task.
+multi_problem_list_description#:#You have selected the following MUMIE Problems and Worksheets
\ No newline at end of file
diff --git a/templates/MumieTasks/tpl.file-drop-zone.html b/templates/MumieTasks/tpl.file-drop-zone.html
index c21fd95..ee73b32 100644
--- a/templates/MumieTasks/tpl.file-drop-zone.html
+++ b/templates/MumieTasks/tpl.file-drop-zone.html
@@ -1,8 +1,6 @@