Skip to content

Commit

Permalink
MBS-8820: User specific creation and archive download
Browse files Browse the repository at this point in the history
  • Loading branch information
PM84 committed Mar 5, 2024
1 parent b67c118 commit afbdfbf
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 33 deletions.
29 changes: 16 additions & 13 deletions classes/Report.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,22 +129,25 @@ public function has_access(string $wstoken): bool {
/**
* Get all attempts for all users inside this quiz, excluding previews
*
* @param int $userid - If set, only the attempts of the given user are included.
* @return array Array of all attempt IDs together with the userid that were
* made inside this quiz. Indexed by attemptid.
*
* @throws \dml_exception
*/
public function get_attempts(): array {
public function get_attempts($userid = 0): array {
global $DB;

return $DB->get_records_sql(
"SELECT id AS attemptid, userid " .
"FROM {quiz_attempts} " .
"WHERE preview = 0 AND quiz = :quizid",
[
"quizid" => $this->quiz->id,
]
);
$conditions = [
'quiz' => $this->quiz->id,
'preview' => 0,
];

if(!empty($userid)) {
$conditions['userid'] = $userid;
}

return $DB->get_records('quiz_attempts', $conditions, '', 'id AS attemptid, userid');
}

/**
Expand Down Expand Up @@ -586,22 +589,22 @@ public function generate_full_page(int $attemptid, array $sections, bool $fix_re
nav.navbar {
display: none !important;
}
footer {
display: none !important;
}
div#page {
margin-top: 0 !important;
padding-left: 0 !important;
padding-right: 0 !important;
height: initial !important;
}
div#page-wrapper {
height: initial !important;
}
.stackinputerror {
display: none !important;
}
Expand Down
29 changes: 18 additions & 11 deletions classes/output/job_overview_table.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ class job_overview_table extends \table_sql {
* @param int $courseid ID of the course
* @param int $cmid ID of the course module
* @param int $quizid ID of the quiz
* @param int $userid - If set, the table is limited to the archives created by the user itself.
*
* @throws \coding_exception
*/
public function __construct(string $uniqueid, int $courseid, int $cmid, int $quizid) {
public function __construct(string $uniqueid, int $courseid, int $cmid, int $quizid, int $userid = 0) {
parent::__construct($uniqueid);
$this->define_columns([
'timecreated',
Expand All @@ -68,16 +69,22 @@ public function __construct(string $uniqueid, int $courseid, int $cmid, int $qui
'',
]);

$this->set_sql(
'j.jobid, j.userid, j.timecreated, j.timemodified, j.status, j.retentiontime, j.artifactfilechecksum, f.pathnamehash, f.filesize, u.username',
'{'.ArchiveJob::JOB_TABLE_NAME.'} AS j JOIN {user} AS u ON j.userid = u.id LEFT JOIN {files} AS f ON j.artifactfileid = f.id',
'j.courseid = :courseid AND j.cmid = :cmid AND j.quizid = :quizid',
[
'courseid' => $courseid,
'cmid' => $cmid,
'quizid' => $quizid,
]
);
$conditions = [
'courseid' => $courseid,
'cmid' => $cmid,
'quizid' => $quizid,
];

$fields = 'j.jobid, j.userid, j.timecreated, j.timemodified, j.status, j.retentiontime, j.artifactfilechecksum, f.pathnamehash, f.filesize, u.username';
$sql = '{'.ArchiveJob::JOB_TABLE_NAME.'} AS j JOIN {user} AS u ON j.userid = u.id LEFT JOIN {files} AS f ON j.artifactfileid = f.id';
$where = 'j.courseid = :courseid AND j.cmid = :cmid AND j.quizid = :quizid';

if (!empty($userid)) {
$conditions['userid'] = $userid;
$where .= ' AND u.id = :userid';
}

$this->set_sql($fields, $sql, $where, $conditions);

$this->sortable(true, 'timecreated', SORT_DESC);
$this->no_sorting('jobid');
Expand Down
11 changes: 11 additions & 0 deletions db/access.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,15 @@
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => [],
],
// Capability to use the webservice. Required for the webservice user.
'mod/quiz_archiver:getownarchive' => [
'riskbitmask' => (RISK_PERSONAL),
'captype' => 'read',
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => [
'student' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW,
],
],
];
13 changes: 10 additions & 3 deletions lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,16 @@
function quiz_archiver_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = []) {
// Check permissions.
require_login($course, false, $cm);
require_capability('mod/quiz:grade', $context);
require_capability('quiz/grading:viewstudentnames', $context);
require_capability('quiz/grading:viewidnumber', $context);

if (!((
has_capability('mod/quiz:grade', $context)
&& has_capability('quiz/grading:viewstudentnames', $context)
&& has_capability('quiz/grading:viewidnumber', $context)
) ||
has_capability('mod/quiz_archiver:getownarchive', $context)
)) {
throw new moodle_exception("You have not the capability to download the archive file.");
}

// Validate course
if ($args[1] !== $course->id) {
Expand Down
62 changes: 57 additions & 5 deletions report.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public function __construct() {
* @throws moodle_exception
*/
public function display($quiz, $cm, $course): bool {
global $OUTPUT;
global $OUTPUT, $USER;

$this->course = $course;
$this->cm = $cm;
Expand Down Expand Up @@ -250,7 +250,7 @@ public function display($quiz, $cm, $course): bool {
// Job overview table
if (array_key_exists('jobOverviewTable', $tplCtx)) {
// Generate table
$jobtbl = new job_overview_table('job_overview_table', $this->course->id, $this->cm->id, $this->quiz->id);
$jobtbl = new job_overview_table('job_overview_table', $this->course->id, $this->cm->id, $this->quiz->id, $USER->id);
$jobtbl->define_baseurl($this->base_url());
ob_start();
$jobtbl->out(10, true);
Expand Down Expand Up @@ -325,6 +325,7 @@ public function display($quiz, $cm, $course): bool {
* @param string $archive_filename_pattern Filename pattern to use for archive generation
* @param string $attempts_filename_pattern Filename pattern to use for attempt report generation
* @param int|null $retention_seconds If set, the archive will be deleted automatically this many seconds after creation
* @param int $userid If set, only quiz attempts of the given user are included.
* @return ArchiveJob|null Created ArchiveJob on success
* @throws coding_exception Handled by Moodle
* @throws dml_exception Handled by Moodle
Expand All @@ -340,12 +341,20 @@ protected function initiate_archive_job(
bool $export_course_backup,
string $archive_filename_pattern,
string $attempts_filename_pattern,
?int $retention_seconds = null
?int $retention_seconds = null,
int $userid = 0
): ?ArchiveJob {
global $USER;

// Check permissions.
require_capability('mod/quiz_archiver:create', $this->context);
if (
!(
has_capability('mod/quiz_archiver:create', $this->context)
|| has_capability('mod/quiz_archiver:getownarchive', $this->context)
)
) {
throw new moodle_exception("You have not the capability to generate the archive file.");
}

// Create temporary webservice token
if (class_exists('core_external\util')) {
Expand All @@ -372,7 +381,7 @@ protected function initiate_archive_job(
}

// Get attempt metadata
$attempts = $this->report->get_attempts();
$attempts = $this->report->get_attempts($userid);

// Prepare task: Export quiz attempts
$task_archive_quiz_attempts = null;
Expand Down Expand Up @@ -475,4 +484,47 @@ protected function base_url(): moodle_url {
return new moodle_url('/mod/quiz/report.php', ['id' => $this->cm->id, 'mode' => 'archiver']);
}

/**
* Initialises an archive job for a specific user.
*
* @param int $userid
* @return ArchiveJob|null Created ArchiveJob on success
*/
public function initiate_users_archive_job(
object $quiz,
object $cm,
object $course,
object $context,
bool $export_attempts,
array $report_sections,
bool $report_keep_html_files,
string $paper_format,
bool $export_quiz_backup,
bool $export_course_backup,
string $archive_filename_pattern,
string $attempts_filename_pattern,
?int $retention_seconds = null,
int $userid = 0
) {
$this->context = $context;
require_capability('mod/quiz_archiver:getownarchive', $this->context);

$this->course = $course;
$this->cm = $cm;
$this->quiz = $quiz;
$this->report = new Report($this->course, $this->cm, $this->quiz);
return $this->initiate_archive_job(
$export_attempts,
$report_sections,
$report_keep_html_files,
$paper_format,
$export_quiz_backup,
$export_course_backup,
$archive_filename_pattern,
$attempts_filename_pattern,
$retention_seconds,
$userid
);
}

}
2 changes: 1 addition & 1 deletion version.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

$plugin->component = 'quiz_archiver';
$plugin->release = '1.2.4';
$plugin->version = 2024021901;
$plugin->version = 2024021902;
$plugin->requires = 2022112800;
$plugin->supported = [401, 403];
//$plugin->incompatible = 402;
Expand Down

0 comments on commit afbdfbf

Please sign in to comment.