diff --git a/Modules/Exercise/classes/class.ilExAssignment.php b/Modules/Exercise/classes/class.ilExAssignment.php index 094444094f53..9639a8a03f86 100644 --- a/Modules/Exercise/classes/class.ilExAssignment.php +++ b/Modules/Exercise/classes/class.ilExAssignment.php @@ -2566,6 +2566,18 @@ public function getCalculatedDeadlines() } return $calculated_deadlines; } + + public function isVersionable() + { + if($this->getType() == ilExAssignment::TYPE_TEXT) + { + $exc_settings = new ilSetting("excs"); + if($exc_settings->get("enable_versioning")) { + return true; + } + } + return false; + } } ?> \ No newline at end of file diff --git a/Modules/Exercise/classes/class.ilExSubmission.php b/Modules/Exercise/classes/class.ilExSubmission.php index b7f26034f302..d91269350698 100644 --- a/Modules/Exercise/classes/class.ilExSubmission.php +++ b/Modules/Exercise/classes/class.ilExSubmission.php @@ -14,8 +14,6 @@ class ilExSubmission const TYPE_TEXT = "Text"; const TYPE_REPO_OBJECT = "RepoObject"; // Wikis - - /** * @var ilObjUser */ @@ -201,7 +199,8 @@ public function getSelectedObject() public function canSubmit() { return ($this->isOwner() && - $this->state->isSubmissionAllowed()); + $this->state->isSubmissionAllowed() || + $this->isVersioned()); } public function canView() @@ -523,8 +522,13 @@ function getFiles(array $a_file_ids = null, $a_only_valid = false, $a_min_timest if($a_min_timestamp) { $sql .= " AND ts > ".$ilDB->quote($a_min_timestamp, "timestamp"); - } - + } + + if($this->isVersioned()) + { + $sql .= " ORDER BY returned_id DESC LIMIT 1"; + } + $result = $ilDB->query($sql); $delivered_files = array(); @@ -654,6 +658,30 @@ public static function findUserFiles($a_user_id, $a_filetitle) } return $res; } + + /** + * Return the submission ids related to the specific user and assignment + * @param $a_user_id + * @param $a_filetitle + * @return array + */ + public function getSubmissionsByUser() : array + { + $sql = "SELECT *". + " FROM exc_returned". + " WHERE user_id = ".$this->db->quote($this->user_id, "integer"). + " AND ass_id = ".$this->db->quote($this->assignment->getId(), "integer"); + + $set = $this->db->query($sql); + + $res = array(); + while($row = $this->db->fetchAssoc($set)) + { + $res[] = $row; + } + + return $res; + } function deleteAllFiles() { @@ -1666,5 +1694,15 @@ static function getDirectoryNameFromUserData($a_user_id) return $targetdir; } + + /** + * Check if the submission has been versioned by tutor/admin + * @return bool + */ + public function isVersioned() : bool + { + $revision = new ilExSubmissionRevision($this); + return $revision->isVersioned(); + } } diff --git a/Modules/Exercise/classes/class.ilExSubmissionPanelsHandlerGUI.php b/Modules/Exercise/classes/class.ilExSubmissionPanelsHandlerGUI.php new file mode 100644 index 000000000000..f218bc2f5a9d --- /dev/null +++ b/Modules/Exercise/classes/class.ilExSubmissionPanelsHandlerGUI.php @@ -0,0 +1,594 @@ + + * @ingroup ModulesExercise + */ +class ilExSubmissionPanelsHandlerGUI +{ + const PANEL_TYPE_SUBMISSION = 1; + const PANEL_TYPE_REVISION = 2; + + const FEEDBACK_ONLY_SUBMISSION = "submission_only"; + const FEEDBACK_FULL_SUBMISSION = "submission_feedback"; + + const GRADE_NOT_GRADED = "notgraded"; + const GRADE_PASSED = "passed"; + const GRADE_FAILED = "failed"; + + /** + * @var ilExSubmission + */ + protected $submission; + + /** + * @var ilLanguage + */ + protected $lng; + + /** + * @var ilTemplate + */ + protected $tpl; + + /** + * @var ilCtrl + */ + protected $ctrl; + + /** + * @var ilTabsGUI + */ + protected $tabs; + + /** + * @var ilExAssignment + */ + protected $assignment; + + /** + * @var \ILIAS\UI\Factory + */ + protected $ui_factory; + + /** + * @var \ILIAS\UI\Renderer + */ + protected $ui_renderer; + + /** + * @var array + */ + protected $filter; + + /** + * @var ilToolbarGUI + */ + protected $toolbar; + + /** + * @var array + */ + protected $submissions_data; + + /** + * @var int + */ + protected $back_link; + + /** + * Constructor + * @param ilExAssignment $a_assignment + * @param integer $a_user_id + */ + public function __construct(ilExAssignment $a_assignment, int $a_user_id = null) + { + global $DIC; + + $this->lng = $DIC->language(); + $this->tpl = $DIC->ui()->mainTemplate(); + $this->ctrl = $DIC->ctrl(); + $this->tabs = $DIC->tabs(); + $this->ui_factory = $DIC->ui()->factory(); + $this->ui_renderer = $DIC->ui()->renderer(); + $this->toolbar = $DIC->toolbar(); + $this->assignment = $a_assignment; + + //going back to submissions and grades tab as default behavior + $this->back_link = $this->ctrl->getParentReturn($this); + + if($a_user_id) + { + $this->submission = new ilExSubmission($a_assignment, $a_user_id); + } + else + { + $this->submission = new ilExSubmission($a_assignment, $DIC->user()->getId()); + + //going back to exercise assignments tab + if($_GET['vw'] != ilExerciseManagementGUI::VIEW_GRADES) { + $this->back_link = $this->ctrl->getLinkTargetByClass("ilObjExerciseGUI", "showOverview"); + } + } + } + + + public function executeCommand() + { + $cmd = $this->ctrl->getCmd(); + $this->{$cmd."Object"}(); + } + + + //TODO structure this data. + public function setSubmissionsData($a_data) + { + $this->submissions_data = $a_data; + } + + + /** + * Display a list of panels with all versioned submissions. + */ + public function showVersionsObject() + { + $revision_obj = new ilExSubmissionRevision($this->submission); + + $submissions = $revision_obj->getRevisions(); + + $this->showSubmissionPanels($this->lng->txt("exc_submission_list_versions"), self::PANEL_TYPE_REVISION, $submissions); + } + + + /** + * Display the HTML with a all submission panels + * @param string $a_title + * @param int $a_type + * @param array $a_submissions_data + */ + public function showSubmissionPanels(string $a_title, int $a_type, array $a_submissions_data) + { + $this->setBackLink(); + + $group_panels_tpl = new ilTemplate("tpl.exc_group_report_panels.html", TRUE, TRUE, "Modules/Exercise"); + $group_panels_tpl->setVariable('TITLE', $a_title); + + $report_html = ""; + + foreach($a_submissions_data as $submission_data) + { + //TODO : feedback data in the list of versions?? should I filter here? Feedback is not showed for versions + $feedback_data = $this->collectFeedbackDataFromPeer($submission_data); + $data = array_merge($feedback_data, $submission_data); + $report_html .= $this->getReportPanel($a_type, $data); + } + + $group_panels_tpl->setVariable('CONTENT', $report_html); + $this->tpl->setContent($group_panels_tpl->get()); + } + + + /** + * Add the Back link to the tabs. (used in submission list and submission compare) + * TODO -> Can we move this to ilExerciseManagementGUI? it will be better + */ + protected function setBackLink() + { + $this->tabs->clearTargets(); + $this->tabs->setBackTarget($this->lng->txt("back"),$this->back_link); + } + + + /** + * Filter initialization + * Filter by grade + * Display Feedback or not. + */ + function initFilter() + { + if($_POST["filter_status"]) { + $this->filter["status"] = trim(ilUtil::stripSlashes($_POST["filter_status"])); + } + + if($_POST["filter_feedback"]) { + $this->filter["feedback"] = trim(ilUtil::stripSlashes($_POST["filter_feedback"])); + } + + $this->lng->loadLanguageModule("search"); + + $this->toolbar->setFormAction($this->ctrl->getFormAction($this, "listTextAssignment")); + + $si_status = new ilSelectInputGUI($this->lng->txt("exc_tbl_status"), "filter_status"); + $options = array( + "" => $this->lng->txt("search_any"), + self::GRADE_NOT_GRADED => $this->lng->txt("exc_notgraded"), + self::GRADE_PASSED => $this->lng->txt("exc_passed"), + self::GRADE_FAILED => $this->lng->txt("exc_failed") + ); + $si_status->setOptions($options); + $si_status->setValue($this->filter["status"]); + + $si_feedback = new ilSelectInputGUI($this->lng->txt("feedback"), "filter_feedback"); + $options = array( + self::FEEDBACK_FULL_SUBMISSION => $this->lng->txt("submissions_feedback"), + self::FEEDBACK_ONLY_SUBMISSION => $this->lng->txt("submissions_only") + ); + $si_feedback->setOptions($options); + $si_feedback->setValue($this->filter["feedback"]); + + $this->toolbar->addInputItem($si_status, true); + $this->toolbar->addInputItem($si_feedback, true); + + //todo: old school here. + include_once "Services/UIComponent/Button/classes/class.ilSubmitButton.php"; + $submit = ilSubmitButton::getInstance(); + $submit->setCaption("filter"); + $submit->setCommand("listTextAssignment"); + $this->toolbar->addButtonInstance($submit); + } + + + /** + * Display list of panels with ALL submissions from this assignment. + */ + function listTextAssignmentObject() + { + $this->initFilter(); + + $button_print = $this->ui_factory->button()->standard($this->lng->txt('print'), "#") + ->withOnLoadCode(function($id) { + return "$('#{$id}').click(function() { window.print(); return false; });"; + }); + $this->toolbar->addSeparator(); + $this->toolbar->addComponent($button_print); + + $title = $this->lng->txt("exc_list_text_assignment").": ".$this->assignment->getTitle(); + + $submission_data = array(); + foreach(ilExSubmission::getAllAssignmentFiles($this->assignment->getExerciseId(), $this->assignment->getId()) as $file) + { + if(trim($file["atext"])) + { + $assignment_data = $this->assignment->getExerciseMemberAssignmentData($file["user_id"], $this->filter["status"]); + if($assignment_data != '') { + $submission_data[] = array_merge($file, $assignment_data); + } + } + } + if(count($submission_data) == 0) + { + $group_panels_tpl = new ilTemplate("tpl.exc_group_report_panels.html", TRUE, TRUE, "Modules/Exercise"); + $group_panels_tpl->setVariable('TITLE', $title); + $mtpl = new ilTemplate("tpl.message.html", true, true, "Services/Utilities"); + $mtpl->setCurrentBlock("info_message"); + $mtpl->setVariable("TEXT", $this->lng->txt("fiter_no_results")); + $mtpl->parseCurrentBlock(); + $report_html = $mtpl->get(); + + $group_panels_tpl->setVariable('CONTENT', $report_html); + $this->tpl->setContent($group_panels_tpl->get()); + } + + $this->showSubmissionPanels($title, self::PANEL_TYPE_SUBMISSION, $submission_data); + } + + + /** + * Display list of panels with submissions from different users. + */ + public function compareTextAssignmentsObject() + { + $this->showSubmissionPanels($this->lng->txt("exc_compare_selected_submissions"), self::PANEL_TYPE_SUBMISSION, $this->submissions_data); + } + + + /** + * Render a panel with the submission report. + * @param $a_type + * @param $a_data + * @return string + * @throws ilDateTimeException + */ + public function getReportPanel(int $a_type, array $a_data) + { + if($a_data['status'] == self::GRADE_NOT_GRADED) { + $str_status_key = $this->lng->txt('exc_tbl_status'); + $str_status_value = $this->lng->txt('not_yet'); + } else { + $str_status_key = $this->lng->txt('exc_tbl_status_time'); + $str_status_value = ilDatePresentation::formatDate(new ilDateTime($a_data["status_time"], IL_CAL_DATETIME)); + } + + if($a_data['feedback_time']) { + $str_evaluation_key = $this->lng->txt('exc_tbl_feedback_time'); + $str_evaluation_value = ilDatePresentation::formatDate(new ilDateTime($a_data["feedback_time"], IL_CAL_DATETIME)); + } else { + $str_evaluation_key = $this->lng->txt('exc_settings_feedback'); + $str_evaluation_value = $this->lng->txt('not_yet'); + } + + $card_content = array( + $this->lng->txt("exc_tbl_submission_date") => ilDatePresentation::formatDate(new ilDateTime($a_data["udate"], IL_CAL_DATETIME)), + $str_status_key => $str_status_value, + $str_evaluation_key => $str_evaluation_value + ); + if($this->displayFeedback($a_type)) + { + $card_content[$this->lng->txt('feedback_given')] = $a_data['fb_given']; + $card_content[$this->lng->txt('feedback_received')] = $a_data['fb_received']; + } + $card_tpl = new ilTemplate("tpl.exc_report_details_card.html", true, true, "Modules/Exercise"); + foreach($card_content as $key => $value) + { + $card_tpl->setCurrentBlock("assingment_card"); + $card_tpl->setVariable("ROW_KEY", $key); + $card_tpl->setVariable("ROW_VALUE", $value); + $card_tpl->parseCurrentBlock(); + } + + $main_panel = $this->ui_factory->panel()->sub($a_data['uname'], $this->ui_factory->legacy($a_data['utext'])) + ->withCard($this->ui_factory->card()->standard($this->lng->txt('text_assignment'))->withSections(array($this->ui_factory->legacy($card_tpl->get())))); + + if($this->displayCardActions($a_type)) + { + $modal = $this->getEvaluationModal($a_data); + + $actions = $this->ui_factory->dropdown()->standard(array( + $this->ui_factory->button()->shy($this->lng->txt("grade_evaluate"), "#")->withOnClick($modal->getShowSignal()), + )); + + $main_panel = $main_panel->withActions($actions); + } + + $feedback_tpl = new ilTemplate("tpl.exc_report_feedback.html", true, true, "Modules/Exercise"); + //if no feedback filter the feedback is displayed. Can be list submissions or compare submissions. + if(array_key_exists("peer", $a_data) && ($this->filter["feedback"] == self::FEEDBACK_FULL_SUBMISSION) || $this->filter["feedback"] == "") + { + $feedback_tpl->setCurrentBlock("feedback"); + foreach($a_data["peer"] as $peer_id) + { + $user = new ilObjUser($peer_id); + $peer_name = $user->getFirstname()." ".$user->getLastname(); + + $feedback_tpl->setCurrentBlock("peer_feedback"); + $feedback_tpl->setVariable("PEER_NAME", $peer_name); + + $submission = new ilExSubmission($this->assignment, $a_data["uid"]); + $values = $submission->getPeerReview()->getPeerReviewValues($peer_id, $a_data["uid"]); + + $review_html = ""; + foreach($this->assignment->getPeerReviewCriteriaCatalogueItems() as $crit) + { + $crit_id = $crit->getId() + ? $crit->getId() + : $crit->getType(); + $crit->setPeerReviewContext($this->assignment, $peer_id, $a_data["uid"]); + + $review_html .= + '
'.$crit->getTitle().'
'. + '
'.$crit->getHTML($values[$crit_id]).'
'; + + } + $feedback_tpl->setVariable("PEER_FEEDBACK", $review_html); + $feedback_tpl->parseCurrentBlock(); + } + $feedback_tpl->parseCurrentBlock(); + } + $feedback_tpl->setVariable("GRADE", $this->lng->txt('grade').": ".$this->lng->txt('exc_'.$a_data['status'])); + $feedback_tpl->setVariable("COMMENT", $this->lng->txt('exc_comment')."
".$a_data['comment']); + + $feedback_panel = $this->ui_factory->panel()->sub("",$this->ui_factory->legacy($feedback_tpl->get())); + + $report = $this->ui_factory->panel()->report("", array($main_panel, $feedback_panel)); + + if($this->displayCardActions($a_type)) + { + return $this->ui_renderer->render([$modal,$report]); + } + + return $this->ui_renderer->render($report); + } + + + /** + * Save assignment submission grade(status) and comment from the roundtrip modal. + */ + public function saveEvaluationFromModalObject() + { + $comment = trim($_POST['comment']); + $user_id = (int)$_POST['mem_id']; + $grade = trim($_POST["grade"]); + $version_id = (int)$_POST['version_id']; + + // versioned/frozen submissions + if ($version_id) + { + //update version + $revision = new ilExSubmissionRevision($this->submission); + $db_grade = $revision->getRevisionStatus($version_id); + $db_comment = $revision->getRevisionComment($version_id); + + if($db_grade !== $grade) { + $revision->updateRevisionStatus($version_id, $grade); + } + if($db_comment !== $comment) { + $revision->updateRevisionComment($version_id, $comment); + } + + ilUtil::sendSuccess($this->lng->txt("exc_version_updated"), true); + $this->ctrl->redirect($this, "showVersions"); + } + // last/current submission + else + { + if ($this->assignment->getId() && $user_id) + { + $member_status = $this->assignment->getMemberStatus($user_id); + $member_status->setComment(ilUtil::stripSlashes($comment)); + $member_status->setStatus($grade); + if ($comment != "") { + $member_status->setFeedback(true); + } + $member_status->update(); + } + ilUtil::sendSuccess($this->lng->txt("exc_status_saved"), true); + $this->ctrl->redirect($this, "listTextAssignment"); + } + } + + + /** + * Returns one modal containing a form where the submission can be graded/evaluated. + * @param $a_data + * @return \ILIAS\UI\Component\Modal\RoundTrip + */ + public function getEvaluationModal($a_data) + { + $modal_tpl = new ilTemplate("tpl.exc_report_evaluation_modal.html", true, true, "Modules/Exercise"); + $modal_tpl->setVariable("USER_NAME",$a_data['uname']); + + //TODO: CHECK ilias string utils. ilUtil shortenText with net blank. + $max_chars = 500; + + //TODO the following show more text does not work properly + + + $u_text = strip_tags($a_data["utext"]); //otherwise will get open P + $text = $u_text; + //show more + if(strlen($u_text) > $max_chars) + { + $text = ""; + $text .= "
"; + $text .= mb_substr($u_text, 0, $max_chars); + $text .= ""; + $text .= mb_substr($u_text, $max_chars); + $text .= "
"; + $text .= ""; + } + $modal_tpl->setVariable("USER_TEXT",$text); + + $form = new ilPropertyFormGUI(); + $form->setFormAction($this->ctrl->getFormAction($this, "saveEvaluationFromModal")); + $form->setId(uniqid('form')); + + //Grade + $options = array( + self::GRADE_NOT_GRADED => $this->lng->txt("exc_notgraded"), + self::GRADE_PASSED => $this->lng->txt("exc_passed"), + self::GRADE_FAILED => $this->lng->txt("exc_failed") + ); + $si = new ilSelectInputGUI($this->lng->txt("exc_tbl_status"), "grade"); + $si->setOptions($options); + $si->setValue($a_data['status']); + $form->addItem($si); + + $item = new ilHiddenInputGUI('mem_id'); + $item->setValue($a_data['uid']); + $form->addItem($item); + + $version = new ilHiddenInputGUI('version_id'); + $version->setValue($a_data['version']); + $form->addItem($version); + + $ta = new ilTextAreaInputGUI($this->lng->txt("exc_comment"), 'comment'); + $ta->setInfo($this->lng->txt("exc_comment_for_learner_info")); + $ta->setValue($a_data['comment']); + $ta->setRows(10); + $form->addItem($ta); + + $modal_tpl->setVariable("FORM",$form->getHTML()); + + $form_id = 'form_' . $form->getId(); + $submit_btn = $this->ui_factory->button()->primary($this->lng->txt("save"), '#') + ->withOnLoadCode(function($id) use ($form_id) { + return "$('#{$id}').click(function() { $('#{$form_id}').submit(); return false; });"; + }); + + return $this->ui_factory->modal()->roundtrip(strtoupper($this->lng->txt("grade_evaluate")), $this->ui_factory->legacy($modal_tpl->get()))->withActionButtons([$submit_btn]); + } + + + /** + * Returns an array with the provided feedback + * @param $a_data array submission data + * @return $data array + */ + public function collectFeedbackDataFromPeer(array $a_data): array + { + $user = new ilObjUser($a_data["user_id"]); + $uname = $user->getFirstname()." ".$user->getLastname(); + + $data = array( + "uid" => $a_data["user_id"], + "uname" => $uname, + "udate" => $a_data["ts"], + "utext" => ilRTE::_replaceMediaObjectImageSrc($a_data["atext"], 1) // mob id to mob src + ); + + //get data peer and assign it + $peer_review = new ilExPeerReview($this->assignment); + $data["peer"] = array(); + foreach($peer_review->getPeerReviewsByPeerId($a_data['user_id']) as $key => $value) + { + $data["peer"][] = $value['giver_id']; + } + + $data["fb_received"] = count($data["peer"]); + $data["fb_given"] = $peer_review->countGivenFeedback(true, $a_data["user_id"]); + + return $data; + } + + + //TODO the confirmation can be moved to ilExerciseManagementGUI + /** + * Confirm create a new version of the submission. + */ + function confirmFreezeSubmissionObject() + { + $user_id = (int)$_GET['usr_id']; + + $cgui = new ilConfirmationGUI(); + $cgui->setFormAction($this->ctrl->getFormAction($this)); + $cgui->setHeaderText($this->lng->txt("exc_msg_sure_to_freeze_submission")); + $cgui->setCancel($this->lng->txt("cancel"), "members"); + $cgui->setConfirm($this->lng->txt("confirm"), "freezeVersion"); + + $cgui->addItem("usr_id", $user_id, + ilUserUtil::getNamePresentation((int) $user_id, false, false, "", true)); + + $this->tpl->setContent($cgui->getHTML()); + } + + /** + * @param $type + * @return bool + */ + function displayCardActions(int $type): bool + { + if($type == self::PANEL_TYPE_SUBMISSION || $type == self::PANEL_TYPE_REVISION) + { + return true; + } + + return false; + } + + /** + * @param int $type + * @return bool + */ + function displayFeedback(int $type): bool + { + if($type == self::PANEL_TYPE_SUBMISSION) + { + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/Modules/Exercise/classes/class.ilExSubmissionRevision.php b/Modules/Exercise/classes/class.ilExSubmissionRevision.php new file mode 100644 index 000000000000..de7382415988 --- /dev/null +++ b/Modules/Exercise/classes/class.ilExSubmissionRevision.php @@ -0,0 +1,265 @@ + + * @ingroup ModulesExercise + */ + +class ilExSubmissionRevision +{ + /** + * @var ilExSubmission + */ + protected $submission; + + /** + * @var int + */ + protected $ass_id; + + /** + * @var int + */ + protected $usr_id; + + /** + * @var ilDBInterface + */ + protected $db; + + + /** + * ilExSubmissionRevision constructor. + * @param ilExSubmission $a_submission + */ + public function __construct(ilExSubmission $a_submission) + { + global $DIC; + + $this->db = $DIC->database(); + $this->submission = $a_submission; + $this->ass_id = $this->submission->getAssignment()->getId(); + $this->usr_id = $this->submission->getUserId(); + } + + + /** + * Store the submission version in the DB. + * The user will be allowed to submit again. + * @return int + */ + public function setVersion() : int + { + $next_version = $this->getLastVersionNumber() + 1; + + $submissions = $this->submission->getSubmissionsByUser(); + + foreach($submissions as $submission) + { + $ass_mem_status = new ilExAssignmentMemberStatus($this->ass_id, $this->submission->getUserId()); + + $next_id = $this->db->nextId('exc_submission_version'); + + $affectedRows = $this->db->manipulateF( + "INSERT INTO exc_submission_version (id, returned_id, obj_id, user_id, filename, filetitle, mimetype, ts, ass_id, atext, late, team_id, status, status_time, mark, u_comment, version, versioned)". + " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)", + array('integer', 'integer', 'integer', 'integer', 'text', 'text', 'text', 'timestamp', 'integer', 'text', 'integer', 'integer', 'text', 'timestamp', 'text', 'text',' integer', 'timestamp'), + array( + $next_id, + $submission['returned_id'], + $submission['obj_id'], + $submission['user_id'], + $submission['filename'], + $submission['filetitle'], + $submission['mimetype'], + $submission['ts'], + $submission['ass_id'], + $submission['atext'], + $submission['late'], + $submission['team_id'], + $ass_mem_status->getStatus(), + $ass_mem_status->getStatusTime(), + $ass_mem_status->getMark(), + $ass_mem_status->getComment(), + $next_version, + ilUtil::now() + ) + ); + } + return $next_version; + } + + + /** + * Get Last submission version number + * @return integer + */ + public function getLastVersionNumber() : int + { + $sql = "SELECT max(version) version". + " FROM exc_submission_version". + " WHERE ass_id = ". + $this->db->quote($this->ass_id, "integer"). + " AND user_id = ". + $this->db->quote($this->usr_id, "integer"); + + $res = $this->db->query($sql); + $row = $this->db->fetchAssoc($res); + + return (int)$row['version']; + } + + + /** + * Compare the submission with the last revision to determine if it was versioned or not. + * @return bool + */ + public function isVersioned() : bool + { + $sql = "SELECT count(r.returned_id) count". + " FROM exc_returned r, exc_submission_version v". + " WHERE r.obj_id = ".$this->submission->getAssignment()->getExerciseId(). + " AND r.ass_id = ".$this->ass_id. + " AND r.user_id = ".$this->usr_id. + " AND r.obj_id = v.obj_id". + " AND r.ass_id = v.ass_id". + " AND r.user_id = v.user_id". + " AND r.ts = v.ts"; + + $res = $this->db->query($sql); + $row = $this->db->fetchAssoc($res); + + return (bool)$row['count']; + } + + /** + * Get all versions for the submission. + * @return array + */ + public function getRevisions() : array + { + $sql = "SELECT * FROM exc_submission_version". + " WHERE obj_id = ".$this->submission->getAssignment()->getExerciseId(). + " AND ass_id = ".$this->ass_id. + " AND user_id = ".$this->usr_id. + " ORDER BY version DESC"; + + $res = $this->db->query($sql); + $data = array(); + while($row = $this->db->fetchAssoc($res)) + { + $data['id'] = $row['id']; + $data['obj_id'] = $row['obj_id']; + $data['user_id'] = $row['user_id']; + $data['udate'] = $row['ts']; + $data['status_time'] = $row['status_time']; + $data['feedback_time'] = $row['feedback_time']; + $data['utext'] = $row['atext']; + $data['ass_id'] = $row['ass_id']; + $data['status'] = $row['status']; + $data['comment'] = $row['u_comment']; + $data['version'] = $row['version']; + $data['versioned'] = $row['versioned']; + $versions[] = $data; + } + return $versions ? $versions : array(); + } + + public function sendNotification() + { + $get_ref = (int)$_GET['ref_id']; + $exc_id = $this->submission->getAssignment()->getExerciseId(); + + if(in_array($get_ref, ilObjExercise::_getAllReferences($exc_id))) + { + $not = new ilExerciseMailNotification(); + $not->setType(ilExerciseMailNotification::TYPE_SUBMISSION_VERSIONED); + $not->setAssignmentId($this->ass_id); + $not->setRefId($get_ref); + $not->setRecipients(array($this->submission->getUserId())); + $not->send(); + } + } + + /** + * Get data from specific revision + * @param integer $id + * @return string|null + */ + public function getRevisionStatus(int $id) + { + $status = null; + + $sql = "SELECT status FROM exc_submission_version". + " WHERE obj_id = ".$this->submission->getAssignment()->getExerciseId(). + " AND ass_id = ".$this->ass_id. + " AND user_id = ".$this->usr_id. + " AND version = ".$id; + + $res = $this->db->query($sql); + while($row = $this->db->fetchAssoc($res)) + { + $status = $row['status']; + } + return $status; + } + + /** + * @param int $id + * @return string|null + */ + public function getRevisionComment(int $id) + { + $comment = null; + + $sql = "SELECT u_comment FROM exc_submission_version". + " WHERE obj_id = ".$this->submission->getAssignment()->getExerciseId(). + " AND ass_id = ".$this->ass_id. + " AND user_id = ".$this->usr_id. + " AND version = ".$id; + + $res = $this->db->query($sql); + while($row = $this->db->fetchAssoc($res)) + { + $comment = $row['u_comment']; + } + return $comment; + } + + /** + * @param int $id + * @param string $comment + */ + public function updateRevisionComment(int $id, string $comment) + { + $query = "UPDATE exc_submission_version". + " SET u_comment = ".$this->db->quote($comment, 'text').", ". + "feedback_time = ".$this->db->quote(ilUtil::now(), 'timestamp'). + " WHERE obj_id = ".$this->submission->getAssignment()->getExerciseId(). + " AND ass_id = ".$this->ass_id. + " AND user_id = ".$this->usr_id. + " AND version = ".$id; + + $res = $this->db->manipulate($query); + } + + /** + * @param int $id + * @param string $status + */ + public function updateRevisionStatus(int $id, string $status) + { + $query = "UPDATE exc_submission_version". + " SET status = ".$this->db->quote($status, 'text').", ". + "status_time = ".$this->db->quote(ilUtil::now(), 'timestamp'). + " WHERE obj_id = ".$this->submission->getAssignment()->getExerciseId(). + " AND ass_id = ".$this->ass_id. + " AND user_id = ".$this->usr_id. + " AND version = ".$id; + + $res = $this->db->manipulate($query); + } +} diff --git a/Modules/Exercise/classes/class.ilExSubmissionTextGUI.php b/Modules/Exercise/classes/class.ilExSubmissionTextGUI.php index 9ee5a9a3e13e..06864cacfce9 100644 --- a/Modules/Exercise/classes/class.ilExSubmissionTextGUI.php +++ b/Modules/Exercise/classes/class.ilExSubmissionTextGUI.php @@ -6,7 +6,7 @@ * * @author Jörg Lützenkirchen * - * @ilCtrl_Calls ilExSubmissionTextGUI: + * @ilCtrl_Calls ilExSubmissionTextGUI: ilExerciseManagementGUI, ilExSubmissionPanelsHandlerGUI * @ingroup ModulesExercise */ class ilExSubmissionTextGUI extends ilExSubmissionBaseGUI @@ -62,8 +62,25 @@ public static function getOverviewContent(ilInfoScreenGUI $a_info, ilExSubmissio $lng = $DIC->language(); $ilCtrl = $DIC->ctrl(); - - if($a_submission->canSubmit()) + + if($a_submission->isVersioned()) + { + $btn_show = ilLinkButton::getInstance(); + $btn_show->setCaption("exc_btn_show_submissions"); + $ilCtrl->setParameterByClass("ilObjExerciseGUI", 'ass_id', $a_submission->getAssignment()->getId()); + $ilCtrl->setParameterByClass("ilObjExerciseGUI", "vw", ilExerciseManagementGUI::VIEW_ASSIGNMENT); + $btn_show->setUrl($ilCtrl->getLinkTargetByClass(array("ilExerciseHandlerGUI", "ilObjExerciseGUI", "ilExerciseManagementGUI", "ilExSubmissionPanelsHandlerGUI"), "showVersions")); + $ilCtrl->setParameterByClass("ilObjExerciseGUI", 'ass_id', ""); + $ilCtrl->setParameterByClass("ilObjExerciseGUI", "vw", ""); + + + $btn_revise = ilLinkButton::getInstance(); + $btn_revise->setPrimary(true); + $btn_revise->setCaption("exc_btn_revise_submission"); + $btn_revise->setUrl($ilCtrl->getLinkTargetByClass(array("ilExSubmissionGUI", "ilExSubmissionTextGUI"), "editAssignmentText")); + $files_str = $btn_show->render()." ".$btn_revise->render(); + } + elseif($a_submission->canSubmit()) { $button = ilLinkButton::getInstance(); $button->setPrimary(true); @@ -199,6 +216,7 @@ function editAssignmentTextObject(ilPropertyFormGUI $a_form = null) { $a_form = $this->initAssignmentTextForm(); + //TODO get last revision/submission files. $files = $this->submission->getFiles(); if($files) { @@ -243,11 +261,19 @@ function updateAssignmentTextObject($a_return = false) $text = trim($form->getInput("atxt")); $existing = $this->submission->getFiles(); - - $returned_id = $this->submission->updateTextSubmission( + + //If versioned, new DB entry + if($this->submission->isVersioned() && $existing[0]['versioned']) + { + $returned_id = $this->submission->addResourceObject("TEXT", $text); + } + else + { + $returned_id = $this->submission->updateTextSubmission( // mob src to mob id - ilRTE::_replaceMediaObjectImageSrc($text, 0)); - + ilRTE::_replaceMediaObjectImageSrc($text, 0)); + } + // no empty text if($returned_id) { diff --git a/Modules/Exercise/classes/class.ilExerciseMailNotification.php b/Modules/Exercise/classes/class.ilExerciseMailNotification.php index e9aaf443c15c..64c711be247f 100644 --- a/Modules/Exercise/classes/class.ilExerciseMailNotification.php +++ b/Modules/Exercise/classes/class.ilExerciseMailNotification.php @@ -19,6 +19,7 @@ class ilExerciseMailNotification extends ilMailNotification const TYPE_FEEDBACK_FILE_ADDED = 20; const TYPE_SUBMISSION_UPLOAD = 30; const TYPE_FEEDBACK_TEXT_ADDED = 40; + const TYPE_SUBMISSION_VERSIONED = 50; /** * @@ -184,6 +185,38 @@ public function send() $this->sendMail(array($rcp),array('system')); } break; + + case self::TYPE_SUBMISSION_VERSIONED: + + foreach($this->getRecipients() as $rcp) + { + $this->initLanguage($rcp); + $this->initMail(); + $this->setSubject( + sprintf($this->getLanguageText('exc_notification_submission_versioned'), + $this->getObjectTitle(true)) + ); + $this->setBody(ilMail::getSalutation($rcp,$this->getLanguage())); + $this->appendBody("\n\n"); + $this->appendBody( + $this->getLanguageText('exc_notification_submission_can_resend')); + $this->appendBody("\n"); + $this->appendBody( + $this->getLanguageText('obj_exc').": ".$this->getObjectTitle(true)); + $this->appendBody("\n"); + $this->appendBody( + $this->getLanguageText('exc_assignment').": ". + ilExAssignment::lookupTitle($this->getAssignmentId())); + $this->appendBody("\n\n"); + $this->appendBody($this->getLanguageText('exc_mail_permanent_link')); + $this->appendBody("\n"); + $this->appendBody($this->createPermanentLink(array(), '_'.$this->getAssignmentId()). + '#fb'.$this->getAssignmentId()); + $this->getMail()->appendInstallationSignature(true); + + $this->sendMail(array($rcp),array('system')); + } + break; } return true; } diff --git a/Modules/Exercise/classes/class.ilExerciseManagementGUI.php b/Modules/Exercise/classes/class.ilExerciseManagementGUI.php index be15aeeff7c8..f34c895578a1 100644 --- a/Modules/Exercise/classes/class.ilExerciseManagementGUI.php +++ b/Modules/Exercise/classes/class.ilExerciseManagementGUI.php @@ -11,7 +11,7 @@ * * @ilCtrl_Calls ilExerciseManagementGUI: ilFileSystemGUI, ilRepositorySearchGUI * @ilCtrl_Calls ilExerciseManagementGUI: ilExSubmissionTeamGUI, ilExSubmissionFileGUI -* @ilCtrl_Calls ilExerciseManagementGUI: ilExSubmissionTextGUI, ilExPeerReviewGUI +* @ilCtrl_Calls ilExerciseManagementGUI: ilExSubmissionTextGUI, ilExPeerReviewGUI, ilExSubmissionPanelsHandlerGUI * * @ingroup ModulesExercise */ @@ -47,11 +47,6 @@ class ilExerciseManagementGUI */ protected $ui_renderer; - /** - * @var array - */ - protected $filter; - /** * @var ilToolbarGUI */ @@ -66,12 +61,6 @@ class ilExerciseManagementGUI const VIEW_PARTICIPANT = 2; const VIEW_GRADES = 3; - const FEEDBACK_ONLY_SUBMISSION = "submission_feedback"; - const FEEDBACK_FULL_SUBMISSION = "submission_only"; - - const GRADE_NOT_GRADED = "notgraded"; - const GRADE_PASSED = "passed"; - const GRADE_FAILED = "failed"; /** * Constructor @@ -211,7 +200,11 @@ public function executeCommand() $gui = new ilExPeerReviewGUI($this->assignment, $this->initSubmission()); $ilCtrl->forwardCommand($gui); break; - + case "ilexsubmissionpanelshandlergui": + $this->ctrl->setReturn($this, "members"); + $gui = new ilExSubmissionPanelsHandlerGUI($this->assignment, (int)$_GET["member_id"]); + $ilCtrl->forwardCommand($gui); + break; default: $cmd = $ilCtrl->getCmd(); switch($cmd) @@ -418,9 +411,12 @@ function membersObject() { $ass_type = $this->assignment->getType(); //todo change addFormButton for addButtonInstance + $this->ctrl->setParameterByClass("ilExSubmissionPanelsHandlerGUI", "vw", self::VIEW_GRADES); if($ass_type == ilExAssignment::TYPE_TEXT) { - $ilToolbar->addFormButton($lng->txt("exc_list_text_assignment"), "listTextAssignment"); + $ilToolbar->addButton($this->lng->txt("exc_list_text_assignment"), + $this->ctrl->getLinkTargetByClass("ilExSubmissionPanelsHandlerGUI", "listTextAssignment")); } + $this->ctrl->setParameterByClass("ilExSubmissionPanelsHandlerGUI", "vw", ""); $ilToolbar->addFormButton($lng->txt("download_all_returned_files"), "downloadSubmissions"); } $this->ctrl->setParameter($this, "vw", self::VIEW_ASSIGNMENT); @@ -508,247 +504,29 @@ function saveGradesObject() // TEXT ASSIGNMENT ?! - /** - * todo: Pagination. - */ - function listTextAssignmentObject() + /*function listTextAssignmentObject() { - $this->initFilter(); - - //tabs - $this->tabs_gui->clearTargets(); - $this->tabs_gui->setBackTarget($this->lng->txt("back"), - $this->ctrl->getLinkTarget($this, "members")); - - $button_print = $this->ui_factory->button()->standard($this->lng->txt('print'), "#") - ->withOnLoadCode(function($id) { - return "$('#{$id}').click(function() { window.print(); return false; });"; - }); - $this->toolbar->addSeparator(); - $this->toolbar->addComponent($button_print); - - //retrieve data - $peer_review = new ilExPeerReview($this->assignment); - $peer_data = $peer_review->getAllPeerReviews(); - - include_once "Services/User/classes/class.ilUserUtil.php"; - include_once "Services/RTE/classes/class.ilRTE.php"; - - $report_html = ""; - //TODO create proper title. - $report_title = $this->lng->txt("exc_list_text_assignment").": ".$this->assignment->getTitle(); - $report_html .= "

".$report_title."

"; - $total_reports = 0; - foreach(ilExSubmission::getAllAssignmentFiles($this->assignment->getExerciseId(), $this->assignment->getId()) as $file) - { - if(trim($file["atext"])) - { - $user = new ilObjUser($file["user_id"]); - $uname = $user->getFirstname()." ".$user->getLastname(); - $data = array( - "uid" => $file["user_id"], - "uname" => $uname, - "udate" => $file["ts"], - "utext" => ilRTE::_replaceMediaObjectImageSrc($file["atext"], 1) // mob id to mob src - ); - - if(isset($peer_data[$file["user_id"]])) - { - $data["peer"] = array_keys($peer_data[$file["user_id"]]); - } - - $data["fb_received"] = count($data["peer"]); - $data["fb_given"] = $peer_review->countGivenFeedback(true, $file["user_id"]); - - $submission_data = $this->assignment->getExerciseMemberAssignmentData($file["user_id"], $this->filter["status"]); - - if(is_array($submission_data)) - { - $data = array_merge($data, $submission_data); - $report_html .= $this->getReportPanel($data); - $total_reports++; - - } - } - } - if($total_reports == 0) - { - $mtpl = new ilTemplate("tpl.message.html", true, true, "Services/Utilities"); - $mtpl->setCurrentBlock("info_message"); - $mtpl->setVariable("TEXT", $this->lng->txt("fiter_no_results")); - $mtpl->parseCurrentBlock(); - $report_html .= $mtpl->get(); - } - - $this->tpl->setContent($report_html); - } - - public function getReportPanel($a_data) - { - $modal = $this->getEvaluationModal($a_data); - - $actions = $this->ui_factory->dropdown()->standard(array( - $this->ui_factory->button()->shy($this->lng->txt("grade_evaluate"), "#")->withOnClick($modal->getShowSignal()), - )); - - if($a_data['status'] == self::GRADE_NOT_GRADED) { - $str_status_key = $this->lng->txt('exc_tbl_status'); - $str_status_value = $this->lng->txt('not_yet'); - } else { - $str_status_key = $this->lng->txt('exc_tbl_status_time'); - $str_status_value = ilDatePresentation::formatDate(new ilDateTime($a_data["status_time"], IL_CAL_DATETIME)); - } - - if($a_data['feedback_time']) { - $str_evaluation_key = $this->lng->txt('exc_tbl_feedback_time'); - $str_evaluation_value = ilDatePresentation::formatDate(new ilDateTime($a_data["feedback_time"], IL_CAL_DATETIME)); - } else { - $str_evaluation_key = $this->lng->txt('exc_settings_feedback'); - $str_evaluation_value = $this->lng->txt('not_yet'); - } - - $card_content = array( - $this->lng->txt("exc_tbl_submission_date") => ilDatePresentation::formatDate(new ilDateTime($a_data["udate"], IL_CAL_DATETIME)), - $str_status_key => $str_status_value, - $str_evaluation_key => $str_evaluation_value, - $this->lng->txt('feedback_given') => $a_data['fb_given'], - $this->lng->txt('feedback_received') => $a_data['fb_received'] - ); - $card_tpl = new ilTemplate("tpl.exc_report_details_card.html", true, true, "Modules/Exercise"); - foreach($card_content as $key => $value) - { - $card_tpl->setCurrentBlock("assingment_card"); - $card_tpl->setVariable("ROW_KEY", $key); - $card_tpl->setVariable("ROW_VALUE", $value); - $card_tpl->parseCurrentBlock(); - } - - $main_panel = $this->ui_factory->panel()->sub($a_data['uname'], $this->ui_factory->legacy($a_data['utext'])) - ->withCard($this->ui_factory->card()->standard($this->lng->txt('text_assignment'))->withSections(array($this->ui_factory->legacy($card_tpl->get()))))->withActions($actions); - - $feedback_tpl = new ilTemplate("tpl.exc_report_feedback.html", true, true, "Modules/Exercise"); - if(array_key_exists("peer", $a_data) && $this->filter["feedback"] == "submission_feedback") - { - $feedback_tpl->setCurrentBlock("feedback"); - foreach($a_data["peer"] as $peer_id) - { - $user = new ilObjUser($peer_id); - $peer_name = $user->getFirstname()." ".$user->getLastname(); - - $feedback_tpl->setCurrentBlock("peer_feedback"); - $feedback_tpl->setVariable("PEER_NAME", $peer_name); - - $submission = new ilExSubmission($this->assignment, $a_data["uid"]); - $values = $submission->getPeerReview()->getPeerReviewValues($peer_id, $a_data["uid"]); - - $review_html = ""; - foreach($this->assignment->getPeerReviewCriteriaCatalogueItems() as $crit) - { - $crit_id = $crit->getId() - ? $crit->getId() - : $crit->getType(); - $crit->setPeerReviewContext($this->assignment, $peer_id, $a_data["uid"]); - - $review_html .= - '
'.$crit->getTitle().'
'. - '
'.$crit->getHTML($values[$crit_id]).'
'; - - } - $feedback_tpl->setVariable("PEER_FEEDBACK", $review_html); - $feedback_tpl->parseCurrentBlock(); - } - $feedback_tpl->parseCurrentBlock(); - } - $feedback_tpl->setVariable("GRADE", $this->lng->txt('grade').": ".$this->lng->txt('exc_'.$a_data['status'])); - $feedback_tpl->setVariable("COMMENT", $this->lng->txt('exc_comment')."
".$a_data['comment']); - - $feedback_panel = $this->ui_factory->panel()->sub("",$this->ui_factory->legacy($feedback_tpl->get())); + $panels_gui = new ilExSubmissionPanelsHandlerGUI($this->assignment); + $this->ctrl->redirect($panels_gui, "listTextAssignment"); + }*/ - $report = $this->ui_factory->panel()->report("", array($main_panel, $feedback_panel)); - - return $this->ui_renderer->render([$modal,$report]); - } - - public function getEvaluationModal($a_data) + public function compareTextAssignmentsObject() { - $modal_tpl = new ilTemplate("tpl.exc_report_evaluation_modal.html", true, true, "Modules/Exercise"); - $modal_tpl->setVariable("USER_NAME",$a_data['uname']); - - //TODO: CHECK ilias string utils. ilUtil shortenText with net blank. - $max_chars = 500; - - $u_text = strip_tags($a_data["utext"]); //otherwise will get open P - $text = $u_text; - //show more - if(strlen($u_text) > $max_chars) + $participants = array_keys($this->getMultiActionUserIds()); + foreach($participants as $participant_id) { - $text = ""; - $text .= "
"; - $text .= mb_substr($u_text, 0, $max_chars); - $text .= ""; - $text .= mb_substr($u_text, $max_chars); - $text .= "
"; - $text .= ""; + $submission = new ilExSubmission($this->assignment,$participant_id); + $file = reset($submission->getFiles()); + $assignment_data = $this->assignment->getExerciseMemberAssignmentData($file["user_id"], $this->filter["status"]); + $submission_data[] = array_merge($file,$assignment_data); } - $modal_tpl->setVariable("USER_TEXT",$text); + $panel_handler = new ilExSubmissionPanelsHandlerGUI($this->assignment); + $panel_handler->setSubmissionsData($submission_data); - $form = new ilPropertyFormGUI(); - $form->setFormAction($this->ctrl->getFormAction($this, "saveEvaluationFromModal")); - $form->setId(uniqid('form')); - - //Grade - $options = array( - self::GRADE_NOT_GRADED => $this->lng->txt("exc_notgraded"), - self::GRADE_PASSED => $this->lng->txt("exc_passed"), - self::GRADE_FAILED => $this->lng->txt("exc_failed") - ); - $si = new ilSelectInputGUI($this->lng->txt("exc_tbl_status"), "grade"); - $si->setOptions($options); - $si->setValue($a_data['status']); - $form->addItem($si); - - $item = new ilHiddenInputGUI('mem_id'); - $item->setValue($a_data['uid']); - $form->addItem($item); - - $ta = new ilTextAreaInputGUI($this->lng->txt("exc_comment"), 'comment'); - $ta->setInfo($this->lng->txt("exc_comment_for_learner_info")); - $ta->setValue($a_data['comment']); - $ta->setRows(10); - $form->addItem($ta); - - $modal_tpl->setVariable("FORM",$form->getHTML()); - - $form_id = 'form_' . $form->getId(); - $submit_btn = $this->ui_factory->button()->primary($this->lng->txt("save"), '#') - ->withOnLoadCode(function($id) use ($form_id) { - return "$('#{$id}').click(function() { $('#{$form_id}').submit(); return false; });"; - }); - - return $this->ui_factory->modal()->roundtrip(strtoupper($this->lng->txt("grade_evaluate")), $this->ui_factory->legacy($modal_tpl->get()))->withActionButtons([$submit_btn]); + $this->ctrl->setCmdClass("ilExSubmissionPanelsHandlerGUI"); + $this->ctrl->forwardCommand($panel_handler); } - /** - * Save assignment submission grade(status) and comment from the roundtrip modal. - */ - public function saveEvaluationFromModalObject() - { - $comment = trim($_POST['comment']); - $user_id = (int)$_POST['mem_id']; - $grade = trim($_POST["grade"]); - - if($this->assignment->getId() && $user_id) { - $member_status = $this->assignment->getMemberStatus($user_id); - $member_status->setComment(ilUtil::stripSlashes($comment)); - $member_status->setStatus($grade); - if($comment != "") { - $member_status->setFeedback(true); - } - $member_status->update(); - } - ilUtil::sendSuccess($this->lng->txt("exc_status_saved"), true); - $this->ctrl->redirect($this, "listTextAssignment"); - } /** * Add user as member @@ -2116,49 +1894,43 @@ protected function setIndividualDeadlineObject() } } - function initFilter() + /** + * Versioning + */ + + /** + * Freeze one submission, update and assign version number. + * Example in: Exercise Submissions table. (Submissions and Grades -> Assignment View tab) + */ + public function freezeVersionObject() { - if($_POST["filter_status"]) { - $this->filter["status"] = trim(ilUtil::stripSlashes($_POST["filter_status"])); - } + $user_id = (int)$_POST['usr_id']; - if($_POST["filter_feedback"]) { - $this->filter["feedback"] = trim(ilUtil::stripSlashes($_POST["filter_feedback"])); - } else { - $this->filter["feedback"] = "submission_feedback"; - } + $submission = new ilExSubmission($this->assignment, $user_id); + $revision = new ilExSubmissionRevision($submission); + $revision->setVersion(); + $revision->sendNotification(); + ilUtil::sendSuccess($this->lng->txt("exc_submission_versioned_ok"), true); + $this->ctrl->redirect($this, "members"); + } - $this->lng->loadLanguageModule("search"); +//TODO the confirmation can be moved to ilExerciseManagementGUI + /** + * Confirm create a new version of the submission. + */ + function confirmFreezeSubmissionObject() + { + $user_id = (int)$_GET['member_id']; - $this->toolbar->setFormAction($this->ctrl->getFormAction($this, "listTextAssignment")); + $cgui = new ilConfirmationGUI(); + $cgui->setFormAction($this->ctrl->getFormAction($this)); + $cgui->setHeaderText($this->lng->txt("exc_msg_sure_to_freeze_submission")); + $cgui->setCancel($this->lng->txt("cancel"), "members"); + $cgui->setConfirm($this->lng->txt("confirm"), "freezeVersion"); - $si_status = new ilSelectInputGUI($this->lng->txt("exc_tbl_status"), "filter_status"); - $options = array( - "" => $this->lng->txt("search_any"), - self::GRADE_NOT_GRADED => $this->lng->txt("exc_notgraded"), - self::GRADE_PASSED => $this->lng->txt("exc_passed"), - self::GRADE_FAILED => $this->lng->txt("exc_failed") - ); - $si_status->setOptions($options); - $si_status->setValue($this->filter["status"]); + $cgui->addItem("usr_id", $user_id, + ilUserUtil::getNamePresentation((int) $user_id, false, false, "", true)); - $si_feedback = new ilSelectInputGUI($this->lng->txt("feedback"), "filter_feedback"); - $options = array( - "submission_feedback" => $this->lng->txt("submissions_feedback"), - "submission_only" => $this->lng->txt("submissions_only") - ); - $si_feedback->setOptions($options); - $si_feedback->setValue($this->filter["feedback"]); - - $this->toolbar->addInputItem($si_status, true); - $this->toolbar->addInputItem($si_feedback, true); - - //todo: old school here. - include_once "Services/UIComponent/Button/classes/class.ilSubmitButton.php"; - $submit = ilSubmitButton::getInstance(); - $submit->setCaption("filter"); - $submit->setCommand("listTextAssignment"); - $this->toolbar->addButtonInstance($submit); + $this->tpl->setContent($cgui->getHTML()); } -} - +} \ No newline at end of file diff --git a/Modules/Exercise/classes/class.ilExerciseMemberTableGUI.php b/Modules/Exercise/classes/class.ilExerciseMemberTableGUI.php index 8e042feafeb4..8f2faad5b99b 100644 --- a/Modules/Exercise/classes/class.ilExerciseMemberTableGUI.php +++ b/Modules/Exercise/classes/class.ilExerciseMemberTableGUI.php @@ -267,6 +267,12 @@ protected function parseModeColumns() $cols["calc_deadline"] = array($this->lng->txt("exc_tbl_calculated_deadline"), "calc_deadline"); } + if($this->ass->isVersionable()) + { + $cols["version"] = array($this->lng->txt("version"), "version"); + $this->cols_mandatory[] = "version"; + } + return $cols; } diff --git a/Modules/Exercise/classes/class.ilExerciseSubmissionTableGUI.php b/Modules/Exercise/classes/class.ilExerciseSubmissionTableGUI.php index d4d718b2dc85..f262efc31426 100644 --- a/Modules/Exercise/classes/class.ilExerciseSubmissionTableGUI.php +++ b/Modules/Exercise/classes/class.ilExerciseSubmissionTableGUI.php @@ -33,7 +33,7 @@ abstract class ilExerciseSubmissionTableGUI extends ilTable2GUI protected $cols_mandatory = array("name", "status"); protected $cols_default = array("login", "submission_date", "idl", "calc_deadline"); protected $cols_order = array("image", "name", "login", "team_members", - "sent_time", "submission", "calc_deadline", "idl", "status", "mark", "status_time", + "sent_time", "submission", "calc_deadline", "idl", "version", "status", "mark", "status_time", "feedback_time", "comment", "notice"); /** @@ -53,8 +53,6 @@ function __construct($a_parent_obj, $a_parent_cmd, ilObjExercise $a_exc, $a_item $this->access = $DIC->access(); $this->tpl = $DIC["tpl"]; $this->lng = $DIC->language(); - - $ilCtrl = $DIC->ctrl(); $this->exc = $a_exc; @@ -97,6 +95,11 @@ function __construct($a_parent_obj, $a_parent_cmd, ilObjExercise $a_exc, $a_item // multi actions $this->addMultiCommand("saveStatusSelected", $this->lng->txt("exc_save_selected")); + + if($this->mode == self::MODE_BY_ASSIGNMENT && $this->ass->getType() == ilExAssignment::TYPE_TEXT) + { + $this->addMultiCommand("compareTextAssignments",$this->lng->txt("exc_compare_submissions")); + } $this->setFormName("ilExcIDlForm"); @@ -203,8 +206,7 @@ protected function parseColumns() { $cols = $this->parseModeColumns(); - $cols["submission"] = array($this->lng->txt("exc_tbl_submission_date"), "submission"); - + $cols["submission"] = array($this->lng->txt("exc_tbl_submission_date"), "submission"); $cols["status"] = array($this->lng->txt("exc_tbl_status"), "status"); $cols["mark"] = array($this->lng->txt("exc_tbl_mark"), "mark"); $cols["status_time"] = array($this->lng->txt("exc_tbl_status_time"), "status_time"); @@ -223,7 +225,7 @@ protected function parseColumns() $cols["comment"] = array($this->lng->txt("exc_tbl_comment"), "comment"); } - $cols["notice"] = array($this->lng->txt("exc_tbl_notice"), "note"); + $cols["notice"] = array($this->lng->txt("exc_tbl_notice"), "note"); return $cols; } @@ -235,12 +237,11 @@ protected function parseRow($a_user_id, ilExAssignment $a_ass, array $a_row) $has_no_team_yet = ($a_ass->hasTeam() && !ilExAssignmentTeam::getTeamId($a_ass->getId(), $a_user_id)); - - + // static columns if($this->mode == self::MODE_BY_ASSIGNMENT) - { + { if(!$a_ass->hasTeam()) { $this->tpl->setVariable("VAL_NAME", $a_row["name"]); @@ -252,6 +253,7 @@ protected function parseRow($a_user_id, ilExAssignment $a_ass, array $a_row) $this->tpl->setCurrentBlock('access_warning'); $this->tpl->setVariable('PARENT_ACCESS', $info[0]["text"]); $this->tpl->parseCurrentBlock(); + } } else @@ -300,6 +302,19 @@ protected function parseRow($a_user_id, ilExAssignment $a_ass, array $a_row) $this->tpl->setVariable("TXT_TEAM_INFO", "(".$a_row["submission_obj"]->getTeam()->getId().")"); } } + //revisions data + if($a_ass->isVersionable()) + { + $revision = new ilExSubmissionRevision($a_row['submission_obj']); + $number_of_revisions = $revision->getLastVersionNumber(); + $is_submission_versioned = $revision->isVersioned(); + + if($number_of_revisions) { + $this->tpl->setVariable("VAL_VERSION", $number_of_revisions); + } else { + $this->tpl->setVariable("VAL_VERSION", ""); + } + } } else { @@ -357,7 +372,7 @@ protected function parseRow($a_user_id, ilExAssignment $a_ass, array $a_row) // selectable columns - + foreach($this->getSelectedColumns() as $col) { switch($col) @@ -471,9 +486,7 @@ protected function parseRow($a_user_id, ilExAssignment $a_ass, array $a_row) } } - // actions - include_once "Services/UIComponent/AdvancedSelectionList/classes/class.ilAdvancedSelectionListGUI.php"; $actions = new ilAdvancedSelectionListGUI(); $actions->setId($a_ass->getId()."_".$a_user_id); @@ -502,6 +515,39 @@ protected function parseRow($a_user_id, ilExAssignment $a_ass, array $a_row) ); } } + + if($a_ass->isVersionable() && $this->mode == self::MODE_BY_ASSIGNMENT) + { + $revision = new ilExSubmissionRevision($a_row['submission_obj']); + $number_of_revisions = $revision->getLastVersionNumber(); + $is_submission_versioned = $revision->isVersioned(); + + if($number_of_revisions) { + $this->tpl->setVariable("VAL_VERSION", $number_of_revisions); + } else { + $this->tpl->setVariable("VAL_VERSION", ""); + } + + $ilCtrl->setParameterByClass("ilExSubmissionPanelsHandlerGUI", "ass_id", $a_ass->getId()); + + if(!$is_submission_versioned && $a_row['submission_obj']->hasSubmitted()) + { + $actions->addItem( + $this->lng->txt("exc_tbl_freeze_version"), + "", + $ilCtrl->getLinkTargetByClass("ilExerciseManagementGUI", "confirmFreezeSubmission") + ); + } + + if($number_of_revisions) + { + $actions->addItem( + $this->lng->txt("exc_tbl_show_frozen_versions"), + "", + $ilCtrl->getLinkTargetByClass("ilExSubmissionPanelsHandlerGUI", "showVersions") + ); + } + } if(!$has_no_team_yet && $a_ass->hasActiveIDl() && @@ -608,7 +654,7 @@ protected function parseRow($a_user_id, ilExAssignment $a_ass, array $a_row) "", $ilCtrl->getLinkTargetByClass("ilExSubmissionTeamGUI", "showTeamLog") ); - } + } $this->tpl->setVariable("ACTIONS", $actions->getHTML()); } @@ -627,4 +673,5 @@ public function render() return parent::render(). implode("\n", $this->comment_modals); } + } \ No newline at end of file diff --git a/Modules/Exercise/classes/class.ilObjExerciseAdministrationGUI.php b/Modules/Exercise/classes/class.ilObjExerciseAdministrationGUI.php index 75735f321255..18b4342d94c7 100644 --- a/Modules/Exercise/classes/class.ilObjExerciseAdministrationGUI.php +++ b/Modules/Exercise/classes/class.ilObjExerciseAdministrationGUI.php @@ -22,12 +22,12 @@ class ilObjExerciseAdministrationGUI extends ilObjectGUI */ public function __construct($a_data, $a_id, $a_call_by_reference = true, $a_prepare_output = true) { - global $DIC; - - $this->lng = $DIC->language(); - $this->settings = $DIC->settings(); - $this->ctrl = $DIC->ctrl(); - $this->access = $DIC->access(); + global $DIC; + + $this->lng = $DIC->language(); + $this->settings = $DIC->settings(); + $this->ctrl = $DIC->ctrl(); + $this->access = $DIC->access(); $this->type = "excs"; parent::__construct($a_data, $a_id, $a_call_by_reference, $a_prepare_output); @@ -97,8 +97,8 @@ public function getAdminTabs() */ public function editSettings($a_form = null) { - $lng = $this->lng; - $ilSetting = $this->settings; + $lng = $this->lng; + $ilSetting = $this->settings; $this->tabs_gui->setTabActive('settings'); @@ -115,7 +115,7 @@ public function editSettings($a_form = null) */ public function saveSettings() { - $ilCtrl = $this->ctrl; + $ilCtrl = $this->ctrl; $this->checkPermission("write"); @@ -124,6 +124,7 @@ public function saveSettings() { $exc_set = new ilSetting("excs"); $exc_set->set("add_to_pd", (bool)$form->getInput("pd")); + $exc_set->set("enable_versioning", (bool)$form->getInput("ev")); ilUtil::sendSuccess($this->lng->txt("settings_saved"),true); $ilCtrl->redirect($this, "editSettings"); @@ -138,7 +139,7 @@ public function saveSettings() */ public function cancel() { - $ilCtrl = $this->ctrl; + $ilCtrl = $this->ctrl; $ilCtrl->redirect($this, "view"); } @@ -150,8 +151,7 @@ public function cancel() */ protected function initFormSettings() { - $lng = $this->lng; - $ilAccess = $this->access; + $lng = $this->lng; include_once('Services/Form/classes/class.ilPropertyFormGUI.php'); $form = new ilPropertyFormGUI(); @@ -171,6 +171,11 @@ protected function initFormSettings() $pd->setChecked($exc_set->get("add_to_pd", true)); $form->addItem($pd); + $pd = new ilCheckboxInputGUI($lng->txt("exc_enable_versioning"), "ev"); + $pd->setInfo($lng->txt("exc_enable_versioning_info")); + $pd->setChecked($exc_set->get("enable_versioning", false)); + $form->addItem($pd); + return $form; } } diff --git a/Modules/Exercise/classes/class.ilObjExerciseGUI.php b/Modules/Exercise/classes/class.ilObjExerciseGUI.php index a2f77e15928c..f928d9cd007e 100755 --- a/Modules/Exercise/classes/class.ilObjExerciseGUI.php +++ b/Modules/Exercise/classes/class.ilObjExerciseGUI.php @@ -16,7 +16,7 @@ * @ilCtrl_Calls ilObjExerciseGUI: ilObjectCopyGUI, ilExportGUI * @ilCtrl_Calls ilObjExerciseGUI: ilCommonActionDispatcherGUI, ilCertificateGUI * @ilCtrl_Calls ilObjExerciseGUI: ilExAssignmentEditorGUI, ilExSubmissionGUI -* @ilCtrl_Calls ilObjExerciseGUI: ilExerciseManagementGUI, ilExcCriteriaCatalogueGUI +* @ilCtrl_Calls ilObjExerciseGUI: ilExerciseManagementGUI, ilExcCriteriaCatalogueGUI, ilExSubmissionPanelsHandlerGUI * * @ingroup ModulesExercise */ @@ -185,7 +185,7 @@ function executeCommand() $crit_gui = new ilExcCriteriaCatalogueGUI($this->object); $this->ctrl->forwardCommand($crit_gui); break; - + default: if(!$cmd) { diff --git a/Modules/Exercise/templates/default/tpl.exc_group_report_panels.html b/Modules/Exercise/templates/default/tpl.exc_group_report_panels.html new file mode 100644 index 000000000000..28c1e829a0f7 --- /dev/null +++ b/Modules/Exercise/templates/default/tpl.exc_group_report_panels.html @@ -0,0 +1,4 @@ +

+{TITLE} +

+{CONTENT} \ No newline at end of file diff --git a/Modules/Exercise/templates/default/tpl.exc_members_row.html b/Modules/Exercise/templates/default/tpl.exc_members_row.html index c6413c8fb4e9..3fa90932322f 100755 --- a/Modules/Exercise/templates/default/tpl.exc_members_row.html +++ b/Modules/Exercise/templates/default/tpl.exc_members_row.html @@ -101,6 +101,12 @@ {VAL_IDL} + + + + {VAL_VERSION} + + diff --git a/Modules/LearningModule/classes/class.ilLMPage.php b/Modules/LearningModule/classes/class.ilLMPage.php index e6bb85544870..daaaaf6ada2c 100644 --- a/Modules/LearningModule/classes/class.ilLMPage.php +++ b/Modules/LearningModule/classes/class.ilLMPage.php @@ -55,6 +55,21 @@ function beforePageContentUpdate($a_page_content) } } + /** + * After update content send notifications. + */ + function afterUpdate() + { + $references = ilObject::_getAllReferences($this->getParentId()); + $notification = new ilLearningModuleNotification( + ilLearningModuleNotification::ACTION_UPDATE, + ilNotification::TYPE_LM_PAGE, + new ilObjLearningModule(reset($references)), + $this->getId()); + + $notification->send(); + } + } ?> diff --git a/Modules/LearningModule/classes/class.ilLMPresentationGUI.php b/Modules/LearningModule/classes/class.ilLMPresentationGUI.php index c9be0a203a4f..82f90f6e75b6 100755 --- a/Modules/LearningModule/classes/class.ilLMPresentationGUI.php +++ b/Modules/LearningModule/classes/class.ilLMPresentationGUI.php @@ -246,6 +246,28 @@ function executeCommand() break; default: + if($_GET["ntf"]) + { + switch($_GET["ntf"]) + { + case 1: + ilNotification::setNotification(ilNotification::TYPE_LM, $this->user->getId(), $this->lm->getId(), false); + break; + + case 2: + ilNotification::setNotification(ilNotification::TYPE_LM, $this->user->getId(), $this->lm->getId(), true); + break; + + case 3: + ilNotification::setNotification(ilNotification::TYPE_LM_PAGE, $this->user->getId(), $this->getCurrentPageId(), false); + break; + + case 4: + ilNotification::setNotification(ilNotification::TYPE_LM_PAGE, $this->user->getId(), $this->getCurrentPageId(), true); + break; + } + $ilCtrl->redirect($this,"layout"); + } $ret = $this->$cmd(); break; } @@ -1054,7 +1076,12 @@ function addHeaderAction($a_redraw = false) { $ilAccess = $this->access; $tpl = $this->tpl; - + + $lm_id = $this->lm->getId(); + $pg_id = $this->getCurrentPageId(); + + $this->lng->loadLanguageModule("content"); + include_once "Services/Object/classes/class.ilCommonActionDispatcherGUI.php"; $dispatcher = new ilCommonActionDispatcherGUI(ilCommonActionDispatcherGUI::TYPE_REPOSITORY, $ilAccess, $this->lm->getType(), $_GET["ref_id"], $this->lm->getId()); @@ -1074,7 +1101,49 @@ function addHeaderAction($a_redraw = false) { $lg->enableRating(true, $this->lng->txt("lm_rating"), false, array("ilcommonactiondispatchergui", "ilratinggui")); - } + } + + // notification + if ($this->user->getId() != ANONYMOUS_USER_ID) + { + if(ilNotification::hasNotification(ilNotification::TYPE_LM, $this->user->getId(), $lm_id)) + { + $this->ctrl->setParameter($this, "ntf", 1); + if (ilNotification::hasOptOut($lm_id)) + { + $lg->addCustomCommand($this->ctrl->getLinkTarget($this), "cont_notification_deactivate_lm"); + } + + $lg->addHeaderIcon("not_icon", + ilUtil::getImagePath("notification_on.svg"), + $this->lng->txt("cont_notification_activated")); + } + else + { + $this->ctrl->setParameter($this, "ntf", 2); + $lg->addCustomCommand($this->ctrl->getLinkTarget($this), "cont_notification_activate_lm"); + + if(ilNotification::hasNotification(ilNotification::TYPE_LM_PAGE, $this->user->getId(), $pg_id)) + { + $this->ctrl->setParameter($this, "ntf", 3); + $lg->addCustomCommand($this->ctrl->getLinkTarget($this), "cont_notification_deactivate_page"); + + $lg->addHeaderIcon("not_icon", + ilUtil::getImagePath("notification_on.svg"), + $this->lng->txt("cont_page_notification_activated")); + } + else + { + $this->ctrl->setParameter($this, "ntf", 4); + $lg->addCustomCommand($this->ctrl->getLinkTarget($this), "cont_notification_activate_page"); + + $lg->addHeaderIcon("not_icon", + ilUtil::getImagePath("notification_off.svg"), + $this->lng->txt("cont_notification_deactivated")); + } + } + $this->ctrl->setParameter($this, "ntf", ""); + } if(!$a_redraw) { @@ -1148,6 +1217,9 @@ function ilLMNotes() $notes_gui->enablePublicNotes(); } + $callback = array($this, "observeNoteAction"); + $notes_gui->addObserver($callback); + if ($next_class == "ilnotegui") { $html = $this->ctrl->forwardCommand($notes_gui); @@ -4398,6 +4470,27 @@ function refreshToc() exit; } + /** + * Generate new ilNote and send Notifications to the users informing that there are new comments in the LM + * @param $a_lm_id + * @param $a_page_id + * @param $a_type + * @param $a_action + * @param $a_note_id + */ + function observeNoteAction($a_lm_id, $a_page_id, $a_type, $a_action, $a_note_id) + { + $note = new ilNote($a_note_id); + $note = $note->getText(); + + $notification = new ilLearningModuleNotification( + ilLearningModuleNotification::ACTION_COMMENT, + ilNotification::TYPE_LM_PAGE, + $this->lm, $a_page_id,$note); + + $notification->send(); + } + } ?> diff --git a/Modules/LearningModule/classes/class.ilLearningModuleNotification.php b/Modules/LearningModule/classes/class.ilLearningModuleNotification.php new file mode 100644 index 000000000000..dd3fe62d9977 --- /dev/null +++ b/Modules/LearningModule/classes/class.ilLearningModuleNotification.php @@ -0,0 +1,258 @@ + + * @version $Id$ + * + * @ingroup ModulesIliasLearningModule + */ +class ilLearningModuleNotification +{ + const ACTION_COMMENT = "comment"; + const ACTION_UPDATE = "update"; + /** + * @var ilObjUser + */ + protected $ilUser; + + /** + * @var ilAccessHandler + */ + protected $ilAccess; + + /** + * @var ilLanguage + */ + protected $lng; + + /** + * @var ilSetting + */ + protected $lm_set; + + /** + * store constant value + * @var string + */ + protected $action; + + /** + * @var int + */ + protected $type; + + /** + * @var ilObjLearningModule + */ + protected $learning_module; + + /** + * @var int + */ + protected $page_id; + + /** + * @var string + */ + protected $comment; + + /** + * @var string + */ + protected $link; + + /** + * @var int + */ + protected $lm_ref_id; + + /** + * @var string + */ + protected $pg_title; + + /** + * ilLearningModuleNotification constructor. + * @param string $a_action + * @param int $a_type Notification type e.g. ilNotification::TYPE_LM_PAGE + * @param ilObjLearningModule $a_learning_module + * @param int $a_page_id + * @param string|null $a_comment + */ + public function __construct(string $a_action, int $a_type, ilObjLearningModule $a_learning_module, int $a_page_id, string $a_comment = null) + { + global $DIC; + + $this->ilUser = $DIC->user(); + $this->ilAccess = $DIC->access(); + $this->lng = $DIC->language(); + $this->lng->loadLanguageModule("content"); + $this->lm_set = new ilSetting("lm"); + $this->action = $a_action; + $this->type = $a_type; + $this->learning_module = $a_learning_module; + $this->page_id = $a_page_id; + $this->comment = $a_comment; + $this->lm_ref_id = $this->learning_module->getRefId(); + $this->link = $this->getLink(); + $this->pg_title = $this->getPageTitle(); + } + + /** + * Generate notifications and send them if necessary + */ + public function send() + { + + $lm_id = $this->learning_module->getId(); + + // #11138 //only comment implemented so always true. + $ignore_threshold = ($this->action == self::ACTION_COMMENT); + + $users = ilNotification::getNotificationsForObject(ilNotification::TYPE_LM, $lm_id, "", $ignore_threshold); + + if ($this->type == ilNotification::TYPE_LM_PAGE) + { + $page_users = ilNotification::getNotificationsForObject($this->type, $this->page_id, null, $ignore_threshold); + $users = array_merge($users, $page_users); + ilNotification::updateNotificationTime(ilNotification::TYPE_LM_PAGE, $this->page_id, $users); + } + + if(!sizeof($users)) + { + return; + } + + ilNotification::updateNotificationTime(ilNotification::TYPE_LM, $lm_id, $users, $this->page_id); + + + foreach(array_unique($users) as $idx => $user_id) + { + if($user_id != $this->ilUser->getId() && + $this->ilAccess->checkAccessOfUser($user_id, 'read', '', $this->lm_ref_id)) + { + // use language of recipient to compose message + $ulng = ilLanguageFactory::_getLanguageOfUser($user_id); + $ulng->loadLanguageModule('content'); + + $subject = $this->getMailSubject($ulng); + $message = $this->getMailBody($ulng,$user_id); + + $mail_obj = new ilMail(ANONYMOUS_USER_ID); + $mail_obj->appendInstallationSignature(true); + $mail_obj->sendMail(ilObjUser::_lookupLogin($user_id), + "", "", $subject, $message, array(), array("system")); + } + } + } + + /** + * Get Link to the LM page + * @return string + */ + protected function getLink() : string + { + // #15192 - should always be present + if($this->page_id) + { + // #18804 - see ilWikiPageGUI::preview() + return ilLink::_getLink("", "pg", null, $this->page_id."_".$this->lm_ref_id); + } + + return ilLink::_getLink($this->lm_ref_id); + + } + + /** + * Get formatted title page + * @return string + */ + protected function getPageTitle() : string + { + return ilLMPageObject::_getPresentationTitle( + $this->page_id, + $this->learning_module->getPageHeader(), $this->learning_module->isActiveNumbering(), + $this->lm_set->get("time_scheduled_page_activation"), false, 0, $this->lng + ); + } + + /** + * get Subject of mail/notification + * @param ilLanguage $ulng + * @return string + */ + protected function getMailSubject(ilLanguage $ulng): string + { + if ($this->action == self::ACTION_COMMENT) + { + return sprintf($ulng->txt('cont_notification_comment_subject_lm'), $this->learning_module->getTitle(), $this->pg_title); + } + + return sprintf($ulng->txt('cont_change_notification_subject_lm'), $this->learning_module->getTitle(), $this->pg_title); + } + + /** + * get email/notification body + * @param ilLanguage $a_ulng + * @param int $a_user_id + * @return string + */ + protected function getMailBody(ilLanguage $a_ulng, int $a_user_id) : string + { + + $message = sprintf($a_ulng->txt('cont_change_notification_salutation'), ilObjUser::_lookupFullname($a_user_id))."\n\n"; + $message .= $a_ulng->txt('cont_notification_'.$this->action."_lm").":\n\n"; + $message .= $a_ulng->txt('learning module').": ".$this->learning_module->getTitle()."\n"; + $message .= $a_ulng->txt('page').": ".$this->pg_title."\n"; + if($this->action == self::ACTION_COMMENT) + { + // include comment/note text + $message .= $a_ulng->txt('cont_commented_by').": ".ilUserUtil::getNamePresentation($this->ilUser->getId())."\n"; + $message .= "\n".$a_ulng->txt('comment').":\n\"".trim($this->comment)."\"\n"; + } + else { + $message .= $this->getPreviewText($a_ulng); + } + + $message .= "\n".$a_ulng->txt('url').": ".$this->link; + + return $message; + } + + /** + * Get first 500 characters of the recently added content + * behavior copied from ilWikiUtil->sendNotification + * @param ilLanguage $a_ulng + * @return string + */ + protected function getPreviewText(ilLanguage $a_ulng) : string + { + $page = new ilLMPageGUI($this->page_id); + $page->setRawPageContent(true); + $page->setAbstractOnly(true); + $page->setFileDownloadLink("."); + $page->setFullscreenLink("."); + $page->setSourcecodeDownloadScript("."); + $str = $page->showPage(); + $str = ilPageObject::truncateHTML($str, 500, "..."); + // making things more readable + $str = str_replace('
', "\n", $str); + $str = str_replace('
', "\n", $str); + $str = str_replace('

', "\n", $str); + $str = str_replace('', "\n", $str); + $str = trim(strip_tags($str)); + + $content = "\n".$a_ulng->txt('content')."\n". + "----------------------------------------\n". + $str."\n". + "----------------------------------------\n"; + + return $content; + + } +} \ No newline at end of file diff --git a/Modules/Wiki/classes/class.ilWikiUtil.php b/Modules/Wiki/classes/class.ilWikiUtil.php index 171c5fa327e0..11ebc1e7412f 100755 --- a/Modules/Wiki/classes/class.ilWikiUtil.php +++ b/Modules/Wiki/classes/class.ilWikiUtil.php @@ -703,13 +703,12 @@ static function sendNotification($a_action, $a_type, $a_wiki_ref_id, $a_page_id, // "fake" new (to enable snippet - if any) $current_version = array_shift($page->getHistoryEntries()); $current_version = $current_version["nr"]; - if(!$current_version) + if(!$current_version && $a_action != "comment") { $a_type = ilNotification::TYPE_WIKI; $a_action = "new"; } - foreach(array_unique($users) as $idx => $user_id) { if($user_id != $ilUser->getId() && @@ -719,50 +718,71 @@ static function sendNotification($a_action, $a_type, $a_wiki_ref_id, $a_page_id, $ulng = ilLanguageFactory::_getLanguageOfUser($user_id); $ulng->loadLanguageModule('wiki'); - $subject = sprintf($ulng->txt('wiki_change_notification_subject'), $wiki->getTitle(), $page->getTitle()); - $message = sprintf($ulng->txt('wiki_change_notification_salutation'), ilObjUser::_lookupFullname($user_id))."\n\n"; - - if($a_type == ilNotification::TYPE_WIKI_PAGE) + if($a_action == "comment") { - // update/delete - $message .= $ulng->txt('wiki_change_notification_page_body_'.$a_action).":\n\n"; + $subject = sprintf($ulng->txt('wiki_notification_comment_subject'), $wiki->getTitle(), $page->getTitle()); + $message = sprintf($ulng->txt('wiki_change_notification_salutation'), ilObjUser::_lookupFullname($user_id))."\n\n"; + + $message .= $ulng->txt('wiki_notification_'.$a_action).":\n\n"; $message .= $ulng->txt('wiki').": ".$wiki->getTitle()."\n"; $message .= $ulng->txt('page').": ".$page->getTitle()."\n"; - $message .= $ulng->txt('wiki_changed_by').": ".ilUserUtil::getNamePresentation($ilUser->getId())."\n"; - - if($snippet) - { - $message .= "\n".$ulng->txt('content')."\n". - "----------------------------------------\n". - $snippet."\n". - "----------------------------------------\n"; - } - + $message .= $ulng->txt('wiki_commented_by').": ".ilUserUtil::getNamePresentation($ilUser->getId())."\n"; + // include comment/note text if($a_comment) - { + { $message .= "\n".$ulng->txt('comment').":\n\"".trim($a_comment)."\"\n"; - } - + } + $message .= "\n".$ulng->txt('wiki_change_notification_page_link').": ".$link; } else { - // new - $message .= $ulng->txt('wiki_change_notification_body_'.$a_action).":\n\n"; - $message .= $ulng->txt('wiki').": ".$wiki->getTitle()."\n"; - $message .= $ulng->txt('page').": ".$page->getTitle()."\n"; - $message .= $ulng->txt('wiki_changed_by').": ".ilUserUtil::getNamePresentation($ilUser->getId())."\n\n"; - - if($snippet) + $subject = sprintf($ulng->txt('wiki_change_notification_subject'), $wiki->getTitle(), $page->getTitle()); + $message = sprintf($ulng->txt('wiki_change_notification_salutation'), ilObjUser::_lookupFullname($user_id))."\n\n"; + + if($a_type == ilNotification::TYPE_WIKI_PAGE) + { + // update/delete + $message .= $ulng->txt('wiki_change_notification_page_body_'.$a_action).":\n\n"; + $message .= $ulng->txt('wiki').": ".$wiki->getTitle()."\n"; + $message .= $ulng->txt('page').": ".$page->getTitle()."\n"; + $message .= $ulng->txt('wiki_changed_by').": ".ilUserUtil::getNamePresentation($ilUser->getId())."\n"; + + if($snippet) + { + $message .= "\n".$ulng->txt('content')."\n". + "----------------------------------------\n". + $snippet."\n". + "----------------------------------------\n"; + } + + // include comment/note text + if($a_comment) + { + $message .= "\n".$ulng->txt('comment').":\n\"".trim($a_comment)."\"\n"; + } + + $message .= "\n".$ulng->txt('wiki_change_notification_page_link').": ".$link; + } + else { - $message .= $ulng->txt('content')."\n". - "----------------------------------------\n". - $snippet."\n". - "----------------------------------------\n\n"; + // new + $message .= $ulng->txt('wiki_change_notification_body_'.$a_action).":\n\n"; + $message .= $ulng->txt('wiki').": ".$wiki->getTitle()."\n"; + $message .= $ulng->txt('page').": ".$page->getTitle()."\n"; + $message .= $ulng->txt('wiki_changed_by').": ".ilUserUtil::getNamePresentation($ilUser->getId())."\n\n"; + + if($snippet) + { + $message .= $ulng->txt('content')."\n". + "----------------------------------------\n". + $snippet."\n". + "----------------------------------------\n\n"; + } + + $message .= $ulng->txt('wiki_change_notification_link').": ".$link; } - - $message .= $ulng->txt('wiki_change_notification_link').": ".$link; } $mail_obj = new ilMail(ANONYMOUS_USER_ID); diff --git a/Services/Notes/classes/class.ilNoteGUI.php b/Services/Notes/classes/class.ilNoteGUI.php index 434f897811a9..530daa4045de 100644 --- a/Services/Notes/classes/class.ilNoteGUI.php +++ b/Services/Notes/classes/class.ilNoteGUI.php @@ -1802,11 +1802,33 @@ protected function notifyObserver($a_action, $a_note) { foreach($this->observer as $item) { - $param = $a_note->getObject(); + $param = $a_note->getObject(); + //TODO refactor this, check what is this news_id from getObject + unset($param['news_id']); $param["action"] = $a_action; $param["note_id"] = $a_note->getId(); - - call_user_func_array($item, $param); + + call_user_func_array($item, $param); + } + } + + //ajax calls don't have callbacks in the observer. (modals) + if($this->ajax) + { + $ref = (int)$_GET['ref_id']; + if(in_array($ref,ilObject::_getAllReferences($this->rep_obj_id))) + { + if($this->obj_type == "pg") + { + $gui = new ilLMPresentationGUI(); + $gui->observeNoteAction($this->rep_obj_id,$this->obj_id,$this->obj_type,$a_action,$a_note->getId()); + } + + if($this->obj_type == "wpg") + { + $gui = new ilWikiPageGUI($this->obj_id, 0, $ref); + $gui->observeNoteAction($this->obj_id,$this->obj_id,$this->obj_type,$a_action,$a_note->getId()); + } } } } diff --git a/Services/Notification/classes/class.ilNotification.php b/Services/Notification/classes/class.ilNotification.php index 02d760c8f8df..307658bbc18c 100644 --- a/Services/Notification/classes/class.ilNotification.php +++ b/Services/Notification/classes/class.ilNotification.php @@ -22,6 +22,8 @@ class ilNotification const TYPE_POLL = 6; const TYPE_LM_BLOCKED_USERS = 7; const TYPE_BOOK = 8; + const TYPE_LM = 9; + const TYPE_LM_PAGE = 10; const THRESHOLD = 180; // time between mails in minutes diff --git a/lang/ilias_de.lang b/lang/ilias_de.lang index 82ec96fcc8ce..66558b1f219b 100644 --- a/lang/ilias_de.lang +++ b/lang/ilias_de.lang @@ -14957,3 +14957,33 @@ rbac#:#mcst_edit_learning_progress#:#Lernfortschrittseinstellungen bearbeiten dateplaner#:#btn_ical#:#iCal style#:#sty_type_code_block#:#Code (Block) style#:#sty_type_code_inline#:#Code (Inline) +exc#:#exc_compare_submissions#:#Compare submissions +exc#:#exc_compare_selected_submissions#:#Compare selected submissions +wiki#:#wiki_notification_comment_subject#:#A comment was added to %s: %s +wiki#:#wiki_notification_comment#:#the following Wiki Page has been commented. +wiki#:#wiki_commented_by#:#Commented by +content#:#cont_notification_activated#:#Notification Activated (Whole LM) +content#:#cont_page_notification_activated#:#Notification Activated (Single Page) +content#:#cont_notification_deactivate_lm#:#Deactivate Notification for Learning Module +content#:#cont_notification_deactivate_page#:#Deactivate Notification for Page +content#:#cont_notification_activate_lm#:#Activate Notification for Learning Module +content#:#cont_notification_activate_page#:#Activate Notification for Page +content#:#cont_notification_deactivated#:#Notification Deactivated +content#:#cont_notification_comment_subject_lm#:#A comment was added to %s: %s +content#:#cont_change_notification_salutation#:#Hello %s, +content#:#cont_notification_comment_lm#:#the following Learning Module Page has been commented +content#:#cont_notification_update_lm#:#the following Learning Module has been updated +content#:#cont_commented_by#:#Commented by +content#:#cont_change_notification_subject_lm#:#Learning Module "%1$s" has been changed: %2$s +exercise#:#exc_enable_versioning#:#Enable versioning +exercise#:#exc_enable_versioning_info#:#Allow tutors and participants to create different versions of a submission +exercise#:#exc_tbl_freeze_version#:#Freeze version of Submission +exercise#:#exc_tbl_show_frozen_versions#:#Show Frozen versions +exercise#:#exc_btn_show_submissions#:#Show Submission History +exercise#:#exc_btn_revise_submission#:#Revise Submission +exercise#:#exc_submission_versioned_ok#:#Submission successfully frozen +exercise#:#exc_submission_list_versions#:#List of Versions +exercise#:#exc_msg_sure_to_freeze_submission#:#Are you sure you want to freeze the submission for this user? +exc#:#exc_notification_submission_versioned#:#Submission versioned +exc#:#exc_notification_submission_can_resend#:#The following submission has been versioned. You are allowed to revise your submission. +exc#:#exc_version_updated#:#Version updated. \ No newline at end of file diff --git a/lang/ilias_en.lang b/lang/ilias_en.lang index 3dff65f3adbf..995fc4baa708 100644 --- a/lang/ilias_en.lang +++ b/lang/ilias_en.lang @@ -14956,4 +14956,34 @@ rbac#:#lso_edit_learning_progress#:#User can edit learning progress settings rbac#:#mcst_edit_learning_progress#:#User can edit learning progress settings dateplaner#:#btn_ical#:#iCal style#:#sty_type_code_block#:#Code (Block) -style#:#sty_type_code_inline#:#Code (Inline) \ No newline at end of file +style#:#sty_type_code_inline#:#Code (Inline) +exc#:#exc_compare_submissions#:#Compare submissions +exc#:#exc_compare_selected_submissions#:#Compare selected submissions +wiki#:#wiki_notification_comment_subject#:#A comment was added to %s: %s +wiki#:#wiki_notification_comment#:#the following Wiki Page has been commented. +wiki#:#wiki_commented_by#:#Commented by +content#:#cont_notification_activated#:#Notification Activated (Whole LM) +content#:#cont_page_notification_activated#:#Notification Activated (Single Page) +content#:#cont_notification_deactivate_lm#:#Deactivate Notification for Learning Module +content#:#cont_notification_deactivate_page#:#Deactivate Notification for Page +content#:#cont_notification_activate_lm#:#Activate Notification for Learning Module +content#:#cont_notification_activate_page#:#Activate Notification for Page +content#:#cont_notification_deactivated#:#Notification Deactivated +content#:#cont_notification_comment_subject_lm#:#A comment was added to %s: %s +content#:#cont_change_notification_salutation#:#Hello %s, +content#:#cont_notification_comment_lm#:#the following Learning Module Page has been commented +content#:#cont_notification_update_lm#:#the following Learning Module has been updated +content#:#cont_commented_by#:#Commented by +content#:#cont_change_notification_subject_lm#:#Learning Module "%1$s" has been changed: %2$s +exercise#:#exc_enable_versioning#:#Enable versioning +exercise#:#exc_enable_versioning_info#:#Allow tutors and participants to create different versions of a submission +exercise#:#exc_tbl_freeze_version#:#Freeze version of Submission +exercise#:#exc_tbl_show_frozen_versions#:#Show Frozen versions +exercise#:#exc_btn_show_submissions#:#Show Submission History +exercise#:#exc_btn_revise_submission#:#Revise Submission +exercise#:#exc_submission_versioned_ok#:#Submission successfully frozen +exercise#:#exc_submission_list_versions#:#List of Versions +exercise#:#exc_msg_sure_to_freeze_submission#:#Are you sure you want to freeze the submission for this user? +exc#:#exc_notification_submission_versioned#:#Submission versioned +exc#:#exc_notification_submission_can_resend#:#The following submission has been versioned. You are allowed to revise your submission. +exc#:#exc_version_updated#:#Version updated. \ No newline at end of file diff --git a/libs/composer/vendor/composer/ClassLoader.php b/libs/composer/vendor/composer/ClassLoader.php index dc02dfb114fb..95f7e0978bad 100644 --- a/libs/composer/vendor/composer/ClassLoader.php +++ b/libs/composer/vendor/composer/ClassLoader.php @@ -377,7 +377,7 @@ private function findFileWithExtension($class, $ext) $subPath = $class; while (false !== $lastPos = strrpos($subPath, '\\')) { $subPath = substr($subPath, 0, $lastPos); - $search = $subPath.'\\'; + $search = $subPath . '\\'; if (isset($this->prefixDirsPsr4[$search])) { $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); foreach ($this->prefixDirsPsr4[$search] as $dir) { diff --git a/libs/composer/vendor/composer/autoload_classmap.php b/libs/composer/vendor/composer/autoload_classmap.php index 236997b08d28..e8e41cbe7ae1 100644 --- a/libs/composer/vendor/composer/autoload_classmap.php +++ b/libs/composer/vendor/composer/autoload_classmap.php @@ -4209,6 +4209,8 @@ 'ilExSubmissionFileGUI' => $baseDir . '/../../Modules/Exercise/classes/class.ilExSubmissionFileGUI.php', 'ilExSubmissionGUI' => $baseDir . '/../../Modules/Exercise/classes/class.ilExSubmissionGUI.php', 'ilExSubmissionObjectGUI' => $baseDir . '/../../Modules/Exercise/classes/class.ilExSubmissionObjectGUI.php', + 'ilExSubmissionPanelsHandlerGUI' => $baseDir . '/../../Modules/Exercise/classes/class.ilExSubmissionPanelsHandlerGUI.php', + 'ilExSubmissionRevision' => $baseDir . '/../../Modules/Exercise/classes/class.ilExSubmissionRevision.php', 'ilExSubmissionTeamGUI' => $baseDir . '/../../Modules/Exercise/classes/class.ilExSubmissionTeamGUI.php', 'ilExSubmissionTextGUI' => $baseDir . '/../../Modules/Exercise/classes/class.ilExSubmissionTextGUI.php', 'ilExampleCache' => $baseDir . '/../../Services/Cache/classes/class.ilExampleCache.php', @@ -4777,6 +4779,7 @@ 'ilLearningModuleImporter' => $baseDir . '/../../Modules/LearningModule/classes/class.ilLearningModuleImporter.php', 'ilLearningModuleLP' => $baseDir . '/../../Modules/LearningModule/classes/class.ilLearningModuleLP.php', 'ilLearningModuleNewsRendererGUI' => $baseDir . '/../../Modules/LearningModule/classes/class.ilLearningModuleNewsRendererGUI.php', + 'ilLearningModuleNotification' => $baseDir . '/../../Modules/LearningModule/classes/class.ilLearningModuleNotification.php', 'ilLearningModulePageCollector' => $baseDir . '/../../Modules/LearningModule/classes/class.ilLearningModulePageCollector.php', 'ilLearningProgress' => $baseDir . '/../../Services/Tracking/classes/class.ilLearningProgress.php', 'ilLearningProgressAccess' => $baseDir . '/../../Services/Tracking/classes/class.ilLearningProgressAccess.php', diff --git a/libs/composer/vendor/composer/autoload_static.php b/libs/composer/vendor/composer/autoload_static.php index ae08f2cc57d9..5464048970a8 100644 --- a/libs/composer/vendor/composer/autoload_static.php +++ b/libs/composer/vendor/composer/autoload_static.php @@ -4517,6 +4517,8 @@ class ComposerStaticInit2fffdf922cf8fdbf1f62eec345993c83 'ilExSubmissionFileGUI' => __DIR__ . '/../..' . '/../../Modules/Exercise/classes/class.ilExSubmissionFileGUI.php', 'ilExSubmissionGUI' => __DIR__ . '/../..' . '/../../Modules/Exercise/classes/class.ilExSubmissionGUI.php', 'ilExSubmissionObjectGUI' => __DIR__ . '/../..' . '/../../Modules/Exercise/classes/class.ilExSubmissionObjectGUI.php', + 'ilExSubmissionPanelsHandlerGUI' => __DIR__ . '/../..' . '/../../Modules/Exercise/classes/class.ilExSubmissionPanelsHandlerGUI.php', + 'ilExSubmissionRevision' => __DIR__ . '/../..' . '/../../Modules/Exercise/classes/class.ilExSubmissionRevision.php', 'ilExSubmissionTeamGUI' => __DIR__ . '/../..' . '/../../Modules/Exercise/classes/class.ilExSubmissionTeamGUI.php', 'ilExSubmissionTextGUI' => __DIR__ . '/../..' . '/../../Modules/Exercise/classes/class.ilExSubmissionTextGUI.php', 'ilExampleCache' => __DIR__ . '/../..' . '/../../Services/Cache/classes/class.ilExampleCache.php', @@ -5085,6 +5087,7 @@ class ComposerStaticInit2fffdf922cf8fdbf1f62eec345993c83 'ilLearningModuleImporter' => __DIR__ . '/../..' . '/../../Modules/LearningModule/classes/class.ilLearningModuleImporter.php', 'ilLearningModuleLP' => __DIR__ . '/../..' . '/../../Modules/LearningModule/classes/class.ilLearningModuleLP.php', 'ilLearningModuleNewsRendererGUI' => __DIR__ . '/../..' . '/../../Modules/LearningModule/classes/class.ilLearningModuleNewsRendererGUI.php', + 'ilLearningModuleNotification' => __DIR__ . '/../..' . '/../../Modules/LearningModule/classes/class.ilLearningModuleNotification.php', 'ilLearningModulePageCollector' => __DIR__ . '/../..' . '/../../Modules/LearningModule/classes/class.ilLearningModulePageCollector.php', 'ilLearningProgress' => __DIR__ . '/../..' . '/../../Services/Tracking/classes/class.ilLearningProgress.php', 'ilLearningProgressAccess' => __DIR__ . '/../..' . '/../../Services/Tracking/classes/class.ilLearningProgressAccess.php', diff --git a/setup/sql/dbupdate_custom.php b/setup/sql/dbupdate_custom.php new file mode 100644 index 000000000000..8d3cdf36611d --- /dev/null +++ b/setup/sql/dbupdate_custom.php @@ -0,0 +1,111 @@ +<#1> +tableExists("exc_submission_version")) +{ + $ilDB->createTable("exc_submission_version", array( + 'id' => array( + 'type' => 'integer', + 'length' => 4, + 'notnull' => true + ), + 'returned_id' => array( + 'type' => 'integer', + 'length' => 4, + 'notnull' => true + ), + 'obj_id' => array( + 'type' => 'integer', + 'length' => 4, + 'notnull' => true + ), + 'user_id' => array( + 'type' => 'integer', + 'length' => 4, + 'notnull' => true + ), + 'filename' => array( + 'type' => 'text', + 'length' => 1000, + 'notnull' => false + ), + 'filetitle' => array( + 'type' => 'text', + 'length' => 1000, + 'notnull' => false + ), + 'mimetype' => array( + 'type' => 'text', + 'length' => 150, + 'notnull' => false + ), + 'ts' => array( + 'type' => 'timestamp', + 'notnull' => false + ), + 'ass_id' => array( + 'type' => 'integer', + 'length' => 4, + 'notnull' => true + ), + 'atext' => array( + 'type' => 'text', + 'length' => 4000, + 'notnull' => true + ), + 'late' => array( + 'type' => 'integer', + 'length' => 4, + 'notnull' => true + ), + 'team_id' => array( + 'type' => 'integer', + 'length' => 4, + 'notnull' => false + ), + 'status' => array( + 'type' => 'text', + 'length' => '9' + ), + 'status_time' => array( + 'type' => 'timestamp', + 'notnull' => false + ), + 'mark' => array( + 'type' => 'text', + 'length' => '32' + ), + 'u_comment' => array( + 'type' => 'text', + 'length' => '4000' + ), + 'version' => array( + 'type' => 'integer', + 'length' => 4, + 'notnull' => true + ), + 'versioned' => array( + 'type' => 'timestamp', + 'notnull' => true + ) + ) + ); + + $ilDB->addPrimaryKey('exc_submission_version', array('id', 'user_id', 'team_id', 'ass_id')); + $ilDB->createSequence('exc_submission_version'); +} +?> +<#2> +getStructure(); +?> +<#3> +tableColumnExists("exc_submission_version","feedback_time")) +{ + $atts = array( + 'type' => 'timestamp', + 'notnull' => false, + ); + $ilDB->addTableColumn("exc_submission_version","feedback_time",$atts); +} +?> \ No newline at end of file