Skip to content
AssignmentTableGUI.php 21.8 KiB
Newer Older
<?php

/**
 * This file is part of ILIAS, a powerful learning management system
 * published by ILIAS open source e-Learning e.V.
 *
 * ILIAS is licensed with the GPL-3.0,
 * see https://www.gnu.org/licenses/gpl-3.0.en.html
 * You should have received a copy of said license along with the
 * source code, too.
 *
 * If this is not the case or you just want to try ILIAS, you'll find
 * us at:
 * https://www.ilias.de
 * https://github.com/ILIAS-eLearning
 *
 *********************************************************************/

declare(strict_types=1);

namespace ILIAS\Plugin\PlagScanExercise\Table;

use ilAccessHandler;
use ilAdvancedSelectionListGUI;
use ilDatePresentation;
use ilDateTime;
use ilExAssignment;
use ilExAssignmentTeam;
use ilExSubmission;
use ilFSStorageExercise;
use ilGlyphGUI;
use ILIAS\Plugin\PlagScanExercise\PlagScan\AssignmentConfigPersistence;
use ILIAS\Plugin\PlagScanExercise\PlagScan\Document;
use ilLegacyFormElementsUtil;
use ilModalGUI;
use ilObjectFactory;
use ilObjExercise;
use ilParticipantsPerAssignmentTableGUI;
use ilPlagScanExercisePlugin;
use ilPropertyFormGUI;
use ilTextAreaInputGUI;
use ilUtil;

class AssignmentTableGUI extends ilParticipantsPerAssignmentTableGUI
{
    protected ilAccessHandler $access;
    protected ilObjExercise $exc;
    protected int $mode;
    protected array $filter;
    protected array $comment_modals = [];
    protected array $cols_mandatory = [
        'name',
        'status'
    ];
    protected array $cols_default = [
        'login',
        'submission_date',
        'idl',
        'calc_deadline'
    ];
    protected array $cols_order = [
        'image',
        'name',
        'login',
        'team_members',
        'sent_time',
        'submission',
        'calc_deadline',
        'idl',
        'status',
        'mark',
        'status_time',
        'feedback_time',
        'comment',
        'notice'
    ];

    public function __construct(object $a_parent_obj, string $a_parent_cmd, ilObjExercise $a_exc, int $a_item_id)
    {
        parent::__construct($a_parent_obj, $a_parent_cmd, $a_exc, $a_item_id);

        $this->initMode($a_item_id);

        $this->setShowTemplates(false);

        $this->setFormAction($this->ctrl->getFormAction($a_parent_obj));
        $this->setRowTemplate("tpl.exc_members_row.html", "Customizing/global/plugins/Services/UIComponent/UserInterfaceHook/PlagScanExercise");
        if ($this->mode === self::MODE_BY_ASSIGNMENT) {
            $this->setDefaultOrderField('name');
            $this->setDefaultOrderDirection('asc');
        }

        $this->addMultiCommand('saveStatusSelected', $this->lng->txt('exc_save_selected'));

        $this->setFormName('ilExcIDlForm');

        if ($this->mode === self::MODE_BY_ASSIGNMENT && $this->ass->hasActiveIDl() && !$this->ass->hasReadOnlyIDl()) {
            $this->addMultiCommand('setIndividualDeadline', $this->lng->txt('exc_individual_deadline_action'));
        }

        if ($this->mode === self::MODE_BY_ASSIGNMENT && $this->exc->hasTutorFeedbackMail()) {
            $this->addMultiCommand('redirectFeedbackMail', $this->lng->txt('exc_tbl_action_feedback_mail'));
        }

        $this->addMultiCommand('sendMembers', $this->lng->txt('exc_send_assignment'));

        if ($this->mode === self::MODE_BY_ASSIGNMENT && $this->ass && $this->ass->hasTeam()) {
            $this->addMultiCommand('createTeams', $this->lng->txt('exc_team_multi_create'));
            $this->addMultiCommand('dissolveTeams', $this->lng->txt('exc_team_multi_dissolve'));
        }

        if ($this->mode === self::MODE_BY_ASSIGNMENT) {
            $this->addMultiCommand('confirmDeassignMembers', $this->lng->txt('exc_deassign_members'));
        }

        $this->setFilterCommand($this->getParentCmd() . 'Apply');
        $this->setResetCommand($this->getParentCmd() . 'Reset');

        $this->initFilter();
        $this->setData($this->parseData());
    }

    public function initFilter(): void
    {
        if ($this->mode === self::MODE_BY_ASSIGNMENT) {
            $item = $this->addFilterItemByMetaType('flt_name', self::FILTER_TEXT, false, $this->lng->txt('name') . " / " . $this->lng->txt('login'));
            if ($item !== null) {
                $this->filter['name'] = $item->getValue();
            }
        }

        $this->lng->loadLanguageModule('search');
        $options = [
            '' => $this->lng->txt('search_any'),
            'notgraded' => $this->lng->txt('exc_notgraded'),
            'passed' => $this->lng->txt('exc_passed'),
            'failed' => $this->lng->txt('exc_failed')
        ];
        $item = $this->addFilterItemByMetaType('flt_status', self::FILTER_SELECT, false, $this->lng->txt('exc_tbl_status'));
        $item->setOptions($options);
        $this->filter['status'] = $item->getValue();

        $options = [
            '' => $this->lng->txt('search_any'),
            'y' => $this->lng->txt('exc_tbl_filter_has_submission'),
            'n' => $this->lng->txt('exc_tbl_filter_has_no_submission')
        ];
        $item = $this->addFilterItemByMetaType('flt_subm', self::FILTER_SELECT, false, $this->lng->txt('exc_tbl_filter_submission'));
        $item->setOptions($options);
        $this->filter['subm'] = $item->getValue();
    }

    public function getSelectableColumns(): array
    {
        $cols = [];

        $columns = $this->parseColumns();
        foreach ($this->cols_order as $id) {
            if (in_array($id, $this->cols_mandatory, true)) {
                continue;
            }

            if (array_key_exists($id, $columns)) {
                $col = $columns[$id];

                $cols[$id] = ['txt' => $col[0], 'default' => in_array($id, $this->cols_default, true)];
            }
        }

        return $cols;
    }

    protected function parseColumns(): array
    {
        $cols = $this->parseModeColumns();

        $cols['submission'] = [$this->lng->txt('exc_tbl_submission_date'), 'submission'];

        $cols['status'] = [$this->lng->txt('exc_tbl_status'), 'status'];
        $cols['mark'] = [$this->lng->txt('exc_tbl_mark'), 'mark'];
        $cols['status_time'] = [$this->lng->txt('exc_tbl_status_time'), 'status_time'];

        $cols['sent_time'] = [$this->lng->txt('exc_tbl_sent_time'), 'sent_time'];

        if (
            $this->exc->hasTutorFeedbackText() ||
            $this->exc->hasTutorFeedbackMail() ||
            $this->exc->hasTutorFeedbackFile()
        ) {
            $cols['feedback_time'] = [$this->lng->txt('exc_tbl_feedback_time'), 'feedback_time'];
        }

        if ($this->exc->hasTutorFeedbackText()) {
            $cols['comment'] = [$this->lng->txt('exc_tbl_comment'), 'comment'];
        }

        $cols['notice'] = [$this->lng->txt('exc_tbl_notice'), 'note'];

        return $cols;
    }

    protected function parseRow(int $a_user_id, ilExAssignment $a_ass, array $a_row): void
    {
        global $DIC;

        $has_no_team_yet = ($a_ass->hasTeam() && !ilExAssignmentTeam::getTeamId($a_ass->getId(), $a_user_id));

        if ($this->mode === self::MODE_BY_ASSIGNMENT) {
            if (!$a_ass->hasTeam()) {
                $this->tpl->setVariable('VAL_NAME', $a_row['name']);

                if (is_array($info = $this->access->getInfo()) &&
                    !$this->access->checkAccessOfUser($a_user_id, 'read', '', $this->exc->getRefId())) {
                    $this->tpl->setCurrentBlock('access_warning');
                    $this->tpl->setVariable('PARENT_ACCESS', $info[0]['text']);
                    $this->tpl->parseCurrentBlock();
                }
            } else {
                asort($a_row['team']);
                foreach ($a_row['team'] as $team_member_id => $team_member_name) {
                    if (count($a_row['team']) > 1) {
                        $this->ctrl->setParameterByClass('ilExSubmissionTeamGUI', 'id', $team_member_id);
                        $url = $this->ctrl->getLinkTargetByClass('ilExSubmissionTeamGUI', 'confirmRemoveTeamMember');
                        $this->ctrl->setParameterByClass('ilExSubmissionTeamGUI', 'id', '');

                        $this->tpl->setCurrentBlock('team_member_removal_bl');
                        $this->tpl->setVariable('URL_TEAM_MEMBER_REMOVAL', $url);
                        $this->tpl->setVariable(
                            'TXT_TEAM_MEMBER_REMOVAL',
                            ilGlyphGUI::get(ilGlyphGUI::CLOSE, $this->lng->txt('remove'))
                        );
                        $this->tpl->parseCurrentBlock();
                    }

                    if (is_array($info = $this->access->getInfo()) &&
                        !$this->access->checkAccessOfUser($team_member_id, 'read', '', $this->exc->getRefId())
                    ) {
                        $this->tpl->setCurrentBlock('team_access_warning');
                        $this->tpl->setVariable('TEAM_PARENT_ACCESS', $info[0]['text']);
                        $this->tpl->parseCurrentBlock();
                    }

                    $this->tpl->setCurrentBlock('team_member');
                    $this->tpl->setVariable('TXT_MEMBER_NAME', $team_member_name);
                    $this->tpl->parseCurrentBlock();
                }

                $this->tpl->setCurrentBlock('team_info');
                if ($has_no_team_yet) {
                    $this->tpl->setVariable('TXT_TEAM_INFO', $this->lng->txt('exc_no_team_yet'));
                } else {
                    $this->tpl->setVariable('TXT_TEAM_INFO', "(" . $a_row['submission_obj']->getTeam()->getId() . ")");
                }
            }
        } else {
            $this->tpl->setVariable('VAL_NAME', $a_row['name']);
        }

        if (!$has_no_team_yet) {
            $this->tpl->setVariable('SEL_' . strtoupper($a_row['status']), " selected='selected' ");
            $this->tpl->setVariable('TXT_NOTGRADED', $this->lng->txt('exc_notgraded'));
            $this->tpl->setVariable('TXT_PASSED', $this->lng->txt('exc_passed'));
            $this->tpl->setVariable('TXT_FAILED', $this->lng->txt('exc_failed'));
        } else {
            $nt_colspan = in_array('mark', $this->getSelectedColumns(), true) ? 2 : 1;

            $this->tpl->setVariable('NO_TEAM_COLSPAN', $nt_colspan);
        }

        $comment_id = '';
        if ($this->exc->hasTutorFeedbackText()) {
            $comment_id = 'excasscomm_' . $a_ass->getId() . '_' . $a_user_id;

            $modal = ilModalGUI::getInstance();
            $modal->setId($comment_id);
            $modal->setHeading($this->lng->txt('exc_tbl_action_feedback_text'));

            $lcomment_form = new ilPropertyFormGUI();
            $lcomment_form->setId($comment_id);
            $lcomment_form->setPreventDoubleSubmission(false);

            $lcomment = new ilTextAreaInputGUI($this->lng->txt('exc_comment_for_learner'), 'lcomment_' . $a_ass->getId() . '_' . $a_user_id);
            $lcomment->setInfo($this->lng->txt('exc_comment_for_learner_info'));
            $lcomment->setValue($a_row['comment']);
            $lcomment->setCols(45);
            $lcomment->setRows(10);
            $lcomment_form->addItem($lcomment);

            $lcomment_form->addCommandButton('save', $this->lng->txt('save'));

            $modal->setBody($lcomment_form->getHTML());

            $this->comment_modals[] = $modal->getHTML();
            unset($modal);
        }

        foreach ($this->getSelectedColumns() as $col) {
            switch($col) {
                case 'image':
                    if (
                        !$a_ass->hasTeam() &&
                        $usr_obj = ilObjectFactory::getInstanceByObjId($a_user_id, false)
                    ) {
                        $this->tpl->setVariable('VAL_IMAGE', $usr_obj->getPersonalPicturePath('xxsmall'));
                        $this->tpl->setVariable('TXT_IMAGE', $this->lng->txt('personal_picture'));
                    }
                    break;
                case 'team_members':
                    if ($a_ass->hasTeam()) {
                        if ([] === $a_row['team']) {
                            $this->tpl->setVariable('VAL_TEAM_MEMBER', $this->lng->txt('exc_no_team_yet'));
                        } else {
                            foreach ($a_row['team'] as $name) {
                                $this->tpl->setCurrentBlock('team_member_bl');
                                $this->tpl->setVariable('VAL_TEAM_MEMBER', $name);
                                $this->tpl->parseCurrentBlock();
                            }
                        }
                    } else {
                        $this->tpl->setVariable('VAL_TEAM_MEMBER', "&nbsp;");
                    }
                    break;
                case 'idl':
                case 'calc_deadline':
                    $this->tpl->setVariable(
                        'VAL_' . strtoupper($col),
                        $a_row[$col] ? ilDatePresentation::formatDate(new ilDateTime($a_row[$col], IL_CAL_UNIX)) : "&nbsp;"
                    );
                    break;
                case 'mark':
                    if ($has_no_team_yet) {
                        break;
                    }
                case 'notice':
                    $this->tpl->setVariable('VAL_' . strtoupper($col), ilLegacyFormElementsUtil::prepareFormOutput(trim($a_row[$col])));
                    break;
                case 'comment':
                    $this->tpl->setVariable('LCOMMENT_ID', $comment_id . '_snip');
                    $this->tpl->setVariable('VAL_' . strtoupper($col), (trim($a_row[$col]) !== '') ? nl2br(trim($a_row[$col])) : "&nbsp;");
                    break;
                case 'submission':
                    if ($a_row['submission_obj']) {
                        foreach ($a_row['submission_obj']->getFiles() as $file) {
                            if ($file['late']) {
                                $this->tpl->setVariable('TXT_LATE', $this->lng->txt('exc_late_submission'));
                                break;
                            }
                        }
                    }
                case 'feedback_time':
                case 'status_time':
                case 'sent_time':
                    $this->tpl->setVariable(
                        'VAL_' . strtoupper($col),
                        $a_row[$col] ? ilDatePresentation::formatDate(new ilDateTime($a_row[$col], IL_CAL_DATETIME)) : "&nbsp;"
                    );
                    break;
                case 'login':
                    if ($a_ass->hasTeam()) {
                        break;
                    }
                default:
                    $this->tpl->setVariable('VAL_' . strtoupper($col), $a_row[$col] ? trim($a_row[$col]) : "&nbsp;");
                    break;
            }
        }

        $actions = new ilAdvancedSelectionListGUI();
        $actions->setId($a_ass->getId() . '_' . $a_user_id);
        $actions->setListTitle($this->lng->txt('actions'));

        $file_info = $a_row['submission_obj']->getDownloadedFilesInfoForTableGUIS($this->getParentObject(), $this->getParentCmd());

        $counter = $file_info['files']['count'];
        if ($counter) {
            if ($file_info['files']['download_url']) {
                $actions->addItem(
                    $file_info['files']['download_txt'] . " (" . $counter . ")",
                    '',
                    $file_info['files']['download_url']
                );
            }

            if ($file_info['files']['download_new_url']) {
                $actions->addItem(
                    $file_info['files']['download_new_txt'],
                    '',
                    $file_info['files']['download_new_url']
                );
            }
        }

        if (!$has_no_team_yet && $a_ass->hasActiveIDl() && !$a_ass->hasReadOnlyIDl()) {
            $idl_id = $a_ass->hasTeam() ? 't' . ilExAssignmentTeam::getTeamId($a_ass->getId(), $a_user_id) : $a_user_id;

            $this->tpl->setVariable('VAL_IDL_ID', $a_ass->getId() . '_' . $idl_id);

            $actions->addItem(
                $this->lng->txt('exc_individual_deadline_action'),
                '',
                "#",
                '',
                '',
                '',
                '',
                false,
                "il.ExcIDl.trigger('" . $idl_id . "'," . $a_ass->getId() . ")"
            );
        }

        if ($this->exc->hasTutorFeedbackMail()) {
            $actions->addItem(
                $this->lng->txt('exc_tbl_action_feedback_mail'),
                '',
                $this->ctrl->getLinkTarget($this->parent_obj, 'redirectFeedbackMail')
            );
        }

        if ($this->exc->hasTutorFeedbackFile()) {
            $storage = new ilFSStorageExercise($this->exc->getId(), $a_ass->getId());
            $counter = $storage->countFeedbackFiles($a_row['submission_obj']->getFeedbackId());
            $counter = $counter ? " (" . $counter . ")" : '';
            $actions->addItem(
                $this->lng->txt('exc_tbl_action_feedback_file') . $counter,
                '',
                $this->ctrl->getLinkTargetByClass('ilfilesystemgui', 'listFiles')
            );
        }

        if ($this->exc->hasTutorFeedbackText()) {
            $actions->addItem(
                $this->lng->txt('exc_tbl_action_feedback_text'),
                '',
                "#",
                '',
                '',
                '',
                '',
                false,
                "il.ExcManagement.showComment('" . $comment_id . "')"
            );
        }

        if (($peer_review = $a_row['submission_obj']->getPeerReview()) && $a_ass->afterDeadlineStrict()) {
            $counter = $peer_review->countGivenFeedback(true, $a_user_id);
            $counter = ($counter !== 0) ? " (" . $counter . ")" : '';
            $actions->addItem(
                $this->lng->txt('exc_tbl_action_peer_review_given') . $counter,
                '',
                $this->ctrl->getLinkTargetByClass('ilexpeerreviewgui', 'showGivenPeerReview')
            );

            $counter = count($peer_review->getPeerReviewsByPeerId($a_user_id, true));
            $counter = $counter ? " (" . $counter . ")" : '';
            $actions->addItem(
                $this->lng->txt('exc_tbl_action_peer_review_received') . $counter,
                '',
                $this->ctrl->getLinkTargetByClass('ilexpeerreviewgui', 'showReceivedPeerReview')
            );
        }

        if ($has_no_team_yet) {
            $actions->addItem(
                $this->lng->txt('exc_create_team'),
                '',
                $this->ctrl->getLinkTargetByClass('ilExSubmissionTeamGUI', 'createSingleMemberTeam')
            );
        } elseif ($a_ass->hasTeam()) {
            $actions->addItem(
                $this->lng->txt('exc_tbl_action_team_log'),
                '',
                $this->ctrl->getLinkTargetByClass('ilExSubmissionTeamGUI', 'showTeamLog')
            );
        }

        $plagscan_persistence = new AssignmentConfigPersistence($DIC->database());
        $plagscan_config = $plagscan_persistence->getByAssignmentId($this->ass->getId());
        $document = new Document();
        $document->getByUserAssignment($a_user_id, $this->ass->getId());
        /** @var ilExSubmission $submission */
        $submission = $a_row['submission_obj'];

        $plugin = ilPlagScanExercisePlugin::getInstance();
        if ($plagscan_config->getActivation() && $document->getPlagscanStatus() === 'not_checked') {
            $this->ctrl->clearParametersByClass('ilExerciseManagementGUI');
            $this->ctrl->setParameterByClass('ilExerciseManagementGUI', 'chkdoc', $document->getPlagscanId());
            $actions->addItem(
                $plugin->txt('start_check'),
                '',
                $this->ctrl->getLinkTargetByClass('ilExerciseManagementGUI', 'members')
            );
        }

        if ($plagscan_config->getActivation() && $document->getPlagscanStatus() === '') {
            $this->ctrl->clearParametersByClass('ilExerciseManagementGUI');
            $this->ctrl->setParameterByClass('ilExerciseManagementGUI', 'subdoc_usr', $submission->getUserId());
            $this->ctrl->setParameterByClass('ilExerciseManagementGUI', 'subdoc_ass', $submission->getAssignment()->getId());
            $actions->addItem(
                $plugin->txt('submit_document'),
                '',
                $this->ctrl->getLinkTargetByClass('ilExerciseManagementGUI', 'members')
            );
        }

        if ($plagscan_config->getActivation()) {
            switch ($document->getPlagscanStatus()) {
                case 'in_progress':
                    $this->tpl->setVariable(
                        'PSX_STATUS',
                        $plugin->txt('status_head') . '<br/>' . $plugin->txt('in_progress')
                    );
                    break;
                case 'not_checked':
                    $this->tpl->setVariable(
                        'PSX_STATUS',
                        $plugin->txt('status_head') . '<br/>' . $plugin->txt('not_checked')
                    );
                    break;
                    $this->tpl->setVariable(
                        'PSX_STATUS',
                        $plugin->txt('status_head') . '<br/>' . $plugin->txt('no_submission')
                    );
                    break;
                default:
                    $plag_level = substr($document->getPlagscanStatus(), 10);
                    $html = 'PlagLevel ' . $plag_level . '% - <a href="' . $document->getShareLink() . '" target="_blank">Report</a>';
                    $this->tpl->setVariable('PSX_STATUS', $html);
            }
        }
        $this->tpl->setVariable('ACTIONS', $actions->getHTML());
    }

    public function render(): string
    {
        global $DIC;

        $url = $this->ctrl->getLinkTarget($this->getParentObject(), 'saveCommentForLearners', '', true, false);

        $DIC->ui()->mainTemplate()->addJavaScript("Modules/Exercise/js/ilExcManagement.js");
        $DIC->ui()->mainTemplate()->addOnLoadCode('il.ExcManagement.init("' . $url . '");');

        return parent::render() .
            implode("\n", $this->comment_modals);
    }
}