Skip to content

MBS-8820: User specific creation and archive download #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions classes/Report.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,22 +131,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
32 changes: 18 additions & 14 deletions classes/output/job_overview_table.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,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 @@ -71,19 +72,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.statusextras, j.retentiontime, j.artifactfilechecksum, '.
'f.pathnamehash, f.filesize, u.username',
'{'.ArchiveJob::JOB_TABLE_NAME.'} j '.
'JOIN {user} u ON j.userid = u.id '.
'LEFT JOIN {files} 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.statusextras, 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 @@ -65,4 +65,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 @@ -48,9 +48,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
59 changes: 55 additions & 4 deletions report.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public static function quiz_can_be_archived(int $quizid): bool {
* @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 @@ -263,12 +263,20 @@ protected function initiate_archive_job(
string $archivefilenamepattern,
string $attemptsfilenamepattern,
?array $imageoptimize = null,
?int $retentionseconds = null
?int $retentionseconds = null,
int $userid = 0
): ?ArchiveJob {
global $CFG, $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.");
}

// Check if webservice is configured properly.
if (autoinstall::plugin_is_unconfigured()) {
Expand Down Expand Up @@ -300,7 +308,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.
$taskarchivequizattempts = null;
Expand Down Expand Up @@ -609,4 +617,47 @@ protected function base_url_with_alert(
return $url;
}

/**
* 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 $exportattempts,
array $reportsections,
bool $reportkeephtmlfiles,
string $paperformat,
bool $exportquizbackup,
bool $exportcoursebackup,
string $archivefilenamepattern,
string $attemptsfilenamepattern,
?int $retentionseconds = 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(
$exportattempts,
$reportsections,
$reportkeephtmlfiles,
$paperformat,
$exportquizbackup,
$exportcoursebackup,
$archivefilenamepattern,
$attemptsfilenamepattern,
$retentionseconds,
$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 = '2.2.0';
$plugin->version = 2024102900;
$plugin->version = 2024102901;
$plugin->requires = 2022112800;
$plugin->supported = [401, 405];
$plugin->maturity = MATURITY_STABLE;