ILIAS  release_8 Revision v8.24
class.ilExerciseManagementGUI.php
Go to the documentation of this file.
1<?php
2
25
37{
38 public const VIEW_ASSIGNMENT = 1;
39 public const VIEW_PARTICIPANT = 2;
40 public const VIEW_GRADES = 3;
41
42 public const FEEDBACK_ONLY_SUBMISSION = "submission_only";
43 public const FEEDBACK_FULL_SUBMISSION = "submission_feedback";
44
45 public const GRADE_NOT_GRADED = "notgraded";
46 public const GRADE_PASSED = "passed";
47 public const GRADE_FAILED = "failed";
48 protected \ILIAS\Repository\HTML\HTMLUtil $html_util;
49 protected \ILIAS\Exercise\InternalGUIService $gui;
50 protected \ILIAS\HTTP\Services $http;
51
52 protected ilCtrl $ctrl;
54 protected ilLanguage $lng;
59 protected array $filter = [];
62 protected ?ilExAssignment $assignment = null;
64 protected ilLogger $log;
65 protected ilObjUser $user;
67 protected ?ilDBInterface $db = null;
68 protected int $ass_id = 0;
69 protected int $requested_member_id = 0;
70 protected int $requested_part_id = 0;
71 protected int $requested_ass_id = 0;
72 protected string $requested_idl_id;
73 protected bool $done = false;
75 protected string $requested_comment;
76 protected string $requested_user_login;
77 protected array $selected_participants;
78 protected array $listed_participants;
79 protected array $selected_ass_ids;
80 protected array $listed_ass_ids;
81 protected array $requested_marks; // key might be ass_ids or user_ids!
82 protected array $requested_status; // key might be ass_ids or user_ids!
83 protected array $requested_tutor_notices; // key might be ass_ids or user_ids!
84 protected array $requested_group_members; // "grpt"
86 protected array $requested_files; // "file"
87 protected string $requested_filter_status;
90
97 public function __construct(InternalService $service, ilExAssignment $a_ass = null)
98 {
100 global $DIC;
101
102 $this->ui_factory = $DIC->ui()->factory();
103 $this->ui_renderer = $DIC->ui()->renderer();
104 $this->user = $DIC->user();
105 $this->toolbar = $DIC->toolbar();
106 $this->log = ilLoggerFactory::getLogger("exc");
107 $this->access = $DIC->access();
108
109 $this->ctrl = $DIC->ctrl();
110 $this->tabs_gui = $DIC->tabs();
111 $this->lng = $DIC->language();
112 $this->tpl = $DIC["tpl"];
113
114 $this->task_factory = $DIC->backgroundTasks()->taskFactory();
115
116 $this->request = $DIC->exercise()->internal()->gui()->request();
118
119 $this->service = $service;
120
121 $this->exercise = $request->getExercise();
122 if ($a_ass !== null) {
123 $this->assignment = $a_ass;
124 $this->ass_id = $this->assignment->getId();
125 }
126 $this->requested_member_id = $request->getMemberId();
127 $this->requested_part_id = $request->getParticipantId();
128 $this->requested_ass_id = $request->getAssId();
129 $this->requested_idl_id = $request->getIdlId();
130 $this->done = $request->getDone();
131 $this->requested_learning_comments = $request->getLearningComments();
132 $this->requested_comment = $request->getComment();
133 $this->requested_user_login = $request->getUserLogin();
134 $this->selected_participants = $request->getSelectedParticipants();
135 $this->listed_participants = $request->getListedParticipants();
136 $this->selected_ass_ids = $request->getSelectedAssignments();
137 $this->listed_ass_ids = $request->getListedAssignments();
138 $this->requested_marks = $request->getMarks();
139 $this->requested_status = $request->getStatus();
140 $this->requested_tutor_notices = $request->getTutorNotices();
141 $this->requested_group_members = $request->getGroupMembers();
142 $this->requested_files = $request->getFiles();
143 $this->requested_filter_status = $request->getFilterStatus();
144 $this->requested_filter_feedback = $request->getFilterFeedback();
145
146 $this->ctrl->saveParameter($this, array("vw", "member_id"));
147 $this->http = $DIC->http();
148 $this->ctrl->saveParameter($this, array("part_id"));
149 $this->gui = $DIC->exercise()->internal()->gui();
150 $this->html_util = $this->gui->html();
151 }
152
157 public function executeCommand(): void
158 {
159 $ilCtrl = $this->ctrl;
161 $ilTabs = $this->tabs_gui;
162
163 $class = $ilCtrl->getNextClass($this);
164 //$cmd = $ilCtrl->getCmd("listPublicSubmissions");
165
166 switch ($class) {
167 case "ilfilesystemgui":
168 $ilTabs->clearTargets();
169 $ilTabs->setBackTarget(
170 $lng->txt("back"),
171 $ilCtrl->getLinkTarget($this, $this->getViewBack())
172 );
173
174 $this->tpl->setOnScreenMessage('info', $lng->txt("exc_fb_tutor_info"));
175
176 $fstorage = new ilFSStorageExercise($this->exercise->getId(), $this->assignment->getId());
177 $fstorage->create();
178
179 $submission = new ilExSubmission($this->assignment, $this->requested_member_id);
180 $feedback_id = $submission->getFeedbackId();
181 $noti_rec_ids = $submission->getUserIds();
182
183 $fs_title = array();
184 foreach ($noti_rec_ids as $rec_id) {
185 $fs_title[] = ilUserUtil::getNamePresentation($rec_id, false, false, "", true);
186 }
187 $fs_title = implode(" / ", $fs_title);
188
189 $fs_gui = new ilFileSystemGUI($fstorage->getFeedbackPath($feedback_id));
190 $fs_gui->setTableId("excfbfil" . $this->assignment->getId() . "_" . $feedback_id);
191 $fs_gui->setAllowDirectories(false);
192 $fs_gui->setTitle($lng->txt("exc_fb_files") . " - " .
193 $this->assignment->getTitle() . " - " .
194 $fs_title);
195 $pcommand = $fs_gui->getLastPerformedCommand();
196 if (is_array($pcommand) && ($pcommand["cmd"] ?? "") == "create_file") {
197 foreach ($noti_rec_ids as $user_id) {
198 $member_status = $this->assignment->getMemberStatus($user_id);
199 $member_status->setFeedback(true);
200 $member_status->update();
201 }
202
203 $this->exercise->sendFeedbackFileNotification(
204 $pcommand["name"] ?? "",
205 $noti_rec_ids,
206 $this->assignment->getId()
207 );
208 }
209 $this->ctrl->forwardCommand($fs_gui);
210 break;
211
212 case 'ilrepositorysearchgui':
213 $rep_search = new ilRepositorySearchGUI();
214 $ref_id = $this->exercise->getRefId();
215 $rep_search->addUserAccessFilterCallable(function ($a_user_ids) use ($ref_id) {
216 return $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
217 'edit_submissions_grades',
218 'edit_submissions_grades',
219 $ref_id,
220 $a_user_ids
221 );
222 });
223 $rep_search->setTitle($this->lng->txt("exc_add_participant"));
224 $rep_search->setCallback($this, 'addMembersObject');
225
226 // Set tabs
227 $this->addSubTabs("assignment");
228 $this->ctrl->setReturn($this, 'members');
229
230 $this->ctrl->forwardCommand($rep_search);
231 break;
232
233 case "ilexsubmissionteamgui":
234 $gui = new ilExSubmissionTeamGUI($this->exercise, $this->initSubmission());
235 $ilCtrl->forwardCommand($gui);
236 break;
237
238 case "ilexsubmissionfilegui":
239 $gui = new ilExSubmissionFileGUI($this->exercise, $this->initSubmission());
240 $ilCtrl->forwardCommand($gui);
241 break;
242
243 case "ilexsubmissiontextgui":
244 $ilCtrl->saveParameter($this, array("part_id"));
245 $gui = new ilExSubmissionTextGUI($this->exercise, $this->initSubmission());
246 $ilCtrl->forwardCommand($gui);
247 break;
248
249 case "ilexpeerreviewgui":
250 $gui = new ilExPeerReviewGUI($this->assignment, $this->initSubmission());
251 $ilCtrl->forwardCommand($gui);
252 break;
253
254 default:
255 $cmd = $ilCtrl->getCmd();
256 switch ($cmd) {
257 case 'downloadSubmissions':
258 $cmd = $ilCtrl->getCmd("downloadSubmissions");
259 break;
260 default:
261 $cmd = $ilCtrl->getCmd("listPublicSubmissions");
262 break;
263 }
264 $this->{$cmd . "Object"}();
265 break;
266 }
267 }
268
269 protected function getViewBack(): string
270 {
271 switch ($this->request->getBackView()) {
273 $back_cmd = "showParticipant";
274 break;
275
277 $back_cmd = "showGradesOverview";
278 break;
279
280 default:
281 // case self::VIEW_ASSIGNMENT:
282 $back_cmd = "members";
283 break;
284 }
285 return $back_cmd;
286 }
287
288 protected function initSubmission(): ilExSubmission
289 {
290 $back_cmd = $this->getViewBack();
291 $this->ctrl->setReturn($this, $back_cmd);
292
293 $this->tabs_gui->clearTargets();
294 $this->tabs_gui->setBackTarget(
295 $this->lng->txt("back"),
296 $this->ctrl->getLinkTarget($this, $back_cmd)
297 );
298
299 return new ilExSubmission($this->assignment, $this->requested_member_id, null, true);
300 }
301
302 public function addSubTabs(string $a_activate): void
303 {
304 $ilTabs = $this->tabs_gui;
306 $ilCtrl = $this->ctrl;
307
308 $ass_id = $this->assignment !== null ? $this->assignment->getId() : 0;
309 $part_id = $this->requested_part_id;
310
311 $ilCtrl->setParameter($this, "vw", "");
312 $ilCtrl->setParameter($this, "member_id", "");
313 $ilCtrl->setParameter($this, "ass_id", "");
314 $ilCtrl->setParameter($this, "part_id", "");
315
316 $ilTabs->addSubTab(
317 "assignment",
318 $lng->txt("exc_assignment_view"),
319 $ilCtrl->getLinkTarget($this, "members")
320 );
321 $ilTabs->addSubTab(
322 "participant",
323 $lng->txt("exc_participant_view"),
324 $ilCtrl->getLinkTarget($this, "showParticipant")
325 );
326 $ilTabs->addSubTab(
327 "grades",
328 $lng->txt("exc_grades_overview"),
329 $ilCtrl->getLinkTarget($this, "showGradesOverview")
330 );
331 $ilTabs->activateSubTab($a_activate);
332
333 $ilCtrl->setParameter($this, "ass_id", $ass_id);
334 $ilCtrl->setParameter($this, "part_id", $part_id);
335 }
336
340 public function waitingDownloadObject(): void
341 {
343 $ilCtrl = $this->ctrl;
344
345 $ilCtrl->setParameterByClass("ilExSubmissionFileGUI", "member_id", $this->requested_member_id);
346 $url = $ilCtrl->getLinkTargetByClass(array("ilExerciseHandlerGUI", "ilObjExerciseGUI", "ilExerciseManagementGUI", "ilExSubmissionFileGUI"), "downloadNewReturned");
347 $js_url = $ilCtrl->getLinkTargetByClass(array("ilExerciseHandlerGUI", "ilObjExerciseGUI", "ilExerciseManagementGUI", "ilExSubmissionFileGUI"), "downloadNewReturned", "", "", false);
348 $this->tpl->setOnScreenMessage('info', $lng->txt("exc_wait_for_files") . "<a href='$url'> " . $lng->txt('exc_download_files') . "</a><script>window.location.href ='" . $js_url . "';</script>");
349 $this->membersObject();
350 }
351
356 public function membersObject(): void
357 {
359 $ilToolbar = $this->toolbar;
360 $ilCtrl = $this->ctrl;
362
363 $this->addSubTabs("assignment");
364
365 // assignment selection
366 $ass = ilExAssignment::getInstancesByExercise($this->exercise->getId());
367
368 if ($this->assignment === null && count($ass) > 0) {
369 $this->assignment = current($ass);
370 }
371
372 reset($ass);
373 if (count($ass) > 1) {
374 $options = array();
375 foreach ($ass as $a) {
376 $options[$a->getId()] = $a->getTitle();
377 }
378 $si = new ilSelectInputGUI($this->lng->txt("exc_assignment"), "ass_id");
379 $si->setOptions($options);
380 $si->setValue($this->assignment->getId());
381 $ilToolbar->addStickyItem($si, true);
382 $button = ilSubmitButton::getInstance();
383 $button->setCaption("select");
384 $button->setCommand("selectAssignment");
385 $ilToolbar->addStickyItem($button);
386
387 $ilToolbar->addSeparator();
388 }
389 // #16165 - if only 1 assignment dropdown is not displayed;
390 elseif ($this->assignment) {
391 $ilCtrl->setParameter($this, "ass_id", $this->assignment->getId());
392 }
393
394 // add member
395 // is only shown if 'edit_submissions_grades' is granted by rbac. positions
396 // access is not sufficient.
397 $has_rbac_access = $GLOBALS['DIC']->access()->checkAccess(
398 'edit_submissions_grades',
399 '',
400 $this->exercise->getRefId()
401 );
402 if ($has_rbac_access) {
404 $this,
405 $ilToolbar,
406 array(
407 'auto_complete_name' => $lng->txt('user'),
408 'submit_name' => $lng->txt('add'),
409 'add_search' => true,
410 'add_from_container' => $this->exercise->getRefId()
411 )
412 );
413 }
414
415 // #16168 - no assignments
416 if ($ass !== []) {
417 if ($has_rbac_access) {
418 $ilToolbar->addSeparator();
419 }
420
421 // we do not want the ilRepositorySearchGUI form action
422 $ilToolbar->setFormAction($ilCtrl->getFormAction($this));
423
424 $ilCtrl->setParameter($this, "ass_id", $this->assignment->getId());
425
426 if ($this->assignment->getType() == ilExAssignment::TYPE_UPLOAD_TEAM) {
427 if (ilExAssignmentTeam::getAdoptableGroups($this->exercise->getRefId())) {
428 $ilToolbar->addButton(
429 $this->lng->txt("exc_adopt_group_teams"),
430 $this->ctrl->getLinkTarget($this, "adoptTeamsFromGroup")
431 );
432
433 $ilToolbar->addSeparator();
434 }
435 } elseif ($this->exercise->hasTutorFeedbackFile()) {
436 if (!$this->assignment->getAssignmentType()->usesTeams()) {
437 // multi-feedback
438 $ilToolbar->addButton(
439 $this->lng->txt("exc_multi_feedback"),
440 $this->ctrl->getLinkTarget($this, "showMultiFeedback")
441 );
442
443 $ilToolbar->addSeparator();
444 }
445 }
446
447 $submission_repository = $this->service->repo()->submission();
448
449 if ($submission_repository->hasSubmissions($this->assignment->getId()) !== 0) {
450 $ass_type = $this->assignment->getType();
451 //todo change addFormButton for addButtonInstance
452 if ($ass_type == ilExAssignment::TYPE_TEXT) {
453 $ilToolbar->addFormButton($lng->txt("exc_list_text_assignment"), "listTextAssignment");
454 }
455 $ilToolbar->addFormButton($lng->txt("download_all_returned_files"), "downloadSubmissions");
456 }
457 $this->ctrl->setParameter($this, "vw", self::VIEW_ASSIGNMENT);
458
459 $exc_tab = new ilParticipantsPerAssignmentTableGUI($this, "members", $this->exercise, $this->assignment->getId());
461 $exc_tab->getHTML() .
462 $this->initIndividualDeadlineModal()
463 );
464 } else {
465 $this->tpl->setOnScreenMessage('info', $lng->txt("exc_no_assignments_available"));
466 }
467
468 $ilCtrl->setParameter($this, "ass_id", "");
469 }
470
471 public function downloadSubmissionsObject(): void
472 {
473 $participant_id = $this->requested_part_id;
474
475 $download_task = new ilDownloadSubmissionsBackgroundTask(
476 (int) $GLOBALS['DIC']->user()->getId(),
477 $this->exercise->getRefId(),
478 $this->exercise->getId(),
479 $this->ass_id,
480 $participant_id
481 );
482
483 if ($download_task->run()) {
484 $this->tpl->setOnScreenMessage('success', $this->lng->txt('exc_down_files_started_bg'), true);
485 }
486
487 if ($this->assignment !== null) {
488 $this->ctrl->redirect($this, "members");
489 } else {
490 $this->ctrl->redirect($this, "showParticipant");
491 }
492 }
493
497 public function membersApplyObject(): void
498 {
499 $this->saveStatusAllObject(null, false);
500 $exc_tab = new ilParticipantsPerAssignmentTableGUI($this, "members", $this->exercise, $this->assignment->getId());
501 $exc_tab->resetOffset();
502 $exc_tab->writeFilterToSession();
503
504 $this->membersObject();
505 }
506
510 public function membersResetObject(): void
511 {
512 $exc_tab = new ilParticipantsPerAssignmentTableGUI($this, "members", $this->exercise, $this->assignment->getId());
513 $exc_tab->resetOffset();
514 $exc_tab->resetFilter();
515
516 $this->membersObject();
517 }
518
519 public function saveGradesObject(): void
520 {
521 $ilCtrl = $this->ctrl;
523
524 foreach ($this->requested_learning_comments as $k => $v) {
525 $marks_obj = new ilLPMarks($this->exercise->getId(), (int) $k);
526 $marks_obj->setComment($this->html_util->strip($v));
527 $marks_obj->update();
528 }
529 foreach ($this->requested_marks as $k => $v) {
530 $marks_obj = new ilLPMarks($this->exercise->getId(), (int) $k);
531 $marks_obj->setMark($this->html_util->strip($v));
532 $marks_obj->update();
533 }
534 $this->tpl->setOnScreenMessage('success', $lng->txt("exc_msg_saved_grades"), true);
535 $ilCtrl->redirect($this, "showGradesOverview");
536 }
537
538
539 // TEXT ASSIGNMENT ?!
540
545 public function listTextAssignmentObject(): void
546 {
547 $this->initFilter();
548 $this->setBackToMembers();
549
551 $button_print = $this->ui_factory->button()->standard($this->lng->txt('print'), "#")
552 ->withOnLoadCode(function ($id) {
553 return "$('#$id').click(function() { window.print(); return false; });";
554 });
555 $this->toolbar->addSeparator();
556 $this->toolbar->addComponent($button_print);
557
558 $group_panels_tpl = new ilTemplate("tpl.exc_group_report_panels.html", true, true, "Modules/Exercise");
559 $group_panels_tpl->setVariable('TITLE', $this->lng->txt("exc_list_text_assignment") . ": " . $this->assignment->getTitle());
560
561 $report_html = "";
562 $total_reports = 0;
563
564 $members = ilExSubmission::getAssignmentParticipants($this->exercise->getId(), $this->ass_id);
565 $members_filter = new ilExerciseMembersFilter($this->exercise->getRefId(), $members, $this->user->getId());
566 $members = $members_filter->filterParticipantsByAccess();
567
568 foreach (ilExSubmission::getAssignmentFilesByUsers($this->exercise->getId(), $this->assignment->getId(), $members) as $file) {
569 if (trim($file["atext"]) && ilObjUser::_exists($file["user_id"])) {
570 $feedback_data = $this->collectFeedbackDataFromPeer($file);
571 $submission_data = $this->assignment->getExerciseMemberAssignmentData((int) $file["user_id"], $this->filter["status"] ?? "");
572
573 if (is_array($submission_data)) {
574 $data = array_merge($feedback_data, $submission_data);
575 $report_html .= $this->getReportPanel($data);
576 $total_reports++;
577 }
578 }
579 }
580 if ($total_reports == 0) {
581 $mess = $this->ui_factory->messageBox()->info($this->lng->txt("fiter_no_results"));
582 $report_html .= $this->ui_renderer->render($mess);
583 }
584
585 $group_panels_tpl->setVariable('CONTENT', $report_html);
586 $this->tpl->setContent($group_panels_tpl->get());
587 }
588
594 public function compareTextAssignmentsObject(): void
595 {
596 $this->setBackToMembers();
597
598 $group_panels_tpl = new ilTemplate("tpl.exc_group_report_panels.html", true, true, "Modules/Exercise");
599 $group_panels_tpl->setVariable('TITLE', $this->lng->txt("exc_compare_selected_submissions"));
600
601 $report_html = "";
602 //participant ids selected via checkboxes
603 $participants = array_keys($this->getMultiActionUserIds());
604
605 $total_reports = 0;
606 foreach ($participants as $participant_id) {
607 $submission = new ilExSubmission($this->assignment, $participant_id);
608
609 //submission data array
610 $files = $submission->getFiles();
611 $file = reset($files);
612
613 if (!$file) {
614 $file = [
615 "user_id" => $participant_id,
616 "ts" => null,
617 "atext" => null
618 ];
619 }
620
621 $feedback_data = $this->collectFeedbackDataFromPeer($file);
622
623 $submission_data = $this->assignment->getExerciseMemberAssignmentData((int) $file["user_id"], $this->filter["status"] ?? "");
624
625 if (is_array($submission_data)) {
626 $data = array_merge($feedback_data, $submission_data);
627 $report_html .= $this->getReportPanel($data);
628 $total_reports++;
629 }
630 }
631
632 $group_panels_tpl->setVariable('CONTENT', $report_html);
633 $this->tpl->setContent($group_panels_tpl->get());
634 }
635
639 public function getReportPanel(array $a_data): string
640 {
641 $modal = $this->getEvaluationModal($a_data);
642
643 $this->ctrl->setParameter($this, "member_id", $a_data['uid']);
644 $actions = array(
645 $this->ui_factory->button()->shy($this->lng->txt("grade_evaluate"), "#")->withOnClick($modal->getShowSignal())
646 );
647
648 if ($this->exercise->hasTutorFeedbackMail()) {
649 $actions[] = $this->ui_factory->button()->shy(
650 $this->lng->txt("exc_tbl_action_feedback_mail"),
651 $this->ctrl->getLinkTarget($this, "redirectFeedbackMail")
652 );
653 }
654 if ($this->exercise->hasTutorFeedbackFile()) {
655 $actions[] = $this->ui_factory->button()->shy(
656 $this->lng->txt("exc_tbl_action_feedback_file"),
657 $this->ctrl->getLinkTargetByClass("ilFileSystemGUI", "listFiles")
658 );
659 }
660
661 $this->ctrl->setParameter($this, "member_id", "");
662
663 $actions_dropdown = $this->ui_factory->dropdown()->standard($actions);
664 if ($a_data['status'] == self::GRADE_NOT_GRADED) {
665 $str_status_key = $this->lng->txt('exc_tbl_status');
666 $str_status_value = $this->lng->txt('not_yet');
667 } else {
668 $str_status_key = $this->lng->txt('exc_tbl_status_time');
669 $str_status_value = ilDatePresentation::formatDate(new ilDateTime($a_data["status_time"], IL_CAL_DATETIME));
670 }
671
672 $str_mark_key = $this->lng->txt("exc_tbl_mark");
673 $str_mark_value = $this->lng->txt('not_yet');
674
675 if (($a_data['mark'] != "")) {
676 $str_mark_value = $a_data['mark'];
677 }
678
679 if ($a_data['feedback_time']) {
680 $str_evaluation_key = $this->lng->txt('exc_tbl_feedback_time');
681 $str_evaluation_value = ilDatePresentation::formatDate(new ilDateTime($a_data["feedback_time"], IL_CAL_DATETIME));
682 } else {
683 $str_evaluation_key = $this->lng->txt('exc_settings_feedback');
684 $str_evaluation_value = $this->lng->txt('not_yet');
685 }
686
687 $card_content = array(
688 $this->lng->txt("exc_tbl_submission_date") => ilDatePresentation::formatDate(new ilDateTime($a_data["udate"], IL_CAL_DATETIME)),
689 $str_status_key => $str_status_value,
690 $str_mark_key => $str_mark_value,
691 $str_evaluation_key => $str_evaluation_value,
692 $this->lng->txt('feedback_given') => $a_data['fb_given'],
693 $this->lng->txt('feedback_received') => $a_data['fb_received']
694 );
695 $card_tpl = new ilTemplate("tpl.exc_report_details_card.html", true, true, "Modules/Exercise");
696 foreach ($card_content as $key => $value) {
697 $card_tpl->setCurrentBlock("assingment_card");
698 $card_tpl->setVariable("ROW_KEY", $key);
699 $card_tpl->setVariable("ROW_VALUE", $value);
700 $card_tpl->parseCurrentBlock();
701 }
702
703 $main_panel = $this->ui_factory->panel()->sub($a_data['uname'], $this->ui_factory->legacy($a_data['utext']))
704 ->withFurtherInformation($this->ui_factory->card()->standard($this->lng->txt('text_assignment'))->withSections(array($this->ui_factory->legacy($card_tpl->get()))))->withActions($actions_dropdown);
705
706 $feedback_tpl = new ilTemplate("tpl.exc_report_feedback.html", true, true, "Modules/Exercise");
707 //if no feedback filter the feedback is displayed. Can be list submissions or compare submissions.
708 $filter_feedback = $this->filter["feedback"] ?? "";
709 if (array_key_exists("peer", $a_data) && (($filter_feedback == self::FEEDBACK_FULL_SUBMISSION) || $filter_feedback == "")) {
710 $feedback_tpl->setCurrentBlock("feedback");
711 foreach ($a_data["peer"] as $peer_id) {
712 if (ilObject::_lookupType($peer_id) == "usr") {
713 $user = new ilObjUser($peer_id);
714 $peer_name = $user->getFirstname() . " " . $user->getLastname();
715 } else {
716 $peer_name = $this->lng->txt("exc_deleted_user");
717 }
718
719 $feedback_tpl->setCurrentBlock("peer_feedback");
720 $feedback_tpl->setVariable("PEER_NAME", $peer_name);
721
722 $submission = new ilExSubmission($this->assignment, $a_data["uid"]);
723 $values = $submission->getPeerReview()->getPeerReviewValues($peer_id, $a_data["uid"]);
724
725 $review_html = "";
726 foreach ($this->assignment->getPeerReviewCriteriaCatalogueItems() as $crit) {
727 $crit_id = $crit->getId()
728 ? $crit->getId()
729 : $crit->getType();
730 $crit->setPeerReviewContext($this->assignment, $peer_id, $a_data["uid"]);
731
732 $review_html .=
733 '<div class="ilBlockPropertyCaption">' . $crit->getTitle() . '</div>' .
734 '<div style="margin:2px 0;">' . $crit->getHTML($values[$crit_id] ?? null) . '</div>';
735 }
736 $feedback_tpl->setVariable("PEER_FEEDBACK", $review_html);
737 $feedback_tpl->parseCurrentBlock();
738 }
739 $feedback_tpl->parseCurrentBlock();
740 }
741 $feedback_tpl->setVariable("GRADE", $this->lng->txt('grade') . ": " . $this->lng->txt('exc_' . $a_data['status']));
742 $feedback_tpl->setVariable("COMMENT", $this->lng->txt('exc_comment') . "<br>" . $a_data['comment']);
743
744 $feedback_panel = $this->ui_factory->panel()->sub("", $this->ui_factory->legacy($feedback_tpl->get()));
745
746 $report = $this->ui_factory->panel()->report("", array($main_panel, $feedback_panel));
747
748 return $this->ui_renderer->render([$modal,$report]);
749 }
750
751 public function getEvaluationModal(
752 array $a_data
753 ): RoundTrip {
754 $modal_tpl = new ilTemplate("tpl.exc_report_evaluation_modal.html", true, true, "Modules/Exercise");
755 $modal_tpl->setVariable("USER_NAME", $a_data['uname']);
756
757 $form = $this->getEvaluationModalForm($a_data);
758 //TODO: CHECK ilias string utils. ilUtil shortenText with net blank.
759 if ($this->exercise->hasTutorFeedbackText()) {
760 $max_chars = 500;
761
762 $u_text = strip_tags($a_data["utext"]); //otherwise will get open P
763 $text = $u_text;
764 //show more
765 if (strlen($u_text) > $max_chars) {
766 $text = "<input type='checkbox' class='read-more-state' id='post-1' />";
767 $text .= "<div class='read-more-wrap'>";
768 $text .= mb_substr($u_text, 0, $max_chars);
769 $text .= "<span class='read-more-target'>";
770 $text .= mb_substr($u_text, $max_chars);
771 $text .= "</span></div>";
772 $text .= "<label for='post-1' class='read-more-trigger'></label>";
773 }
774 $modal_tpl->setVariable("USER_TEXT", $text);
775 }
776
777 $modal_tpl->setVariable("FORM", $form->getHTML());
778
779 $form_id = 'form_' . $form->getId();
780 $submit_btn = $this->ui_factory->button()->primary($this->lng->txt("save"), '#')
781 ->withOnLoadCode(function ($id) use ($form_id) {
782 return "$('#$id').click(function() { $('#$form_id').submit(); return false; });";
783 });
784
785 return $this->ui_factory->modal()->roundtrip(strtoupper($this->lng->txt("grade_evaluate")), $this->ui_factory->legacy($modal_tpl->get()))->withActionButtons([$submit_btn]);
786 }
787
788 public function getEvaluationModalForm(
789 array $a_data
791 $form = new ilPropertyFormGUI();
792 $form->setFormAction($this->ctrl->getFormAction($this, "saveEvaluationFromModal"));
793 $form->setId(uniqid('form'));
794
795 //Grade
796 $options = array(
797 self::GRADE_NOT_GRADED => $this->lng->txt("exc_notgraded"),
798 self::GRADE_PASSED => $this->lng->txt("exc_passed"),
799 self::GRADE_FAILED => $this->lng->txt("exc_failed")
800 );
801 $si = new ilSelectInputGUI($this->lng->txt("exc_tbl_status"), "grade");
802 $si->setOptions($options);
803 $si->setValue($a_data['status'] ?? "");
804 $form->addItem($si);
805
806 //Mark
807 $mark_input = new ilTextInputGUI($this->lng->txt("exc_tbl_mark"), "mark");
808 $mark_input->setValue($a_data['mark'] ?? "");
809 $mark_input->setMaxLength(32);
810 $mark_input->setSize(4);
811 $form->addItem($mark_input);
812
813 $item = new ilHiddenInputGUI('mem_id');
814 $item->setValue($a_data['uid'] ?? "");
815 $form->addItem($item);
816
817 //TODO: CHECK ilias string utils. ilUtil shortenText with net blank.
818 if ($this->exercise->hasTutorFeedbackText()) {
819 $ta = new ilTextAreaInputGUI($this->lng->txt("exc_comment"), 'comment');
820 $ta->setInfo($this->lng->txt("exc_comment_for_learner_info"));
821 $ta->setValue($a_data['comment'] ?? "");
822 $ta->setRows(10);
823 $form->addItem($ta);
824 }
825 return $form;
826 }
827
828 // Save assignment submission grade(status) and comment from the roundtrip modal.
829
833 public function saveEvaluationFromModalObject(): void
834 {
835 $form = $this->getEvaluationModalForm([]);
836 $user_id = 0;
837 $comment = "";
838 $mark = "";
839 $grade = "";
840 if ($form->checkInput()) {
841 $comment = trim($form->getInput('comment'));
842 $user_id = (int) $form->getInput('mem_id');
843 $grade = trim($form->getInput('grade'));
844 $mark = trim($form->getInput('mark'));
845 }
846
847 if ($this->assignment->getId() && $user_id > 0) {
848 $member_status = $this->assignment->getMemberStatus($user_id);
849 $member_status->setComment(ilUtil::stripSlashes($comment));
850 if ($grade != "") {
851 $member_status->setStatus($grade);
852 }
853 $member_status->setMark($mark);
854 if ($comment != "") {
855 $member_status->setFeedback(true);
856 }
857 $member_status->update();
858 }
859 $this->tpl->setOnScreenMessage('success', $this->lng->txt("exc_status_saved"), true);
860 $this->ctrl->redirect($this, "listTextAssignment");
861 }
862
863 // Add user as member
864
868 public function addUserFromAutoCompleteObject(): void
869 {
870 if ($this->requested_user_login == "") {
871 $this->tpl->setOnScreenMessage('failure', $this->lng->txt('msg_no_search_string'));
872 $this->membersObject();
873 return;
874 }
875 $users = explode(',', $this->requested_user_login);
876
877 $user_ids = array();
878 foreach ($users as $user) {
879 $user_id = ilObjUser::_lookupId($user);
880
881 if (!$user_id) {
882 $this->tpl->setOnScreenMessage('failure', $this->lng->txt('user_not_known'));
883 $this->membersObject();
884 return;
885 }
886
887 $user_ids[] = $user_id;
888 }
889
890 $this->addMembersObject($user_ids);
891 }
892
893 // Add new partipant
894 public function addMembersObject($a_user_ids = array()): void
895 {
896 if (!count($a_user_ids)) {
897 $this->tpl->setOnScreenMessage('failure', $this->lng->txt("no_checkbox"), true);
898 } else {
899 if (!$this->exercise->members_obj->assignMembers($a_user_ids)) {
900 $this->tpl->setOnScreenMessage('failure', $this->lng->txt("exc_members_already_assigned"), true);
901 } else {
902 $this->tpl->setOnScreenMessage('success', $this->lng->txt("exc_members_assigned"), true);
903 }
904 }
905 $this->ctrl->redirect($this, "members");
906 }
907
911 public function selectAssignmentObject(): void
912 {
913 $ctrl = $this->ctrl;
914 $ctrl->setParameter($this, "ass_id", $this->requested_ass_id);
915 $ctrl->redirect($this, "members");
916 }
917
921 public function showParticipantObject(): void
922 {
924 $ilToolbar = $this->toolbar;
925 $ilCtrl = $this->ctrl;
927 $access = $this->access;
928
929 $this->addSubTabs("participant");
930 $this->ctrl->setParameter($this, "ass_id", "");
931
932 // participant selection
933 $members = $this->exercise->members_obj->getMembers();
934
936 'edit_submissions_grades',
937 'edit_submissions_grades',
938 $this->exercise->getRefId(),
939 $members
940 );
941
942
943 if (count($members) == 0) {
944 $this->tpl->setOnScreenMessage('info', $lng->txt("exc_no_participants"));
945 return;
946 }
947
948 $mems = array();
949 foreach ($members as $mem_id) {
950 if (ilObject::_lookupType($mem_id) == "usr") {
951 $name = ilObjUser::_lookupName($mem_id);
952 if (trim($name["login"]) != "") { // #20073
953 $mems[$mem_id] = $name;
954 }
955 }
956 }
957
958 $mems = ilArrayUtil::sortArray($mems, "lastname", "asc", false, true);
959
960 if ($this->requested_part_id == 0 && $mems !== [] && key($mems) > 0) {
961 $ilCtrl->setParameter($this, "part_id", key($mems));
962 $ilCtrl->redirect($this, "showParticipant");
963 }
964
965 $current_participant = $this->requested_part_id;
966
967 reset($mems);
968 if (count($mems) > 1) {
969 $options = array();
970 foreach ($mems as $k => $m) {
971 $options[$k] =
972 $m["lastname"] . ", " . $m["firstname"] . " [" . $m["login"] . "]";
973 }
974 $si = new ilSelectInputGUI($this->lng->txt("exc_participant"), "part_id");
975 $si->setOptions($options);
976 $si->setValue($current_participant);
977 $ilToolbar->addStickyItem($si, true);
978 $button = ilSubmitButton::getInstance();
979 $button->setCaption("select");
980 $button->setCommand("selectParticipant");
981 $ilToolbar->addStickyItem($button);
982 }
983
984 if ($mems !== []) {
985 $this->ctrl->setParameter($this, "vw", self::VIEW_PARTICIPANT);
986 $this->ctrl->setParameter($this, "part_id", $current_participant);
987
988 $ilToolbar->addSeparator();
989 $ilToolbar->setFormAction($ilCtrl->getFormAction($this));
990 $ilToolbar->addFormButton($lng->txt("download_all_returned_files"), "downloadSubmissions");
991
993 $this,
994 "showParticipant",
995 $this->exercise,
996 $current_participant
997 );
998 $tpl->setContent($part_tab->getHTML() .
999 $this->initIndividualDeadlineModal());
1000 } else {
1001 $this->tpl->setOnScreenMessage('info', $this->lng->txt("exc_no_assignments_available"));
1002 }
1003 }
1004
1007 public function showParticipantApplyObject(): void
1008 {
1009 $exc_tab = new ilAssignmentsPerParticipantTableGUI($this, "showParticipant", $this->exercise, $this->requested_part_id);
1010 $exc_tab->resetOffset();
1011 $exc_tab->writeFilterToSession();
1012
1013 $this->showParticipantObject();
1014 }
1015
1018 public function showParticipantResetObject(): void
1019 {
1020 $exc_tab = new ilAssignmentsPerParticipantTableGUI($this, "showParticipant", $this->exercise, $this->requested_part_id);
1021 $exc_tab->resetOffset();
1022 $exc_tab->resetFilter();
1023
1024 $this->showParticipantObject();
1025 }
1026
1030 public function selectParticipantObject(): void
1031 {
1032 $ctrl = $this->ctrl;
1033 $ctrl->setParameter($this, "part_id", $this->requested_part_id);
1034 $ctrl->redirect($this, "showParticipant");
1035 }
1036
1037 public function showGradesOverviewObject(): void
1038 {
1039 $tpl = $this->tpl;
1040 $ilToolbar = $this->toolbar;
1041 $ilCtrl = $this->ctrl;
1042 $lng = $this->lng;
1043
1044 $this->addSubTabs("grades");
1045
1046 $mem_obj = new ilExerciseMembers($this->exercise);
1047 $mems = $mem_obj->getMembers();
1048
1049 $mems = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
1050 'edit_submissions_grades',
1051 'edit_submissions_grades',
1052 $this->exercise->getRefId(),
1053 $mems
1054 );
1055 if (count($mems) > 0) {
1056 $ilToolbar->addButton(
1057 $lng->txt("exc_export_excel"),
1058 $ilCtrl->getLinkTarget($this, "exportExcel")
1059 );
1060 }
1061
1062 $this->ctrl->setParameter($this, "vw", self::VIEW_GRADES);
1063
1064 $grades_tab = new ilExGradesTableGUI(
1065 $this,
1066 "showGradesOverview",
1067 $this->service,
1068 $mem_obj
1069 );
1070 $tpl->setContent($grades_tab->getHTML());
1071 }
1072
1076 public function redirectFeedbackMailObject(): void
1077 {
1078 if ($this->requested_member_id > 0) {
1079 $submission = new ilExSubmission($this->assignment, $this->requested_member_id);
1080 $members = $submission->getUserIds();
1081 } elseif ($members = $this->getMultiActionUserIds()) {
1082 $members = array_keys($members);
1083 }
1084
1085 if ($members !== []) {
1086 $logins = array();
1087 foreach ($members as $user_id) {
1088 $member_status = $this->assignment->getMemberStatus($user_id);
1089 $member_status->setFeedback(true);
1090 $member_status->update();
1091
1092 $logins[] = ilObjUser::_lookupLogin($user_id);
1093 }
1094 $logins = implode(",", $logins);
1095
1096 // #16530 - see ilObjCourseGUI::createMailSignature
1097 $sig = chr(13) . chr(10) . chr(13) . chr(10);
1098 $sig .= $this->lng->txt('exc_mail_permanent_link');
1099 $sig .= chr(13) . chr(10) . chr(13) . chr(10);
1100 $sig .= ilLink::_getLink($this->exercise->getRefId());
1101 $sig = rawurlencode(base64_encode($sig));
1102
1104 $this,
1105 $this->getViewBack(),
1106 array(),
1107 array(
1108 'type' => 'new',
1109 'rcp_to' => $logins,
1111 )
1112 ));
1113 }
1114 }
1115
1116 // Download all submitted files (of all members).
1117
1123 public function downloadAllObject(): void
1124 {
1125 $members = array();
1126
1127 foreach ($this->exercise->members_obj->getMembers() as $member_id) {
1128 $submission = new ilExSubmission($this->assignment, $member_id);
1129 $submission->updateTutorDownloadTime();
1130
1131 // get member object (ilObjUser)
1132 if (ilObject::_exists($member_id)) {
1133 $storage_id = "";
1134 // adding file metadata
1135 foreach ($submission->getFiles() as $file) {
1136 if ($this->assignment->getAssignmentType()->isSubmissionAssignedToTeam()) {
1137 $storage_id = $file["team_id"];
1138 } else {
1139 $storage_id = $file["user_id"];
1140 }
1141
1142 $members[$storage_id]["files"][$file["returned_id"]] = $file;
1143 }
1144 if ($this->assignment->getAssignmentType()->isSubmissionAssignedToTeam()) {
1145 $name = "Team " . $submission->getTeam()->getId();
1146 } else {
1148 $tmp_obj = ilObjectFactory::getInstanceByObjId($member_id);
1149 $name = $tmp_obj->getFirstname() . " " . $tmp_obj->getLastname();
1150 }
1151 if ($storage_id > 0) {
1152 $members[$storage_id]["name"] = $name;
1153 }
1154 unset($tmp_obj);
1155 }
1156 }
1157
1158 ilExSubmission::downloadAllAssignmentFiles($this->assignment, $members, "");
1159 }
1160
1164 protected function getMultiActionUserIds(bool $a_keep_teams = false): array
1165 {
1166 $members = [];
1167 // multi-user
1168 if ($this->assignment !== null) {
1169 if (count($this->selected_participants) == 0) {
1170 $this->tpl->setOnScreenMessage('failure', $this->lng->txt("no_checkbox"), true);
1171 $this->ctrl->redirect($this, "members");
1172 }
1173
1174 foreach ($this->selected_participants as $user_id) {
1175 $submission = new ilExSubmission($this->assignment, $user_id);
1176 $tmembers = $submission->getUserIds();
1177 if (!$a_keep_teams) {
1178 foreach ($tmembers as $tuser_id) {
1179 $members[$tuser_id] = 1;
1180 }
1181 } else {
1182 if ($tmembers) {
1183 $members[] = $tmembers;
1184 } else {
1185 // no team yet
1186 $members[] = $user_id;
1187 }
1188 }
1189 }
1190 }
1191 // multi-ass
1192 else {
1193 if (count($this->selected_ass_ids) == 0) {
1194 $this->tpl->setOnScreenMessage('failure', $this->lng->txt("no_checkbox"), true);
1195 $this->ctrl->redirect($this, "showParticipant");
1196 }
1197
1198 $user_id = $this->requested_part_id;
1199
1200 foreach ($this->selected_ass_ids as $ass_id) {
1201 $submission = new ilExSubmission(new ilExAssignment($ass_id), $user_id);
1202 $tmembers = $submission->getUserIds();
1203 if (!$a_keep_teams) {
1204 foreach ($tmembers as $tuser_id) {
1205 $members[$ass_id][] = $tuser_id;
1206 }
1207 } else {
1208 if ($tmembers) {
1209 $members[$ass_id][] = $tmembers;
1210 } else {
1211 // no team yet
1212 $members[$ass_id][] = $user_id;
1213 }
1214 }
1215 }
1216 }
1217
1218 return $members;
1219 }
1220
1221 // Send assignment per mail to participants
1222
1226 public function sendMembersObject(): void
1227 {
1228 $members = $this->getMultiActionUserIds();
1229
1230 $this->tpl->setOnScreenMessage('success', $this->lng->txt("exc_sent"), true);
1231 if ($this->assignment !== null) {
1232 $this->exercise->sendAssignment($this->assignment, array_keys($members));
1233 $this->ctrl->redirect($this, "members");
1234 } else {
1235 foreach ($members as $ass_id => $users) {
1236 $this->exercise->sendAssignment(new ilExAssignment($ass_id), $users);
1237 }
1238 $this->ctrl->setParameter($this, "part_id", $this->requested_part_id); // #17629
1239 $this->ctrl->redirect($this, "showParticipant");
1240 }
1241 }
1242
1246 public function confirmDeassignMembersObject(): void
1247 {
1248 $ilCtrl = $this->ctrl;
1249 $tpl = $this->tpl;
1250 $lng = $this->lng;
1251
1252 $members = $this->getMultiActionUserIds();
1253
1254 $cgui = new ilConfirmationGUI();
1255 $cgui->setFormAction($ilCtrl->getFormAction($this));
1256 $cgui->setHeaderText($lng->txt("exc_msg_sure_to_deassign_participant"));
1257 $cgui->setCancel($lng->txt("cancel"), "members");
1258 $cgui->setConfirm($lng->txt("remove"), "deassignMembers");
1259 foreach ($members as $k => $m) {
1260 $cgui->addItem(
1261 "member_ids[]",
1262 $k,
1263 ilUserUtil::getNamePresentation((int) $k, false, false, "", true)
1264 );
1265 }
1266
1267 $tpl->setContent($cgui->getHTML());
1268 }
1269
1270 // Deassign members from exercise
1271
1275 public function deassignMembersObject(): void
1276 {
1277 $ilCtrl = $this->ctrl;
1278 $lng = $this->lng;
1279
1280 $member_ids = $this->request->getMemberIds();
1281
1282 foreach ($member_ids as $usr_id) {
1283 $this->exercise->members_obj->deassignMember((int) $usr_id);
1284 $this->removeUserSubmissionFilesFromWebDir((int) $usr_id);
1285 }
1286 $this->tpl->setOnScreenMessage('success', $lng->txt("exc_msg_participants_removed"), true);
1287 $ilCtrl->redirect($this, "members");
1288 }
1289
1290 public function removeUserSubmissionFilesFromWebDir(int $user_id): void
1291 {
1292 $storage = new ilFSWebStorageExercise($this->exercise->getId(), $this->ass_id);
1293 $storage->deleteUserSubmissionDirectory($user_id);
1294 }
1295
1300 public function saveStatusParticipantObject(array $selected_ass_ids = null): void
1301 {
1302 $ilCtrl = $this->ctrl;
1303
1304 $member_id = $this->requested_part_id;
1305 $data = array();
1306 $marks = $this->requested_marks;
1307 $status = $this->requested_status;
1308 $notices = $this->requested_tutor_notices;
1309 foreach ($this->listed_ass_ids as $ass_id) {
1310 if (is_array($selected_ass_ids) &&
1311 !in_array($ass_id, $selected_ass_ids)) {
1312 continue;
1313 }
1314
1315 $data[$ass_id][$member_id] = array(
1316 "status" => $status[$ass_id]
1317 );
1318 if (isset($marks[$ass_id])) {
1319 $data[$ass_id][$member_id]["mark"] = $marks[$ass_id];
1320 }
1321 if (isset($notices[$ass_id])) {
1322 $data[$ass_id][$member_id]["notice"] = $notices[$ass_id];
1323 }
1324 }
1325
1326 $ilCtrl->setParameter($this, "part_id", $member_id); // #17629
1327 $this->saveStatus($data);
1328 }
1329
1333 public function saveStatusAllObject(
1334 array $a_selected = null,
1335 bool $a_redirect = true
1336 ): void {
1337 $user_ids = $this->listed_participants;
1338 $marks = $this->requested_marks;
1339 $notices = $this->requested_tutor_notices;
1340 $status = $this->requested_status;
1341 $filtered_user_ids = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
1342 'edit_submissions_grades',
1343 'edit_submissions_grades',
1344 $this->exercise->getRefId(),
1345 $user_ids
1346 );
1347
1348 $data = array();
1349 foreach ($filtered_user_ids as $user_id) {
1350 if (is_array($a_selected) &&
1351 !in_array($user_id, $a_selected)) {
1352 continue;
1353 }
1354
1355 $data[-1][$user_id] = array(
1356 "status" => $status[$user_id] ?? null
1357 );
1358
1359 if (isset($marks[$user_id])) {
1360 $data[-1][$user_id]["mark"] = $marks[$user_id];
1361 }
1362 if (isset($notices[$user_id])) {
1363 $data[-1][$user_id]["notice"] = $notices[$user_id];
1364 }
1365 }
1366 $this->saveStatus($data, $a_redirect);
1367 }
1368
1372 public function saveStatusSelectedObject(): void
1373 {
1374 //$members = $this->getMultiActionUserIds();
1375
1376 if ($this->assignment !== null) {
1377 $this->saveStatusAllObject($this->selected_participants);
1378 } else {
1379 $this->saveStatusParticipantObject($this->selected_ass_ids);
1380 }
1381 }
1382
1383 // Save status of selected members
1384
1388 protected function saveStatus(
1389 array $a_data,
1390 bool $a_redirect = true
1391 ): void {
1392 $ilCtrl = $this->ctrl;
1393
1394 $saved_for = array();
1395 foreach ($a_data as $ass_id => $users) {
1396 $ass = ($ass_id < 0)
1397 ? $this->assignment
1398 : new ilExAssignment($ass_id);
1399 foreach ($users as $user_id => $values) {
1400 // this will add team members if available
1401 $submission = new ilExSubmission($ass, $user_id);
1402 foreach ($submission->getUserIds() as $sub_user_id) {
1403 $uname = ilObjUser::_lookupName($sub_user_id);
1404 $saved_for[$sub_user_id] = $uname["lastname"] . ", " . $uname["firstname"];
1405
1406 $member_status = $ass->getMemberStatus($sub_user_id);
1407
1408 // see bug #22566
1409 $status = $values["status"];
1410 if ($status == "") {
1411 $status = self::GRADE_NOT_GRADED;
1412 }
1413 $member_status->setStatus($status);
1414 if (array_key_exists("mark", $values)) {
1415 $member_status->setMark($values["mark"]);
1416 }
1417 if (array_key_exists("notice", $values)) {
1418 $member_status->setNotice($values["notice"]);
1419 }
1420 $member_status->update();
1421 }
1422 }
1423 }
1424
1425 $save_for_str = "";
1426 if ($saved_for !== []) {
1427 $save_for_str = "(" . implode(" - ", $saved_for) . ")";
1428 }
1429
1430 if ($a_redirect) {
1431 $this->tpl->setOnScreenMessage('success', $this->lng->txt("exc_status_saved") . " " . $save_for_str, true);
1432 $ilCtrl->redirect($this, $this->getViewBack());
1433 }
1434 }
1435
1439 public function saveCommentForLearnersObject(): void
1440 {
1441 $res = array("result" => false);
1442
1443 if ($this->ctrl->isAsynch()) {
1444 $ass_id = $this->requested_ass_id;
1445 $user_id = $this->requested_member_id;
1446 $comment = trim($this->requested_comment);
1447
1448 if ($ass_id && $user_id) {
1449 $submission = new ilExSubmission($this->assignment, $user_id);
1450 $user_ids = $submission->getUserIds();
1451
1452 $all_members = new ilExerciseMembers($this->exercise);
1453 $all_members = $all_members->getMembers();
1454
1455 $reci_ids = array();
1456 foreach ($user_ids as $user_id) {
1457 if (in_array($user_id, $all_members)) {
1458 $member_status = $this->assignment->getMemberStatus($user_id);
1459 $member_status->setComment(ilUtil::stripSlashes($comment));
1460 $member_status->setFeedback(true);
1461 $member_status->update();
1462
1463 if (trim($comment) !== '' && trim($comment) !== '0') {
1464 $reci_ids[] = $user_id;
1465 }
1466 }
1467 }
1468
1469 if ($reci_ids !== []) {
1470 // send notification
1471 $this->exercise->sendFeedbackFileNotification(
1472 "",
1473 $reci_ids,
1474 $ass_id,
1475 true
1476 );
1477 }
1478
1479 $res = array("result" => true, "snippet" => nl2br($comment));
1480 }
1481 }
1482
1483 echo(json_encode($res));
1484 exit();
1485 }
1486
1487 public function exportExcelObject(): void
1488 {
1489 $this->exercise->exportGradesExcel();
1490 exit;
1491 }
1492
1493
1494 //
1495 // TEAM
1496 //
1497
1501 public function createTeamsObject(): void
1502 {
1503 $ilCtrl = $this->ctrl;
1504
1505 $members = $this->getMultiActionUserIds(true);
1506
1507 $new_members = array();
1508
1509 foreach ($members as $group) {
1510 if (is_array($group)) {
1511 $new_members = array_merge($new_members, $group);
1512
1513 $first_user = $group;
1514 $first_user = array_shift($first_user);
1515 $team = ilExAssignmentTeam::getInstanceByUserId($this->assignment->getId(), $first_user);
1516 foreach ($group as $user_id) {
1517 $team->removeTeamMember($user_id);
1518 }
1519 } else {
1520 $new_members[] = $group;
1521 }
1522 }
1523
1524 if ($new_members !== []) {
1525 // see ilExSubmissionTeamGUI::addTeamMemberActionObject()
1526
1527 $first_user = array_shift($new_members);
1528 $team = ilExAssignmentTeam::getInstanceByUserId($this->assignment->getId(), $first_user, true);
1529 foreach ($new_members as $user_id) {
1530 $team->addTeamMember($user_id);
1531 }
1532
1533 // re-evaluate complete team, as some members might have had submitted
1534 $submission = new ilExSubmission($this->assignment, $first_user);
1535 $this->exercise->processExerciseStatus(
1536 $this->assignment,
1537 $team->getMembers(),
1538 $submission->hasSubmitted(),
1539 $submission->validatePeerReviews()
1540 );
1541 }
1542
1543 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
1544 $ilCtrl->redirect($this, "members");
1545 }
1546
1550 public function dissolveTeamsObject(): void
1551 {
1552 $ilCtrl = $this->ctrl;
1553
1554 $members = $this->getMultiActionUserIds(true);
1555
1556 foreach ($members as $group) {
1557 // if single member - nothing to do
1558 if (is_array($group)) {
1559 // see ilExSubmissionTeamGUI::removeTeamMemberObject()
1560
1561 $first_user = $group;
1562 $first_user = array_shift($first_user);
1563 $team = ilExAssignmentTeam::getInstanceByUserId($this->assignment->getId(), $first_user);
1564 foreach ($group as $user_id) {
1565 $team->removeTeamMember($user_id);
1566 }
1567
1568 // reset ex team members, as any submission is not valid without team
1569 $this->exercise->processExerciseStatus(
1570 $this->assignment,
1571 $group,
1572 false
1573 );
1574 }
1575 }
1576
1577 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
1578 $ilCtrl->redirect($this, "members");
1579 }
1580
1582 ilPropertyFormGUI $a_form = null
1583 ): void {
1584 $ilCtrl = $this->ctrl;
1585 $ilTabs = $this->tabs_gui;
1586 $lng = $this->lng;
1587 $tpl = $this->tpl;
1588
1589 $ilTabs->clearTargets();
1590 $ilTabs->setBackTarget(
1591 $lng->txt("back"),
1592 $ilCtrl->getLinkTarget($this, $this->getViewBack())
1593 );
1594
1595 if ($a_form === null) {
1596 $a_form = $this->initGroupForm();
1597 }
1598 $tpl->setContent($a_form->getHTML());
1599 }
1600
1601 protected function initGroupForm(): ilPropertyFormGUI
1602 {
1603 $lng = $this->lng;
1604
1605 $form = new ilPropertyFormGUI();
1606 $form->setTitle($lng->txt("exc_adopt_group_teams") . " - " . $this->assignment->getTitle());
1607 $form->setFormAction($this->ctrl->getFormAction($this, "createTeamsFromGroups"));
1608
1609 $all_members = array();
1610 foreach (ilExAssignmentTeam::getGroupMembersMap($this->exercise->getRefId()) as $grp_id => $group) {
1611 if (count($group["members"]) !== 0) {
1612 $grp_team = new ilCheckboxGroupInputGUI($lng->txt("obj_grp") . " \"" . $group["title"] . "\"", "grpt[" . $grp_id . "]");
1613 $grp_value = $options = array();
1614 foreach ($group["members"] as $user_id) {
1615 $user_name = ilUserUtil::getNamePresentation($user_id, false, false, "", true);
1616 $options[$user_id] = $user_name;
1617 if (!in_array($user_id, $all_members)) {
1618 $grp_value[] = $user_id;
1619 $all_members[] = $user_id;
1620 }
1621 }
1622 asort($options);
1623 foreach ($options as $user_id => $user_name) {
1624 $grp_team->addOption(new ilCheckboxOption($user_name, $user_id));
1625 }
1626 $grp_team->setValue($grp_value);
1627 } else {
1628 $grp_team = new ilNonEditableValueGUI($group["title"]);
1629 $grp_team->setValue($lng->txt("exc_adopt_group_teams_no_members"));
1630 }
1631 $form->addItem($grp_team);
1632 }
1633
1634 if ($all_members !== []) {
1635 $form->addCommandButton("createTeamsFromGroups", $lng->txt("save"));
1636 }
1637 $form->addCommandButton("members", $lng->txt("cancel"));
1638
1639 return $form;
1640 }
1641
1645 public function createTeamsFromGroupsObject(): void
1646 {
1647 $lng = $this->lng;
1648
1649 $req_members = $this->requested_group_members;
1650
1651 $form = $this->initGroupForm();
1652 if ($form->checkInput()) {
1653 $map = ilExAssignmentTeam::getGroupMembersMap($this->exercise->getRefId());
1654 $all_members = $teams = array();
1655 $valid = true;
1656 foreach (array_keys($map) as $grp_id) {
1657 if (isset($req_members[$grp_id]) && is_array($req_members[$grp_id])) {
1658 $members = $req_members[$grp_id];
1659 $teams[] = $members;
1660 $invalid_team_members = array();
1661
1662 foreach ($members as $user_id) {
1663 if (!array_key_exists($user_id, $all_members)) {
1664 $all_members[$user_id] = $grp_id;
1665 } else {
1666 // user is selected in multiple groups
1667 $invalid_team_members[] = $user_id;
1668 }
1669 }
1670
1671 if ($invalid_team_members !== []) {
1672 $valid = false;
1673
1674 $alert = array();
1675 foreach ($invalid_team_members as $user_id) {
1676 $user_name = ilUserUtil::getNamePresentation($user_id, false, false, "", true);
1677 $grp_title = $map[$all_members[$user_id]]["title"];
1678 $alert[] = sprintf($lng->txt("exc_adopt_group_teams_conflict"), $user_name, $grp_title);
1679 }
1680 $input = $form->getItemByPostVar("grpt[" . $grp_id . "]");
1681 $input->setAlert(implode("<br/>", $alert));
1682 }
1683 }
1684 }
1685 if ($valid) {
1686 if ($teams !== []) {
1687 $existing_users = array_keys(ilExAssignmentTeam::getAssignmentTeamMap($this->assignment->getId()));
1688
1689 // create teams from group selections
1690 $sum = array("added" => 0, "blocked" => 0);
1691 foreach ($teams as $members) {
1692 foreach ($members as $user_id) {
1693 if (!$this->exercise->members_obj->isAssigned($user_id)) {
1694 $this->exercise->members_obj->assignMember($user_id);
1695 }
1696
1697 if (!in_array($user_id, $existing_users)) {
1698 $sum["added"]++;
1699 } else {
1700 $sum["blocked"]++;
1701 }
1702 }
1703
1704 $first = array_shift($members);
1705 $team = ilExAssignmentTeam::getInstanceByUserId($this->assignment->getId(), $first, true);
1706
1707 // getTeamId() does NOT send notification
1708 // $team->sendNotification($this->exercise->getRefId(), $first, "add");
1709
1710 foreach ($members as $user_id) {
1711 $team->addTeamMember($user_id);
1712 }
1713 }
1714
1715 $mess = array();
1716 if ($sum["added"] !== 0) {
1717 $mess[] = sprintf($lng->txt("exc_adopt_group_teams_added"), $sum["added"]);
1718 }
1719 if ($sum["blocked"] !== 0) {
1720 $mess[] = sprintf($lng->txt("exc_adopt_group_teams_blocked"), $sum["blocked"]);
1721 }
1722 if ($sum["added"] !== 0) {
1723 $this->tpl->setOnScreenMessage('success', implode(" ", $mess), true);
1724 } else {
1725 $this->tpl->setOnScreenMessage('failure', implode(" ", $mess), true);
1726 }
1727 }
1728 $this->ctrl->redirect($this, "members");
1729 } else {
1730 $this->tpl->setOnScreenMessage('failure', $lng->txt("form_input_not_valid"));
1731 }
1732 }
1733
1734 $form->setValuesByPost();
1735 $this->adoptTeamsFromGroupObject($form);
1736 }
1737
1738
1742
1743 public function initMultiFeedbackForm(int $a_ass_id): ilPropertyFormGUI
1744 {
1745 $lng = $this->lng;
1746
1747 $form = new ilPropertyFormGUI();
1748 $form->addCommandButton("uploadMultiFeedback", $lng->txt("upload"));
1749 $form->addCommandButton("members", $lng->txt("cancel"));
1750
1751 // multi feedback file
1752 $fi = new ilFileInputGUI($lng->txt("exc_multi_feedback_file"), "mfzip");
1753 $fi->setSuffixes(array("zip"));
1754 $fi->setRequired(true);
1755 $form->addItem($fi);
1756
1757 $form->setTitle(ilExAssignment::lookupTitle($a_ass_id));
1758 $form->setFormAction($this->ctrl->getFormAction($this, "uploadMultiFeedback"));
1759
1760 return $form;
1761 }
1762
1768 ilPropertyFormGUI $a_form = null
1769 ): void {
1770 $ilToolbar = $this->toolbar;
1771 $lng = $this->lng;
1772 $tpl = $this->tpl;
1773
1774 $this->tpl->setOnScreenMessage('info', $lng->txt("exc_multi_feedb_info"));
1775
1776 $this->addSubTabs("assignment");
1777
1778 // #13719
1779 $button = ilLinkButton::getInstance();
1780 $button->setCaption("exc_download_zip_structure");
1781 $button->setUrl($this->ctrl->getLinkTarget($this, "downloadMultiFeedbackZip"));
1782 $button->setOmitPreventDoubleSubmission(true);
1783 $ilToolbar->addButtonInstance($button);
1784
1785 if ($a_form === null) {
1786 $a_form = $this->initMultiFeedbackForm($this->assignment->getId());
1787 }
1788
1789 $tpl->setContent($a_form->getHTML());
1790 }
1791
1795 public function downloadMultiFeedbackZipObject(): void
1796 {
1797 $this->assignment->sendMultiFeedbackStructureFile($this->exercise);
1798 }
1799
1803 public function uploadMultiFeedbackObject(): void
1804 {
1805 // #11983
1806 $form = $this->initMultiFeedbackForm($this->assignment->getId());
1807 if ($form->checkInput()) {
1808 try {
1809 $this->assignment->uploadMultiFeedbackFile(ilArrayUtil::stripSlashesArray($_FILES["mfzip"]));
1810 $this->ctrl->redirect($this, "showMultiFeedbackConfirmationTable");
1811 } catch (ilException $e) {
1812 $this->tpl->setOnScreenMessage('failure', $e->getMessage(), true);
1813 $this->ctrl->redirect($this, "showMultiFeedback");
1814 }
1815 }
1816
1817 $form->setValuesByPost();
1818 $this->showMultiFeedbackObject($form);
1819 }
1820
1825 {
1826 $tpl = $this->tpl;
1827
1828 $this->addSubTabs("assignment");
1829
1830 $tab = new ilFeedbackConfirmationTable2GUI($this, "showMultiFeedbackConfirmationTable", $this->assignment);
1831 $tpl->setContent($tab->getHTML());
1832 }
1833
1837 public function cancelMultiFeedbackObject(): void
1838 {
1839 $this->assignment->clearMultiFeedbackDirectory();
1840 $this->ctrl->redirect($this, "members");
1841 }
1842
1846 public function saveMultiFeedbackObject(): void
1847 {
1848 $this->assignment->saveMultiFeedbackFiles($this->requested_files, $this->exercise);
1849
1850 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
1851 $this->ctrl->redirect($this, "members");
1852 }
1853
1854
1855 //
1856 // individual deadlines
1857 //
1858
1859 protected function initIndividualDeadlineModal(): string
1860 {
1861 $lng = $this->lng;
1862 $tpl = $this->tpl;
1863
1864 // prepare modal+
1865 $modal = ilModalGUI::getInstance();
1866 $modal->setHeading($lng->txt("exc_individual_deadline"));
1867 $modal->setId("ilExcIDl");
1868 $modal->setBody('<div id="ilExcIDlBody"></div>');
1869 $modal = $modal->getHTML();
1870
1871 $ajax_url = $this->ctrl->getLinkTarget($this, "handleIndividualDeadlineCalls", "", true, false);
1872
1873 $tpl->addJavaScript("./Modules/Exercise/js/ilExcIDl.js", true, 3);
1874 $tpl->addOnLoadCode('il.ExcIDl.init("' . $ajax_url . '");');
1875
1877
1878 return $modal;
1879 }
1880
1885 array $a_data
1886 ): array {
1887 if ($a_data) {
1888 $map = array();
1889 $ass_tmp = array();
1890 foreach ($a_data as $item) {
1891 $item = explode("_", $item);
1892 $ass_id = $item[0];
1893 $user_id = $item[1];
1894
1895 if (!array_key_exists($ass_id, $ass_tmp)) {
1896 if ($this->assignment &&
1897 $ass_id == $this->assignment->getId()) {
1898 $ass_tmp[$ass_id] = $this->assignment;
1899 } else {
1900 $ass_tmp[$ass_id] = new ilExAssignment($ass_id);
1901 }
1902 }
1903
1904 $map[$ass_id][] = $user_id;
1905 }
1906
1907 return array($map, $ass_tmp);
1908 }
1909 return [];
1910 }
1911
1916 protected function handleIndividualDeadlineCallsObject(): void
1917 {
1918 $tpl = $this->tpl;
1919
1920 $this->ctrl->saveParameter($this, "part_id");
1921
1922 // from request "dn", see ilExcIdl.js
1923 if ($this->done) {
1924 $this->tpl->setOnScreenMessage('success', $this->lng->txt("settings_saved"), true);
1925 $this->ctrl->redirect($this, $this->assignment !== null
1926 ? "members"
1927 : "showParticipant");
1928 }
1929
1930 // initial form call
1931 if ($this->requested_idl_id != "") {
1932 $tmp = $this->parseIndividualDeadlineData(explode(",", $this->requested_idl_id));
1933 if (is_array($tmp)) {
1934 $form = $this->initIndividualDeadlineForm($tmp[1], $tmp[0]);
1935 echo $form->getHTML() .
1936 $tpl->getOnLoadCodeForAsynch();
1937 }
1938 }
1939 // form "submit"
1940 else {
1941 $tmp = array();
1942 $post = $this->http->request()->getParsedBody();
1943 foreach (array_keys($post) as $id) {
1944 if (substr($id, 0, 3) == "dl_") {
1945 $tmp[] = substr($id, 3);
1946 }
1947 }
1948 $tmp = $this->parseIndividualDeadlineData($tmp);
1949 $ass_map = $tmp[1];
1950 $users = $tmp[0];
1951 unset($tmp);
1952
1953 $form = $this->initIndividualDeadlineForm($ass_map, $users);
1954 $res = array();
1955 if ($valid = $form->checkInput()) {
1956 foreach ($users as $ass_id => $users2) {
1957 $ass = $ass_map[$ass_id];
1958
1959 // :TODO: should individual deadlines BEFORE extended be possible?
1960 $dl = new ilDateTime($ass->getDeadline(), IL_CAL_UNIX);
1961
1962 foreach ($users2 as $user_id) {
1963 $date_field = $form->getItemByPostVar("dl_" . $ass_id . "_" . $user_id);
1964 if (ilDate::_before($date_field->getDate(), $dl)) {
1965 $date_field->setAlert(sprintf($this->lng->txt("exc_individual_deadline_before_global"), ilDatePresentation::formatDate($dl)));
1966 $valid = false;
1967 } else {
1968 $res[$ass_id][$user_id] = $date_field->getDate();
1969 }
1970 }
1971 }
1972 }
1973
1974 if (!$valid) {
1975 $form->setValuesByPost();
1976 echo $form->getHTML() .
1977 $tpl->getOnLoadCodeForAsynch();
1978 } else {
1979 foreach ($res as $ass_id => $users) {
1980 $ass = $ass_map[$ass_id];
1981
1982 foreach ($users as $id => $date) {
1983 $ass->setIndividualDeadline($id, $date);
1984 }
1985
1986 $ass->recalculateLateSubmissions();
1987 }
1988
1989 echo "ok";
1990 }
1991 }
1992
1993 exit();
1994 }
1995
1999 protected function initIndividualDeadlineForm(
2000 array $a_ass_map,
2001 array $ids
2003 $form = new ilPropertyFormGUI();
2004 $form->setFormAction($this->ctrl->getFormAction($this));
2005 $form->setName("ilExcIDlForm");
2006
2007 foreach ($ids as $ass_id => $users) {
2008 $ass = $a_ass_map[$ass_id];
2009
2010 $section = new ilFormSectionHeaderGUI();
2011 $section->setTitle($ass->getTitle());
2012 $form->addItem($section);
2013
2014 $teams = ilExAssignmentTeam::getInstancesFromMap($ass->getId());
2015
2016 $values = $ass->getIndividualDeadlines();
2017
2018 foreach ($users as $id) {
2019 // single user
2020 if (is_numeric($id)) {
2022 $name = $name["lastname"] . ", " . $name["firstname"];
2023 }
2024 // team
2025 else {
2026 $name = "";
2027 $team_id = (int) substr($id, 1);
2028 if (array_key_exists($team_id, $teams)) {
2029 $name = array();
2030 foreach ($teams[$team_id]->getMembers() as $member_id) {
2031 $uname = ilObjUser::_lookupName($member_id);
2032 $name[] = $uname["lastname"] . ", " . $uname["firstname"];
2033 }
2034 asort($name);
2035 $name = implode("<br />", $name);
2036 }
2037 }
2038
2039 $dl = new ilDateTimeInputGUI($name, "dl_" . $ass_id . "_" . $id);
2040 $dl->setShowTime(true);
2041 $dl->setRequired(true);
2042 $form->addItem($dl);
2043
2044 if (array_key_exists($id, $values)) {
2045 $dl->setDate(new ilDateTime($values[$id], IL_CAL_UNIX));
2046 }
2047 }
2048 }
2049
2050 $form->addCommandButton("", $this->lng->txt("save"));
2051
2052 return $form;
2053 }
2054
2058 protected function setIndividualDeadlineObject(): void
2059 {
2060 // this will only get called if no selection
2061 $this->tpl->setOnScreenMessage('failure', $this->lng->txt("select_one"));
2062
2063 if ($this->assignment !== null) {
2064 $this->membersObject();
2065 } else {
2066 $this->showParticipantObject();
2067 }
2068 }
2069
2070 public function initFilter(): void
2071 {
2072 if ($this->requested_filter_status != "") {
2073 $this->filter["status"] = $this->requested_filter_status;
2074 }
2075
2076 $this->lng->loadLanguageModule("search");
2077
2078 $this->toolbar->setFormAction($this->ctrl->getFormAction($this, "listTextAssignment"));
2079
2080 // Status
2081
2082 $si_status = new ilSelectInputGUI($this->lng->txt("exc_tbl_status"), "filter_status");
2083 $options = array(
2084 "" => $this->lng->txt("search_any"),
2085 self::GRADE_NOT_GRADED => $this->lng->txt("exc_notgraded"),
2086 self::GRADE_PASSED => $this->lng->txt("exc_passed"),
2087 self::GRADE_FAILED => $this->lng->txt("exc_failed")
2088 );
2089 $si_status->setOptions($options);
2090 $si_status->setValue($this->filter["status"] ?? "");
2091
2092 $si_feedback = new ilSelectInputGUI($this->lng->txt("feedback"), "filter_feedback");
2093 $options = array(
2094 self::FEEDBACK_FULL_SUBMISSION => $this->lng->txt("submissions_feedback"),
2095 self::FEEDBACK_ONLY_SUBMISSION => $this->lng->txt("submissions_only")
2096 );
2097 $si_feedback->setOptions($options);
2098 $si_feedback->setValue($this->filter["feedback"] ?? "");
2099
2100 $this->toolbar->addInputItem($si_status, true);
2101
2102 // Submissions and Feedback
2103 #24713
2104 if ($this->assignment->getPeerReview()) {
2105 if ($this->requested_filter_feedback != "") {
2106 $this->filter["feedback"] = $this->requested_filter_feedback;
2107 } else {
2108 $this->filter["feedback"] = "submission_feedback";
2109 }
2110
2111 $si_feedback = new ilSelectInputGUI($this->lng->txt("feedback"), "filter_feedback");
2112 $options = array(
2113 "submission_feedback" => $this->lng->txt("submissions_feedback"),
2114 "submission_only" => $this->lng->txt("submissions_only")
2115 );
2116 $si_feedback->setOptions($options);
2117 $si_feedback->setValue($this->filter["feedback"] ?? "");
2118
2119 $this->toolbar->addInputItem($si_feedback, true);
2120 }
2121
2122 //todo: old school here.
2123 $submit = ilSubmitButton::getInstance();
2124 $submit->setCaption("filter");
2125 $submit->setCommand("listTextAssignment");
2126 $this->toolbar->addButtonInstance($submit);
2127 }
2128
2132 public function openSubmissionPrintViewObject(): void
2133 {
2134 $this->openSubmissionViewObject(true);
2135 }
2136
2140 public function openSubmissionViewObject(bool $print_version = false): void
2141 {
2142 global $DIC;
2143
2144 $member_id = $this->requested_member_id;
2145
2146 $submission = new ilExSubmission($this->assignment, $member_id);
2147
2148 $last_opening = $submission->getLastOpeningHTMLView();
2149 $submission_time = $submission->getLastSubmission();
2150
2151 // e.g. /<datadir>/<clientid>/ilExercise/3/exc_367/subm_1/<ass_id>/20210628175716_368
2152 $zip_original_full_path = $this->getSubmissionZipFilePath($submission, $print_version);
2153 $this->log->debug("zip original full path: " . $zip_original_full_path);
2154
2155 // e.g. ilExercise/3/exc_367/subm_1/<ass_id>/20210628175716_368
2156 $zip_internal_path = $this->getWebFilePathFromExternalFilePath($zip_original_full_path);
2157 $this->log->debug("zip internal path: " . $zip_internal_path);
2158
2159 $arr = explode("_", basename($zip_original_full_path));
2160 $obj_date = $arr[0];
2161 $obj_id = (int) ($arr[1] ?? 0);
2162 if ($obj_id === 0) {
2163 throw new ilExerciseException("Cannot open HTML view for " . $zip_internal_path . " / " .
2164 $submission->getSubmittedPrintFile() . ".");
2165 }
2166
2167 $obj_id = $this->assignment->getAssignmentType()->getExportObjIdForResourceId($obj_id);
2168 if ($print_version) {
2169 $obj_id .= "print";
2170 }
2171
2172 $obj_dir = $this->assignment->getAssignmentType()->getStringIdentifier() . "_" . $obj_id;
2173
2174 $index_html_file = ILIAS_WEB_DIR .
2175 DIRECTORY_SEPARATOR .
2176 CLIENT_ID .
2177 DIRECTORY_SEPARATOR .
2178 dirname($zip_internal_path) .
2179 DIRECTORY_SEPARATOR .
2180 $obj_dir .
2181 DIRECTORY_SEPARATOR .
2182 "index.html";
2183 $this->log->debug("index html file: " . $index_html_file);
2184
2186
2187 $web_filesystem = $DIC->filesystem()->web();
2188 if ($last_opening > $submission_time && $web_filesystem->has($index_html_file)) {
2189 ilUtil::redirect($index_html_file);
2190 }
2191 $error_msg = "";
2192 if ($zip_original_full_path) {
2193 $file_copied = $this->copyFileToWebDir($zip_internal_path);
2194 $this->log->debug("file copied: " . $file_copied);
2195 if ($file_copied) {
2196 ilFileUtils::unzip($file_copied, true);
2197 $web_filesystem->delete($zip_internal_path);
2198 $this->log->debug("deleting: " . $zip_internal_path);
2199
2200 $submission_repository = $this->service->repo()->submission();
2201 $submission_repository->updateWebDirAccessTime($this->assignment->getId(), $member_id);
2202
2203 ilUtil::redirect($index_html_file . "?" . time());
2204 }
2205
2206 $error_msg = $this->lng->txt("exc_copy_zip_error");
2207 }
2208
2209 if ($error_msg === '' || $error_msg === '0') {
2210 $error_msg = $this->lng->txt("exc_find_zip_error");
2211 }
2212
2213 $this->tpl->setOnScreenMessage('failure', $error_msg);
2214 }
2215
2221 protected function getSubmissionZipFilePath(
2222 ilExSubmission $submission,
2223 bool $print_versions = false
2224 ): ?string {
2225 $submitted = $submission->getFiles(
2226 null,
2227 false,
2228 null,
2229 $print_versions
2230 );
2231 if ($submitted !== []) {
2232 $submitted = array_pop($submitted);
2233
2234 return $submitted['filename'];
2235 }
2236
2237 return null;
2238 }
2239
2244 protected function copyFileToWebDir(
2245 string $internal_file_path
2246 ): ?string {
2247 global $DIC;
2248
2249 $web_filesystem = $DIC->filesystem()->web();
2250 $data_filesystem = $DIC->filesystem()->storage();
2251
2252 $internal_dirs = dirname($internal_file_path);
2253 $zip_file = basename($internal_file_path);
2254
2255 if ($data_filesystem->has($internal_file_path)) {
2256 $this->log->debug("internal file path: " . $internal_file_path);
2257 if ($web_filesystem->hasDir($internal_dirs)) {
2258 $web_filesystem->deleteDir($internal_dirs);
2259 }
2260 $web_filesystem->createDir($internal_dirs);
2261
2262 if ($web_filesystem->has($internal_file_path)) {
2263 $web_filesystem->delete($internal_file_path);
2264 }
2265 if (!$web_filesystem->has($internal_file_path)) {
2266 $this->log->debug("writing: " . $internal_file_path);
2267 $stream = $data_filesystem->readStream($internal_file_path);
2268 $web_filesystem->writeStream($internal_file_path, $stream);
2269
2270 return ILIAS_ABSOLUTE_PATH .
2271 DIRECTORY_SEPARATOR .
2273 DIRECTORY_SEPARATOR .
2274 CLIENT_ID .
2275 DIRECTORY_SEPARATOR .
2276 $internal_dirs .
2277 DIRECTORY_SEPARATOR .
2278 $zip_file;
2279 }
2280 }
2281
2282 return null;
2283 }
2284
2289 string $external_file_path
2290 ): string {
2291 list($external_path, $internal_file_path) = explode(CLIENT_ID . "/ilExercise", $external_file_path);
2292 $internal_file_path = "ilExercise" . $internal_file_path;
2293 return $internal_file_path;
2294 }
2295
2296 /*
2297 * Add the Back link to the tabs. (used in submission list and submission compare)
2298 */
2299 protected function setBackToMembers(): void
2300 {
2301 //tabs
2302 $this->tabs_gui->clearTargets();
2303 $this->tabs_gui->setBackTarget(
2304 $this->lng->txt("back"),
2305 $this->ctrl->getLinkTarget($this, "members")
2306 );
2307 }
2308
2310 array $a_data
2311 ): array {
2312 $user = new ilObjUser($a_data["user_id"]);
2313 $uname = $user->getFirstname() . " " . $user->getLastname();
2314
2315 $data = array(
2316 "uid" => $a_data["user_id"],
2317 "uname" => $uname,
2318 "udate" => $a_data["ts"],
2319 "utext" => ilRTE::_replaceMediaObjectImageSrc($a_data["atext"], 1) // mob id to mob src
2320 );
2321
2322 //get data peer and assign it
2323 $peer_review = new ilExPeerReview($this->assignment);
2324 $data["peer"] = array();
2325 foreach ($peer_review->getPeerReviewsByPeerId($a_data['user_id']) as $value) {
2326 $data["peer"][] = $value['giver_id'];
2327 }
2328
2329 $data["fb_received"] = count($data["peer"]);
2330 $data["fb_given"] = $peer_review->countGivenFeedback(true, $a_data["user_id"]);
2331
2332 return $data;
2333 }
2334}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
if(!defined('PATH_SEPARATOR')) $GLOBALS['_PEAR_default_error_mode']
Definition: PEAR.php:64
$comment
Definition: buildRTE.php:72
setVariable($variable, $value='')
Sets a variable value.
Definition: IT.php:514
Exercise gui request wrapper.
getTutorNotices()
key might be ass_ids or user_ids!
getMarks()
key might be ass_ids or user_ids!
getStatus()
key might be ass_ids or user_ids!
const IL_CAL_UNIX
const IL_CAL_DATETIME
return true
static stripSlashesArray(array $a_arr, bool $a_strip_html=true, string $a_allow="")
static sortArray(array $array, string $a_array_sortby_key, string $a_array_sortorder="asc", bool $a_numeric=false, bool $a_keep_keys=false)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Class ilCtrl provides processing control methods.
redirect(object $a_gui_obj, string $a_cmd=null, string $a_anchor=null, bool $is_async=false)
@inheritDoc
setParameter(object $a_gui_obj, string $a_parameter, $a_value)
@inheritDoc
setParameterByClass(string $a_class, string $a_parameter, $a_value)
@inheritDoc
static formatDate(ilDateTime $date, bool $a_skip_day=false, bool $a_include_wd=false, bool $include_seconds=false)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
@classDescription Date and time handling
static _before(ilDateTime $start, ilDateTime $end, string $a_compare_field='', string $a_tz='')
compare two dates and check start is before end This method does not consider tz offsets.
Download submissions and feedback for exercises.
static getAssignmentTeamMap(int $a_ass_id)
static getAdoptableGroups(int $a_exc_ref_id)
static getInstanceByUserId(int $a_assignment_id, int $a_user_id, bool $a_create_on_demand=false)
static getInstancesFromMap(int $a_assignment_id)
static getGroupMembersMap(int $a_exc_ref_id)
Exercise assignment.
static getInstancesByExercise(int $a_exc_id)
static lookupTitle(int $a_id)
Exercise participant table.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Exercise peer review.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static downloadAllAssignmentFiles(ilExAssignment $a_ass, array $members, string $to_path)
Download all submitted files of an assignment (all user)
static getAssignmentFilesByUsers(int $a_exc_id, int $a_ass_id, array $a_users)
static getAssignmentParticipants(int $a_exercise_id, int $a_ass_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Class ilExerciseManagementGUI.
initIndividualDeadlineForm(array $a_ass_map, array $ids)
saveStatusParticipantObject(array $selected_ass_ids=null)
Save assignment status (participant view)
uploadMultiFeedbackObject()
Upload multi feedback file.
showMultiFeedbackObject(ilPropertyFormGUI $a_form=null)
Show multi-feedback screen.
saveStatus(array $a_data, bool $a_redirect=true)
getMultiActionUserIds(bool $a_keep_teams=false)
adoptTeamsFromGroupObject(ilPropertyFormGUI $a_form=null)
copyFileToWebDir(string $internal_file_path)
Generate the directories and copy the file if necessary.
openSubmissionViewObject(bool $print_version=false)
Open HTML view for portfolio submissions.
showMultiFeedbackConfirmationTableObject()
Show multi feedback confirmation table.
saveMultiFeedbackObject()
Save multi feedback.
getWebFilePathFromExternalFilePath(string $external_file_path)
Get the object specific file path from an external full file path.
openSubmissionPrintViewObject()
Open HTML view for portfolio submissions.
cancelMultiFeedbackObject()
Cancel Multi Feedback.
saveStatusAllObject(array $a_selected=null, bool $a_redirect=true)
ILIAS Exercise InternalGUIService $gui
ILIAS Repository HTML HTMLUtil $html_util
membersObject()
All participants and submission of one assignment.
compareTextAssignmentsObject()
TODO -> Deal with the redirection after update the grade via action button.
getSubmissionZipFilePath(ilExSubmission $submission, bool $print_versions=false)
Returns the ZIP file path from outside web directory.
downloadMultiFeedbackZipObject()
Download multi-feedback structrue file.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This class represents a file property in a property form.
File System Explorer GUI class.
static unzip(string $path_to_zip_file, bool $overwrite_existing=false, bool $unpack_flat=false)
setFormAction(string $a_formaction)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setContent(string $a_html)
Sets content for standard template.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
language handling
txt(string $a_topic, string $a_default_lang_fallback_mod="")
gets the text for a given topic if the topic is not in the list, the topic itself with "-" will be re...
static getLogger(string $a_component_id)
Get component logger.
Component logger with individual log levels by component id.
static getRedirectTarget( $gui, string $cmd, array $gui_params=[], array $mail_params=[], array $context_params=[])
static getInstance()
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Class ilObjExercise.
User class.
static _lookupId($a_user_str)
static _lookupName(int $a_user_id)
lookup user name
static _lookupLogin(int $a_user_id)
static getInstanceByObjId(?int $obj_id, bool $stop_on_error=true)
get an instance of an Ilias object by object id
static _lookupType(int $id, bool $reference=false)
static _exists(int $id, bool $reference=false, ?string $type=null)
checks if an object exists in object_data
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This class represents a property form user interface.
static _replaceMediaObjectImageSrc(string $a_text, int $a_direction=0, string $nic='')
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
static fillAutoCompleteToolbar(object $parent_object, ilToolbarGUI $toolbar=null, array $a_options=[], bool $a_sticky=false)
array( auto_complete_name = $lng->txt('user'), auto_complete_size = 15, user_type = array(ilCoursePar...
This class represents a selection list property in a property form.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
clearTargets()
clear all targets
special template class to simplify handling of ITX/PEAR
This class represents a text area property in a property form.
This class represents a text property in a property form.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getNamePresentation( $a_user_id, bool $a_user_image=false, bool $a_profile_link=false, string $a_profile_back_link="", bool $a_force_first_lastname=false, bool $a_omit_login=false, bool $a_sortable=true, bool $a_return_data_array=false, $a_ctrl_path="ilpublicuserprofilegui")
Default behaviour is:
static stripSlashes(string $a_str, bool $a_strip_html=true, string $a_allow="")
static redirect(string $a_script)
static signFolderOfStartFile(string $start_file_path)
if(!file_exists(getcwd() . '/ilias.ini.php'))
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: confirmReg.php:20
const CLIENT_ID
Definition: constants.php:41
const ILIAS_WEB_DIR
Definition: constants.php:45
$valid
global $DIC
Definition: feed.php:28
This is how the factory for UI elements looks.
Definition: Factory.php:38
An entity that renders components to a string output.
Definition: Renderer.php:31
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Interface ilDBInterface.
filterUserIdsByRbacOrPositionOfCurrentUser(string $rbac_perm, string $pos_perm, int $ref_id, array $user_ids)
if($DIC->http() ->request() ->getMethod()=="GET" &&isset($DIC->http() ->request() ->getQueryParams()['tex'])) $tpl
Definition: latex.php:41
exit
Definition: login.php:28
$ref_id
Definition: ltiauth.php:67
$res
Definition: ltiservices.php:69
$post
Definition: ltitoken.php:49
if($format !==null) $name
Definition: metadata.php:247
static http()
Fetches the global http state from ILIAS.
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
string $key
Consumer key/client ID value.
Definition: System.php:193
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
$url
$lng