ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
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;
53  protected ilTabsGUI $tabs_gui;
54  protected ilLanguage $lng;
57  protected Factory $ui_factory;
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;
88  protected string $requested_filter_feedback;
89  protected GUIRequest $request;
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();
117  $request = $this->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;
160  $lng = $this->lng;
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()) {
272  case self::VIEW_PARTICIPANT:
273  $back_cmd = "showParticipant";
274  break;
275 
276  case self::VIEW_GRADES:
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;
305  $lng = $this->lng;
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  {
342  $lng = $this->lng;
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  {
358  $tpl = $this->tpl;
359  $ilToolbar = $this->toolbar;
360  $ilCtrl = $this->ctrl;
361  $lng = $this->lng;
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());
460  $tpl->setContent(
461  $exc_tab->getHTML() .
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(),
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;
522  $lng = $this->lng;
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
790  ): ilPropertyFormGUI {
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  {
923  $tpl = $this->tpl;
924  $ilToolbar = $this->toolbar;
925  $ilCtrl = $this->ctrl;
926  $lng = $this->lng;
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 
935  $members = $access->filterUserIdsByRbacOrPositionOfCurrentUser(
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 
992  $part_tab = new ilAssignmentsPerParticipantTableGUI(
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 
1581  public function adoptTeamsFromGroupObject(
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 
1767  public function showMultiFeedbackObject(
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 
1884  protected function parseIndividualDeadlineData(
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
2002  ): ilPropertyFormGUI {
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 
2185  ilWACSignedPath::signFolderOfStartFile($index_html_file);
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 .
2272  ILIAS_WEB_DIR .
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 }
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...
addOnLoadCode(string $a_code, int $a_batch=2)
Add on load code.
An entity that renders components to a string output.
Definition: Renderer.php:30
$res
Definition: ltiservices.php:69
getFiles(array $a_file_ids=null, bool $a_only_valid=false, string $a_min_timestamp=null, bool $print_versions=false)
Get submission items (not only files)
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...
exit
Definition: login.php:28
Exercise assignment.
const IL_CAL_DATETIME
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getLogger(string $a_component_id)
Get component logger.
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 downloadAllAssignmentFiles(ilExAssignment $a_ass, array $members, string $to_path)
Download all submitted files of an assignment (all user)
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...
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...
redirect(object $a_gui_obj, string $a_cmd=null, string $a_anchor=null, bool $is_async=false)
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.
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.
getTutorNotices()
key might be ass_ids or user_ids!
static stripSlashes(string $a_str, bool $a_strip_html=true, string $a_allow="")
$valid
copyFileToWebDir(string $internal_file_path)
Generate the directories and copy the file if necessary.
static lookupTitle(int $a_id)
getStatus()
key might be ass_ids or user_ids!
static formatDate(ilDateTime $date, bool $a_skip_day=false, bool $a_include_wd=false, bool $include_seconds=false)
getMarks()
key might be ass_ids or user_ids!
static _lookupName(int $a_user_id)
lookup user name
Exercise internal service.
getWebFilePathFromExternalFilePath(string $external_file_path)
Get the object specific file path from an external full file path.
static _lookupId($a_user_str)
setParameterByClass(string $a_class, string $a_parameter, $a_value)
setSuffixes(array $a_suffixes)
setOptions(array $a_options)
static getInstanceByUserId(int $a_assignment_id, int $a_user_id, bool $a_create_on_demand=false)
showMultiFeedbackObject(ilPropertyFormGUI $a_form=null)
Show multi-feedback screen.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
const IL_CAL_UNIX
filterParticipantsByAccess()
Filter manageable members by position or rbac access.
addJavaScript(string $a_js_file, bool $a_add_version_parameter=true, int $a_batch=2)
Add a javascript file that should be included in the header.
static getAssignmentParticipants(int $a_exercise_id, int $a_ass_id)
ILIAS Exercise InternalGUIService $gui
static unzip(string $path_to_zip_file, bool $overwrite_existing=false, bool $unpack_flat=false)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getAdoptableGroups(int $a_exc_ref_id)
getSubmissionZipFilePath(ilExSubmission $submission, bool $print_versions=false)
Returns the ZIP file path from outside web directory.
global $DIC
Definition: feed.php:28
saveStatusAllObject(array $a_selected=null, bool $a_redirect=true)
saveStatusParticipantObject(array $selected_ass_ids=null)
Save assignment status (participant view)
if($format !==null) $name
Definition: metadata.php:247
resetOffset(bool $a_in_determination=false)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Exercise peer review.
filterUserIdsByRbacOrPositionOfCurrentUser(string $rbac_perm, string $pos_perm, int $ref_id, array $user_ids)
compareTextAssignmentsObject()
TODO -> Deal with the redirection after update the grade via action button.
static _exists(int $id, bool $reference=false, ?string $type=null)
checks if an object exists in object_data
Class ilObjExercise.
$ref_id
Definition: ltiauth.php:67
static http()
Fetches the global http state from ILIAS.
static signFolderOfStartFile(string $start_file_path)
Download submissions and feedback for exercises.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
uploadMultiFeedbackObject()
Upload multi feedback file.
const CLIENT_ID
Definition: constants.php:41
if(!defined('PATH_SEPARATOR')) $GLOBALS['_PEAR_default_error_mode']
Definition: PEAR.php:64
string $key
Consumer key/client ID value.
Definition: System.php:193
downloadMultiFeedbackZipObject()
Download multi-feedback structrue file.
ILIAS Repository HTML HTMLUtil $html_util
static fillAutoCompleteToolbar(object $parent_object, ilToolbarGUI $toolbar=null, array $a_options=[], bool $a_sticky=false)
array( auto_complete_name = $lng->txt(&#39;user&#39;), auto_complete_size = 15, user_type = array(ilCoursePar...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
clearTargets()
clear all targets
static getAssignmentFilesByUsers(int $a_exc_id, int $a_ass_id, array $a_users)
$comment
Definition: buildRTE.php:72
static getAssignmentTeamMap(int $a_ass_id)
static redirect(string $a_script)
getOnLoadCodeForAsynch()
Get js onload code for ajax calls.
Class ilExerciseManagementGUI.
static getInstancesByExercise(int $a_exc_id)
static getInstanceByObjId(?int $obj_id, bool $stop_on_error=true)
get an instance of an Ilias object by object id
static getInstance()
setOnScreenMessage(string $a_type, string $a_txt, bool $a_keep=false)
Set a message to be displayed to the user.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
cancelMultiFeedbackObject()
Cancel Multi Feedback.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
openSubmissionViewObject(bool $print_version=false)
Open HTML view for portfolio submissions.
membersObject()
All participants and submission of one assignment.
__construct(Container $dic, ilPlugin $plugin)
This class represents a text area property in a property form.
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
static getRedirectTarget( $gui, string $cmd, array $gui_params=[], array $mail_params=[], array $context_params=[])
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
adoptTeamsFromGroupObject(ilPropertyFormGUI $a_form=null)
showMultiFeedbackConfirmationTableObject()
Show multi feedback confirmation table.
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...
$url
saveMultiFeedbackObject()
Save multi feedback.
File System Explorer GUI class.
setParameter(object $a_gui_obj, string $a_parameter, $a_value)
static getGroupMembersMap(int $a_exc_ref_id)
static _lookupType(int $id, bool $reference=false)
$post
Definition: ltitoken.php:49
Exercise gui request wrapper.
Exercise participant table.
getMultiActionUserIds(bool $a_keep_teams=false)
static stripSlashesArray(array $a_arr, bool $a_strip_html=true, string $a_allow="")
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
initIndividualDeadlineForm(array $a_ass_map, array $ids)
saveStatus(array $a_data, bool $a_redirect=true)
setContent(string $a_html)
Sets content for standard template.
openSubmissionPrintViewObject()
Open HTML view for portfolio submissions.
static getInstancesFromMap(int $a_assignment_id)
const ILIAS_WEB_DIR
Definition: constants.php:45
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static sortArray(array $array, string $a_array_sortby_key, string $a_array_sortorder="asc", bool $a_numeric=false, bool $a_keep_keys=false)
setTableId(string $a_val)
static _lookupLogin(int $a_user_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...