Skip to content
Merged
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
11 changes: 7 additions & 4 deletions block_qrcode.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,16 @@ public function get_content() {
// Displays the block.
/** @var block_qrcode_renderer $renderer */
$renderer = $this->page->get_renderer('block_qrcode');
$qrcode = $renderer->display_image($COURSE->id, $this->instance->id);

// Check if we're on an activity page.
$modulecontextid = $this->page->cm ? $this->page->cm->id : null;
$qrcode = $renderer->display_image($COURSE->id, $this->instance->id, $modulecontextid);
$this->content->text .= $qrcode;

// Students can't see the download button.
if (has_capability('block/qrcode:download', $this->context)) {
$this->content->text .= '<br><br>';
$this->content->text .= $renderer->display_download_section($COURSE->id, $this->instance->id);
$this->content->text .= $renderer->display_download_section($COURSE->id, $this->instance->id, $modulecontextid);
}

$this->page->requires->js_call_amd('block_qrcode/fullscreenqrcode', 'init', [$qrcode]);
Expand All @@ -73,12 +76,12 @@ public function get_content() {
}

/**
* The block is only available at course-view pages.
* The block is available at course-view and activity pages.
*
* @return array of applicable formats
*/
public function applicable_formats() {
return ['course-view' => true, 'mod' => false, 'my' => false];
return ['course-view' => true, 'mod' => true, 'my' => false];
}

/**
Expand Down
12 changes: 8 additions & 4 deletions classes/output/renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,17 @@ class renderer extends \plugin_renderer_base {
* Returns a QR code as html image.
* @param int $courseid course id
* @param int $instanceid instance id of block
* @param int $modulecontextid course module id (optional)
* @return string html-string
*/
public function display_image($courseid, $instanceid) {
public function display_image($courseid, $instanceid, $modulecontextid = null) {
$link = new \moodle_url('/blocks/qrcode/download.php', [
'courseid' => $courseid,
'download' => false,
'format' => 1,
'size' => 300,
'instance' => $instanceid]);
'instance' => $instanceid,
'modulecontextid' => $modulecontextid]);

return \html_writer::img(
$link,
Expand All @@ -57,13 +59,15 @@ public function display_image($courseid, $instanceid) {
* Displays the download section (menus for choosing format & size, download button). *
* @param int $courseid course id
* @param int $instanceid instance id of block
* @param int $modulecontextid course module id (optional)
* @return string html-string
*/
public function display_download_section($courseid, $instanceid) {
public function display_download_section($courseid, $instanceid, $modulecontextid = null) {
$download = new \moodle_url('/blocks/qrcode/download.php', [
'courseid' => $courseid,
'download' => true,
'instance' => $instanceid]);
'instance' => $instanceid,
'modulecontextid' => $modulecontextid]);
$mform = new \block_qrcode\block_qrcode_form(
$download,
['format' => 1, 'size' => 300],
Expand Down
38 changes: 34 additions & 4 deletions classes/output_image.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,21 +90,28 @@ class output_image {
* @var int
*/
protected $instanceid;

/**
* Course module ID for activity pages.
* @var int|null
*/
protected $modulecontextid;

/**
* output_image constructor.
* @param int $format file type
* @param int $size image size
* @param int $courseid course for which the qrcode is created
* @param int $instanceid
* @param int|null $modulecontextid course module id for activity pages
*/
public function __construct($format, $size, $courseid, $instanceid) {
public function __construct($format, $size, $courseid, $instanceid, $modulecontextid = null) {
global $CFG, $DB;
$this->format = $format;
$this->size = (int)$size;
$this->course = get_course($courseid);
$this->instanceid = $instanceid;
$file = $CFG->localcachedir . '/block_qrcode/course-' .
(int)$courseid . '-' . $this->size; // Set file path.
$this->modulecontextid = $modulecontextid;

$instance = $DB->get_record('block_instances', ['id' => $instanceid], '*', MUST_EXIST);
$block = block_instance('qrcode', $instance);
Expand All @@ -117,6 +124,13 @@ public function __construct($format, $size, $courseid, $instanceid) {
$this->uselogo = ($block->config->usedefault && get_config('block_qrcode', 'use_logo') == 1) ||
(!$block->config->usedefault && $block->config->instc_uselogo);

$file = "{$CFG->localcachedir}/block_qrcode/";
if ($modulecontextid) {
$file .= "module-{$modulecontextid}-{$this->size}";
} else {
$file .= "course-{$courseid}-{$this->size}";
}

// Set custom logo path.
if ($this->uselogo) {
$this->logofile = $this->get_logo();
Expand All @@ -142,11 +156,27 @@ public function __construct($format, $size, $courseid, $instanceid) {

/**
* Return the course link url to be included into the QR code.
* @return url the course link
* @return url the course link or activity link
*/
public function course_link_url(): url {
$customwwwroot = get_config('block_qrcode', 'custom_wwwroot');

// If we're on an activity page, link to the activity instead of the course.
if (!is_null($this->modulecontextid)) {
global $DB;
$cm = $DB->get_record('course_modules', ['id' => $this->modulecontextid], 'id, module, instance');
if ($cm) {
// Get module info.
$module = $DB->get_record('modules', ['id' => $cm->module], 'name');
if ($module) {
return new url("{$customwwwroot}/mod/{$module->name}/view.php", [
'id' => $cm->id,
'utm_source' => 'block_qrcode',
]);
}
}
}

return new url("{$customwwwroot}/course/view.php", [
'id' => $this->course->id,
'utm_source' => 'block_qrcode',
Expand Down
3 changes: 2 additions & 1 deletion download.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@
$format = required_param('format', PARAM_TEXT);
$instanceid = required_param('instance', PARAM_INT);
$size = optional_param('size', 300, PARAM_INT);
$modulecontextid = optional_param('modulecontextid', null, PARAM_INT);

if ($download) {
require_capability('block/qrcode:download', context_course::instance($courseid));
}

$outputimg = new block_qrcode\output_image($format, $size, $courseid, $instanceid);
$outputimg = new block_qrcode\output_image($format, $size, $courseid, $instanceid, $modulecontextid);
$outputimg->output_image($download);
117 changes: 117 additions & 0 deletions tests/output_image_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,121 @@ public function test_no_logo(): void {
$outputimg->create_image();
$this->assertFileExists($file);
}

/**
* The course link generation without modulecontextid returns course URL.
* @covers \block_qrcode\output_image::course_link_url
*/
public function test_course_link_url_without_activity(): void {
set_config('custom_wwwroot', '', 'block_qrcode');
$image = new output_image(1, 150, $this->course->id, $this->block->id);

$expectedurl = new url('/course/view.php', [
'id' => $this->course->id,
'utm_source' => 'block_qrcode',
]);

$this->assertEquals($expectedurl, $image->course_link_url());
}

/**
* The course link generation with valid modulecontextid returns activity URL.
* @covers \block_qrcode\output_image::course_link_url
*/
public function test_course_link_url_with_activity(): void {
global $DB;

set_config('custom_wwwroot', '', 'block_qrcode');

// Create a forum activity.
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $this->course->id]);
$cm = $DB->get_record('course_modules', ['course' => $this->course->id, 'instance' => $forum->id]);

$image = new output_image(1, 150, $this->course->id, $this->block->id, $cm->id);
$url = $image->course_link_url();

$this->assertStringContainsString('/mod/forum/view.php', $url->out(false));
$this->assertStringContainsString('id=' . $cm->id, $url->out(false));
$this->assertStringContainsString('utm_source=block_qrcode', $url->out(false));
}

/**
* The course link generation with activity falls back to course URL if module not found.
* @covers \block_qrcode\output_image::course_link_url
*/
public function test_course_link_url_with_invalid_activity(): void {
set_config('custom_wwwroot', '', 'block_qrcode');

// Use a non-existent modulecontextid.
$image = new output_image(1, 150, $this->course->id, $this->block->id, 99999);

$expectedurl = new url('/course/view.php', [
'id' => $this->course->id,
'utm_source' => 'block_qrcode',
]);

// Should fall back to course URL.
$this->assertEquals($expectedurl, $image->course_link_url());
}

/**
* The course link generation respects custom wwwroot with activity URL.
* @covers \block_qrcode\output_image::course_link_url
*/
public function test_course_link_url_with_activity_and_custom_wwwroot(): void {
global $DB;

$customwwwroot = 'https://moodle.example.de';
set_config('custom_wwwroot', $customwwwroot, 'block_qrcode');

// Create a quiz activity.
$quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $this->course->id]);
$cm = $DB->get_record('course_modules', ['course' => $this->course->id, 'instance' => $quiz->id]);

$image = new output_image(1, 150, $this->course->id, $this->block->id, $cm->id);
$url = $image->course_link_url();

$this->assertStringContainsString($customwwwroot . '/mod/quiz/view.php', $url->out(false));
$this->assertStringContainsString('id=' . $cm->id, $url->out(false));
}

/**
* The output_image constructor accepts optional modulecontextid parameter.
* @covers \block_qrcode\output_image::__construct
*/
public function test_output_image_constructor_with_modulecontextid(): void {
global $DB;

// Create an assignment activity.
$assign = $this->getDataGenerator()->create_module('assign', ['course' => $this->course->id]);
$cm = $DB->get_record('course_modules', ['course' => $this->course->id, 'instance' => $assign->id]);

// Should not throw any exceptions.
$image = new output_image(1, 150, $this->course->id, $this->block->id, $cm->id);
$this->assertInstanceOf(output_image::class, $image);
}

/**
* Different activity types generate correct module URLs.
* @covers \block_qrcode\output_image::course_link_url
*/
public function test_course_link_url_with_different_activity_types(): void {
global $DB;

set_config('custom_wwwroot', '', 'block_qrcode');

// Test with different activity types.
$activitytypes = ['forum', 'quiz', 'assign'];

foreach ($activitytypes as $type) {
$activity = $this->getDataGenerator()->create_module($type, ['course' => $this->course->id]);
$cm = $DB->get_record('course_modules', ['course' => $this->course->id, 'instance' => $activity->id]);

$image = new output_image(1, 150, $this->course->id, $this->block->id, $cm->id);
$url = $image->course_link_url()->out(false);

$this->assertStringContainsString("/mod/{$type}/view.php", $url);
$this->assertStringContainsString('id=' . $cm->id, $url);
}
}
}