Skip to content

Commit a321430

Browse files
committed
MBS-8820: User specific creation and archive download
1 parent 9c80c96 commit a321430

File tree

6 files changed

+107
-31
lines changed

6 files changed

+107
-31
lines changed

classes/Report.php

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,22 +131,25 @@ public function has_access(string $wstoken): bool {
131131
/**
132132
* Get all attempts for all users inside this quiz, excluding previews
133133
*
134+
* @param int $userid - If set, only the attempts of the given user are included.
134135
* @return array Array of all attempt IDs together with the userid that were
135136
* made inside this quiz. Indexed by attemptid.
136137
*
137138
* @throws \dml_exception
138139
*/
139-
public function get_attempts(): array {
140+
public function get_attempts($userid = 0): array {
140141
global $DB;
141142

142-
return $DB->get_records_sql(
143-
"SELECT id AS attemptid, userid " .
144-
"FROM {quiz_attempts} " .
145-
"WHERE preview = 0 AND quiz = :quizid",
146-
[
147-
"quizid" => $this->quiz->id,
148-
]
149-
);
143+
$conditions = [
144+
'quiz' => $this->quiz->id,
145+
'preview' => 0,
146+
];
147+
148+
if(!empty($userid)) {
149+
$conditions['userid'] = $userid;
150+
}
151+
152+
return $DB->get_records('quiz_attempts', $conditions, '', 'id AS attemptid, userid');
150153
}
151154

152155
/**

classes/output/job_overview_table.php

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ class job_overview_table extends \table_sql {
4848
* @param int $courseid ID of the course
4949
* @param int $cmid ID of the course module
5050
* @param int $quizid ID of the quiz
51+
* @param int $userid - If set, the table is limited to the archives created by the user itself.
5152
*
5253
* @throws \coding_exception
5354
*/
54-
public function __construct(string $uniqueid, int $courseid, int $cmid, int $quizid) {
55+
public function __construct(string $uniqueid, int $courseid, int $cmid, int $quizid, int $userid = 0) {
5556
parent::__construct($uniqueid);
5657
$this->define_columns([
5758
'timecreated',
@@ -71,19 +72,22 @@ public function __construct(string $uniqueid, int $courseid, int $cmid, int $qui
7172
'',
7273
]);
7374

74-
$this->set_sql(
75-
'j.jobid, j.userid, j.timecreated, j.timemodified, j.status, j.statusextras, j.retentiontime, j.artifactfilechecksum, '.
76-
'f.pathnamehash, f.filesize, u.username',
77-
'{'.ArchiveJob::JOB_TABLE_NAME.'} j '.
78-
'JOIN {user} u ON j.userid = u.id '.
79-
'LEFT JOIN {files} f ON j.artifactfileid = f.id',
80-
'j.courseid = :courseid AND j.cmid = :cmid AND j.quizid = :quizid',
81-
[
82-
'courseid' => $courseid,
83-
'cmid' => $cmid,
84-
'quizid' => $quizid,
85-
]
86-
);
75+
$conditions = [
76+
'courseid' => $courseid,
77+
'cmid' => $cmid,
78+
'quizid' => $quizid,
79+
];
80+
81+
$fields = 'j.jobid, j.userid, j.timecreated, j.timemodified, j.status, j.statusextras, j.retentiontime, j.artifactfilechecksum, f.pathnamehash, f.filesize, u.username';
82+
$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';
83+
$where = 'j.courseid = :courseid AND j.cmid = :cmid AND j.quizid = :quizid';
84+
85+
if (!empty($userid)) {
86+
$conditions['userid'] = $userid;
87+
$where .= ' AND u.id = :userid';
88+
}
89+
90+
$this->set_sql($fields, $sql, $where, $conditions);
8791

8892
$this->sortable(true, 'timecreated', SORT_DESC);
8993
$this->no_sorting('jobid');

db/access.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,15 @@
6565
'contextlevel' => CONTEXT_SYSTEM,
6666
'archetypes' => [],
6767
],
68+
// Capability to use the webservice. Required for the webservice user.
69+
'mod/quiz_archiver:getownarchive' => [
70+
'riskbitmask' => (RISK_PERSONAL),
71+
'captype' => 'read',
72+
'contextlevel' => CONTEXT_SYSTEM,
73+
'archetypes' => [
74+
'student' => CAP_ALLOW,
75+
'editingteacher' => CAP_ALLOW,
76+
'manager' => CAP_ALLOW,
77+
],
78+
],
6879
];

lib.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,16 @@
4848
function quiz_archiver_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = []) {
4949
// Check permissions.
5050
require_login($course, false, $cm);
51-
require_capability('mod/quiz:grade', $context);
52-
require_capability('quiz/grading:viewstudentnames', $context);
53-
require_capability('quiz/grading:viewidnumber', $context);
51+
52+
if (!((
53+
has_capability('mod/quiz:grade', $context)
54+
&& has_capability('quiz/grading:viewstudentnames', $context)
55+
&& has_capability('quiz/grading:viewidnumber', $context)
56+
) ||
57+
has_capability('mod/quiz_archiver:getownarchive', $context)
58+
)) {
59+
throw new moodle_exception("You have not the capability to download the archive file.");
60+
}
5461

5562
// Validate course.
5663
if ($args[1] !== $course->id) {

report.php

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public static function quiz_can_be_archived(int $quizid): bool {
8989
* @throws moodle_exception
9090
*/
9191
public function display($quiz, $cm, $course): bool {
92-
global $OUTPUT;
92+
global $OUTPUT, $USER;
9393

9494
$this->course = $course;
9595
$this->cm = $cm;
@@ -263,12 +263,20 @@ protected function initiate_archive_job(
263263
string $archivefilenamepattern,
264264
string $attemptsfilenamepattern,
265265
?array $imageoptimize = null,
266-
?int $retentionseconds = null
266+
?int $retentionseconds = null,
267+
int $userid = 0
267268
): ?ArchiveJob {
268269
global $CFG, $USER;
269270

270271
// Check permissions.
271-
require_capability('mod/quiz_archiver:create', $this->context);
272+
if (
273+
!(
274+
has_capability('mod/quiz_archiver:create', $this->context)
275+
|| has_capability('mod/quiz_archiver:getownarchive', $this->context)
276+
)
277+
) {
278+
throw new moodle_exception("You have not the capability to generate the archive file.");
279+
}
272280

273281
// Check if webservice is configured properly.
274282
if (autoinstall::plugin_is_unconfigured()) {
@@ -300,7 +308,7 @@ protected function initiate_archive_job(
300308
}
301309

302310
// Get attempt metadata.
303-
$attempts = $this->report->get_attempts();
311+
$attempts = $this->report->get_attempts($userid);
304312

305313
// Prepare task: Export quiz attempts.
306314
$taskarchivequizattempts = null;
@@ -609,4 +617,47 @@ protected function base_url_with_alert(
609617
return $url;
610618
}
611619

620+
/**
621+
* Initialises an archive job for a specific user.
622+
*
623+
* @param int $userid
624+
* @return ArchiveJob|null Created ArchiveJob on success
625+
*/
626+
public function initiate_users_archive_job(
627+
object $quiz,
628+
object $cm,
629+
object $course,
630+
object $context,
631+
bool $exportattempts,
632+
array $reportsections,
633+
bool $reportkeephtmlfiles,
634+
string $paperformat,
635+
bool $exportquizbackup,
636+
bool $exportcoursebackup,
637+
string $archivefilenamepattern,
638+
string $attemptsfilenamepattern,
639+
?int $retentionseconds = null,
640+
int $userid = 0
641+
) {
642+
$this->context = $context;
643+
require_capability('mod/quiz_archiver:getownarchive', $this->context);
644+
645+
$this->course = $course;
646+
$this->cm = $cm;
647+
$this->quiz = $quiz;
648+
$this->report = new Report($this->course, $this->cm, $this->quiz);
649+
return $this->initiate_archive_job(
650+
$exportattempts,
651+
$reportsections,
652+
$reportkeephtmlfiles,
653+
$paperformat,
654+
$exportquizbackup,
655+
$exportcoursebackup,
656+
$archivefilenamepattern,
657+
$attemptsfilenamepattern,
658+
$retentionseconds,
659+
$userid
660+
);
661+
}
662+
612663
}

version.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
$plugin->component = 'quiz_archiver';
2828
$plugin->release = '2.2.0';
29-
$plugin->version = 2024102900;
29+
$plugin->version = 2024102901;
3030
$plugin->requires = 2022112800;
3131
$plugin->supported = [401, 405];
3232
$plugin->maturity = MATURITY_STABLE;

0 commit comments

Comments
 (0)