MDL-87679 core_course: New event for viewing restricted elements

This commit is contained in:
Amaia Anabitarte
2026-02-17 15:28:40 +01:00
parent 5586d836a9
commit 4f774171ee
10 changed files with 244 additions and 3 deletions
@@ -0,0 +1,8 @@
issueNumber: MDL-87679
notes:
core_course:
- message: >-
New 'restricted' parameter has been added to course_section_view()
function to define whether the section was restricted for the user or
not.
type: improved
@@ -208,3 +208,12 @@ Feature: Display availability for activities and sections
And I should see "Section 1" in the "page-header" "region"
And I should see "Not available unless" in the "region-main" "region"
And "Page 1" "link" should not exist in the "region-main" "region"
# Check the logs.
And I am on the "C1" "Course" page logged in as "teacher1"
And I navigate to "Reports > Logs" in current page administration
And I click on "Get these logs" "button"
And I should see "Restricted section viewed"
And I click on "Restricted section viewed" "link"
And I switch to "action" window
And I should see "Section 1" in the "page-header" "region"
And I should see "Not available unless" in the "region-main" "region"
@@ -93,6 +93,14 @@ class restricted_module {
$response->getBody()->write($renderer->render($cmoutput));
$response->getBody()->write($OUTPUT->footer());
$eventdata = [
'objectid' => $cm->id,
'context' => $context,
];
$event = \core\event\course_restricted_module_viewed::create($eventdata);
$event->trigger();
user_accesstime_log($context->instanceid);
return $response;
}
}
@@ -60,6 +60,7 @@ class restricted_section {
require_once($CFG->dirroot . '/course/lib.php');
$course = get_course($section->course);
$context = \context_course::instance($course->id);
$format = course_get_format($course->id);
$format->set_sectionid($section->id);
$outputclass = $format->get_output_classname('content');
@@ -70,7 +71,7 @@ class restricted_section {
$PAGE->set_pagelayout('course');
$PAGE->add_body_classes(['limitedwidth', 'single-section-page']);
$PAGE->set_pagetype('course-view-section-' . $course->format . '-restricted');
$PAGE->set_context(\context_course::instance($course->id));
$PAGE->set_context($context);
$sectiontitle = $format->get_section_name($section);
$strtitle = get_string('restrictedtitle', 'course', $sectiontitle);
@@ -82,6 +83,9 @@ class restricted_section {
$response->getBody()->write($renderer->render($sectionoutput));
$response->getBody()->write($OUTPUT->footer());
// Trigger section viewed event.
course_section_view($context, $section->id, true);
return $response;
}
}
+7 -2
View File
@@ -4255,15 +4255,20 @@ function course_update_communication_instance_data(stdClass $data): void {
*
* @param context_course $context course context object
* @param int $sectionid section number
* @param bool $restricted Whether the section is restricted for the user or not.
* @since Moodle 4.4.
*/
function course_section_view(context_course $context, int $sectionid): void {
function course_section_view(context_course $context, int $sectionid, bool $restricted = false): void {
$eventdata = [
'objectid' => $sectionid,
'context' => $context,
];
$event = \core\event\section_viewed::create($eventdata);
if ($restricted) {
$event = \core\event\restricted_section_viewed::create($eventdata);
} else {
$event = \core\event\section_viewed::create($eventdata);
}
$event->trigger();
user_accesstime_log($context->instanceid);
+31
View File
@@ -7234,6 +7234,37 @@ final class courselib_test extends advanced_testcase {
$this->assertEquals($section->id, $event->objectid);
}
/**
* Test course_section_view() function for restricted sections.
*
* @covers ::course_section_view
*/
public function test_course_restricted_section_view(): void {
$this->resetAfterTest();
// Course without sections.
$course = $this->getDataGenerator()->create_course(['numsections' => 5], ['createsections' => true]);
$coursecontext = context_course::instance($course->id);
$format = course_get_format($course->id);
$sections = $format->get_sections();
$section = reset($sections);
// Redirect events to the sink, so we can recover them later.
$sink = $this->redirectEvents();
course_section_view($coursecontext, $section->id, true);
$events = $sink->get_events();
$event = reset($events);
// Check the event details are correct.
$this->assertInstanceOf('\core\event\restricted_section_viewed', $event);
$this->assertEquals(context_course::instance($course->id), $event->get_context());
$this->assertEquals('course_sections', $event->objecttable);
$this->assertEquals($section->id, $event->objectid);
}
/**
* Tests get_sorted_course_formats returns plugins in cases where plugins are
* installed previously but no longer exist, or not installed yet.
+2
View File
@@ -826,6 +826,7 @@ $string['eventcoursessearched'] = 'Courses searched';
$string['eventcourseresetended'] = 'Course reset ended';
$string['eventcourseresetstarted'] = 'Course reset started';
$string['eventcourserestored'] = 'Course restored';
$string['eventcourserestrictedmoduleviewed'] = 'Restricted module viewed';
$string['eventcoursestarted'] = 'Course started';
$string['eventcourseupdated'] = 'Course updated';
$string['eventcoursesectioncreated'] = 'Course section created';
@@ -842,6 +843,7 @@ $string['eventemailfailed'] = 'Email failed to send';
$string['eventmycoursesviewed'] = 'My courses viewed';
$string['eventname'] = 'Event name';
$string['eventrecentactivityviewed'] = 'Recent activity viewed';
$string['eventrestrictedsectionviewed'] = 'Restricted section viewed';
$string['eventsearchindexed'] = 'Search data indexed';
$string['eventsearchresultsviewed'] = 'Search results viewed';
$string['eventsectionviewed'] = 'Section viewed';
@@ -0,0 +1,67 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core\event;
/**
* Restricted module viewed event class.
*
* @package core
* @copyright 2026 Amaia Anabitarte <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_restricted_module_viewed extends \core\event\course_module_viewed {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'course_modules';
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
$cm = get_coursemodule_from_id(null, $this->contextinstanceid, 0, false, MUST_EXIST);
return "The user with id '$this->userid' viewed the restricted '{$cm->modname}' activity with " .
"course module id '$this->contextinstanceid'.";
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventcourserestrictedmoduleviewed', 'core');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
$cm = get_coursemodule_from_id(null, $this->contextinstanceid, 0, false, MUST_EXIST);
return new \moodle_url("/mod/$cm->modname/view.php", ['id' => $this->contextinstanceid]);
}
}
@@ -0,0 +1,44 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core\event;
/**
* Restricted section viewed event class.
*
* @package core
* @copyright 2026 Amaia Anabitarte <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restricted_section_viewed extends section_viewed {
/**
* Returns non-localised description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' viewed the restricted section with id '$this->objectid'.";
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventrestrictedsectionviewed', 'core');
}
}
@@ -0,0 +1,63 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core\event;
use advanced_testcase;
use context_module;
use stdClass;
use moodle_url;
/**
* Tests for base course module viewed event.
*
* @package core
* @covers \core\event\course_restricted_module_viewed
* @copyright 2026 Amaia Anabitarte <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class course_restricted_module_viewed_test extends advanced_testcase {
/**
* Test event properties and methods.
*/
public function test_event_attributes(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$record = new stdClass();
$record->course = $course->id;
$feed = $this->getDataGenerator()->create_module('feedback', $record);
$cm = get_coursemodule_from_instance('feedback', $feed->id);
$context = context_module::instance($cm->id);
// Trigger the page view event.
$sink = $this->redirectEvents();
$pageevent = course_restricted_module_viewed::create([
'context' => $context,
'courseid' => $course->id,
'objectid' => $cm->id,
]);
$pageevent->trigger();
$result = $sink->get_events();
$event = reset($result);
$sink->close();
$this->assertSame('course_modules', $event->objecttable);
$url = new moodle_url('/mod/feedback/view.php', ['id' => $cm->id]);
$this->assertEquals($url, $event->get_url());
$this->assertEventContextNotUsed($event);
}
}