mirror of
https://github.com/moodle/moodle.git
synced 2025-04-22 08:55:15 +02:00
Merge branch 'MDL-83892-main-v02' of https://github.com/ferranrecio/moodle
This commit is contained in:
commit
741c072b58
9
.upgradenotes/MDL-83892-2025022410503826.yml
Normal file
9
.upgradenotes/MDL-83892-2025022410503826.yml
Normal file
@ -0,0 +1,9 @@
|
||||
issueNumber: MDL-83892
|
||||
notes:
|
||||
core:
|
||||
- message: >-
|
||||
A new method get_instance_record has been added to cm_info object so
|
||||
core can get the activity table record without using the $DB object
|
||||
every time. Also, the method caches de result so getting more than once
|
||||
per execution is much faster.
|
||||
type: improved
|
@ -21,6 +21,7 @@ use core\context\module as module_context;
|
||||
use core_completion\cm_completion_details;
|
||||
use core_courseformat\local\overview\overviewitem;
|
||||
use core_courseformat\output\local\overview\activityname;
|
||||
use core_courseformat\output\local\overview\overviewpage;
|
||||
use core_courseformat\base as courseformat;
|
||||
|
||||
/**
|
||||
@ -69,6 +70,16 @@ abstract class activityoverviewbase {
|
||||
$this->format = courseformat::instance($this->course);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects to the overview page for the activity.
|
||||
*
|
||||
* @param int $courseid The course id.
|
||||
* @param string $modname The module name.
|
||||
*/
|
||||
public static function redirect_to_overview_page(int $courseid, string $modname): void {
|
||||
redirect(overviewpage::get_modname_url($courseid, $modname));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin specific overview items for the activity.
|
||||
*
|
||||
|
@ -53,13 +53,17 @@ class activityname implements renderable, named_templatable {
|
||||
$section = $this->cm->get_section_info();
|
||||
$course = $this->cm->get_course();
|
||||
$format = course_format::instance($course);
|
||||
return (object) [
|
||||
|
||||
$result = (object) [
|
||||
'activityname' => \core_external\util::format_string($cm->name, $cm->context, true),
|
||||
'activityurl' => $cm->url,
|
||||
'sectiontitle' => $format->get_section_name($section),
|
||||
'hidden' => empty($cm->visible),
|
||||
'stealth' => $cm->is_stealth(),
|
||||
];
|
||||
if ($format->uses_sections()) {
|
||||
$result->sectiontitle = $format->get_section_name($section);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,6 +51,21 @@ class overviewpage implements renderable, named_templatable {
|
||||
$this->context = context_course::instance($this->course->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL to the course overview page for a given course and module name.
|
||||
*
|
||||
* @param int $courseid
|
||||
* @param string $modname
|
||||
* @return url
|
||||
*/
|
||||
public static function get_modname_url(int $courseid, string $modname): url {
|
||||
return new url(
|
||||
url: '/course/overview.php',
|
||||
params: ['id' => $courseid, 'expand[]' => $modname],
|
||||
anchor: "{$modname}_overview_collapsible",
|
||||
);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function export_for_template(\renderer_base $output): stdClass {
|
||||
$modfullnames = $this->get_course_activities_overview_list();
|
||||
|
@ -32,15 +32,23 @@
|
||||
<div class="fw-bold">
|
||||
<a href="{{activityurl}}" class="activityname">{{activityname}}</a>
|
||||
</div>
|
||||
{{#sectiontitle}}
|
||||
<div>
|
||||
{{sectiontitle}}
|
||||
</div>
|
||||
{{/sectiontitle}}
|
||||
<div class="ml-2">
|
||||
{{#hidden}}
|
||||
<span class="badge badge-success">{{#str}}hiddenfromstudents{{/str}}</span>
|
||||
<span class="badge rounded-pill text-bg-secondary fw-normal">
|
||||
{{#pix}}i/show, core{{/pix}}
|
||||
{{#str}}hiddenfromstudents{{/str}}
|
||||
</span>
|
||||
{{/hidden}}
|
||||
{{#stealth}}
|
||||
<span class="badge badge-warning">{{#str}}hiddenoncoursepage{{/str}}</span>
|
||||
<span class="badge rounded-pill text-bg-secondary fw-normal">
|
||||
{{#pix}}t/stealth, core{{/pix}}
|
||||
{{#str}}hiddenoncoursepage{{/str}}
|
||||
</span>
|
||||
{{/stealth}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -72,7 +72,7 @@
|
||||
]
|
||||
}
|
||||
}}
|
||||
<div class="course-overview border border-secondary border-1 rounded">
|
||||
<div class="course-overview border border-secondary border-1 rounded pt-2">
|
||||
<table
|
||||
class="course-overview-table boxaligncenter {{!
|
||||
}} table table-responsive w-100 d-block d-md-table"
|
||||
|
@ -4832,8 +4832,32 @@ function course_output_fragment_course_overview($args) {
|
||||
}
|
||||
$modname = $args['modname'];
|
||||
$course = get_course($args['courseid']);
|
||||
$context = context_course::instance($course->id, MUST_EXIST);
|
||||
can_access_course($course);
|
||||
|
||||
// Some plugins may have a list view event.
|
||||
$eventclassname = 'mod_' . $modname . '\\event\\course_module_instance_list_viewed';
|
||||
// Do not confuse this "resource" with the "mod_resource" module.
|
||||
// This "resource" is the table that aggregate all activities considered "resources"
|
||||
// (files, folders, pages, text and media...). While the "mod_resource" is a poorly
|
||||
// named plugin representing an uploaded file, and it is also one of the activities
|
||||
// that can be aggregated in the "resource" table.
|
||||
if ($modname === 'resource') {
|
||||
$eventclassname = 'core\\event\\course_resources_list_viewed';
|
||||
}
|
||||
if (class_exists($eventclassname)) {
|
||||
try {
|
||||
$event = $eventclassname::create(['context' => $context]);
|
||||
$event->add_record_snapshot('course', $course);
|
||||
$event->trigger();
|
||||
} catch (\Throwable $th) {
|
||||
// This may happens if the plugin implements a custom event class.
|
||||
// It is highly unlikely but we should not stop the rendering because of this.
|
||||
// Instead, we will log the error and continue.
|
||||
debugging('Error while triggering the course module instance viewed event: ' . $th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$content = '';
|
||||
$format = course_get_format($course);
|
||||
$renderer = $format->get_renderer($PAGE);
|
||||
|
@ -40,6 +40,11 @@ $context = context_course::instance($course->id, MUST_EXIST);
|
||||
require_login($course);
|
||||
require_capability('moodle/course:viewoverview', $context);
|
||||
|
||||
// Trigger event, course information viewed.
|
||||
$event = \core\event\course_overview_viewed::create(['context' => $context]);
|
||||
$event->add_record_snapshot('course', $course);
|
||||
$event->trigger();
|
||||
|
||||
$format = course_get_format($course);
|
||||
$renderer = $format->get_renderer($PAGE);
|
||||
$overviewpageclass = $format->get_output_classname('overview\\overviewpage');
|
||||
|
@ -24,127 +24,7 @@
|
||||
*/
|
||||
|
||||
require_once('../config.php');
|
||||
require_once("$CFG->libdir/resourcelib.php");
|
||||
|
||||
$id = required_param('id', PARAM_INT); // course id
|
||||
$courseid = required_param('id', PARAM_INT);
|
||||
|
||||
$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
|
||||
$PAGE->set_pagelayout('incourse');
|
||||
require_course_login($course, true);
|
||||
|
||||
// get list of all resource-like modules
|
||||
$allmodules = $DB->get_records('modules', array('visible'=>1));
|
||||
$availableresources = array();
|
||||
foreach ($allmodules as $key=>$module) {
|
||||
$modname = $module->name;
|
||||
$libfile = "$CFG->dirroot/mod/$modname/lib.php";
|
||||
if (!file_exists($libfile)) {
|
||||
continue;
|
||||
}
|
||||
$archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
|
||||
if ($archetype != MOD_ARCHETYPE_RESOURCE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$availableresources[] = $modname;
|
||||
}
|
||||
|
||||
// Triger view event.
|
||||
$event = \core\event\course_resources_list_viewed::create(array('context' => context_course::instance($course->id)));
|
||||
$event->add_record_snapshot('course', $course);
|
||||
$event->trigger();
|
||||
|
||||
$strresources = get_string('resources');
|
||||
$strname = get_string('name');
|
||||
$strintro = get_string('moduleintro');
|
||||
$strlastmodified = get_string('lastmodified');
|
||||
|
||||
$PAGE->set_url('/course/resources.php', array('id' => $course->id));
|
||||
$PAGE->set_title($course->shortname.': '.$strresources);
|
||||
$PAGE->set_heading($course->fullname);
|
||||
$PAGE->navbar->add($strresources);
|
||||
echo $OUTPUT->header();
|
||||
|
||||
$modinfo = get_fast_modinfo($course);
|
||||
$usesections = course_format_uses_sections($course->format);
|
||||
$cms = array();
|
||||
$resources = array();
|
||||
foreach ($modinfo->cms as $cm) {
|
||||
if (!in_array($cm->modname, $availableresources)) {
|
||||
continue;
|
||||
}
|
||||
// Exclude activities that aren't visible or have no view link (e.g. label). Account for folder being displayed inline.
|
||||
if (!$cm->uservisible || (!$cm->has_view() && strcmp($cm->modname, 'folder') !== 0)) {
|
||||
continue;
|
||||
}
|
||||
$cms[$cm->id] = $cm;
|
||||
$resources[$cm->modname][] = $cm->instance;
|
||||
}
|
||||
|
||||
// preload instances
|
||||
foreach ($resources as $modname=>$instances) {
|
||||
$additionalfields = '';
|
||||
if (plugin_supports('mod', $modname, FEATURE_MOD_INTRO)) {
|
||||
$additionalfields = ',intro,introformat';
|
||||
}
|
||||
$resources[$modname] = $DB->get_records_list($modname, 'id', $instances, 'id', 'id,name'.$additionalfields);
|
||||
}
|
||||
|
||||
if (!$cms) {
|
||||
notice(get_string('thereareno', 'moodle', $strresources), "$CFG->wwwroot/course/view.php?id=$course->id");
|
||||
exit;
|
||||
}
|
||||
|
||||
$table = new html_table();
|
||||
$table->attributes['class'] = 'generaltable mod_index';
|
||||
|
||||
if ($usesections) {
|
||||
|
||||
$strsectionname = course_get_format($course)->get_generic_section_name();
|
||||
$table->head = array ($strsectionname, $strname, $strintro);
|
||||
$table->align = array ('center', 'left', 'left');
|
||||
} else {
|
||||
$table->head = array ($strlastmodified, $strname, $strintro);
|
||||
$table->align = array ('left', 'left', 'left');
|
||||
}
|
||||
|
||||
$currentsection = '';
|
||||
foreach ($cms as $cm) {
|
||||
if (!isset($resources[$cm->modname][$cm->instance])) {
|
||||
continue;
|
||||
}
|
||||
$resource = $resources[$cm->modname][$cm->instance];
|
||||
$printsection = '';
|
||||
if ($usesections) {
|
||||
if ($cm->sectionnum !== $currentsection) {
|
||||
if ($cm->sectionnum) {
|
||||
$printsection = get_section_name($course, $cm->sectionnum);
|
||||
}
|
||||
if ($currentsection !== '') {
|
||||
$table->data[] = 'hr';
|
||||
}
|
||||
$currentsection = $cm->sectionnum;
|
||||
}
|
||||
}
|
||||
|
||||
$extra = empty($cm->extra) ? '' : $cm->extra;
|
||||
$icon = '<img src="'.$cm->get_icon_url().'" class="activityicon" alt="'.$cm->get_module_type_name().'" /> ';
|
||||
|
||||
if (isset($resource->intro) && isset($resource->introformat)) {
|
||||
$intro = format_module_intro($cm->modname, $resource, $cm->id);
|
||||
} else {
|
||||
$intro = '';
|
||||
}
|
||||
|
||||
$class = $cm->visible ? '' : 'class="dimmed"'; // hidden modules are dimmed
|
||||
$url = $cm->url ?: new moodle_url("/mod/{$cm->modname}/view.php", ['id' => $cm->id]);
|
||||
|
||||
$table->data[] = array (
|
||||
$printsection,
|
||||
"<a $class $extra href=\"" . $url ."\">" . $icon . $cm->get_formatted_name() . "</a>",
|
||||
$intro);
|
||||
}
|
||||
|
||||
echo html_writer::table($table);
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
\core_courseformat\activityoverviewbase::redirect_to_overview_page($courseid, 'resource');
|
||||
|
@ -215,3 +215,38 @@ Feature: Users can access the course activities overview page
|
||||
When I am on the "Course 1" "course > activities > resource" page logged in as "student1"
|
||||
Then I should see "To do" in the "Activity 1" "table_row"
|
||||
And I should see "View" in the "Activity 1" "table_row"
|
||||
|
||||
Scenario: The course overview page should log a page event and a reource list event
|
||||
Given the following "activity" exists:
|
||||
| activity | folder |
|
||||
| name | Activity 1 |
|
||||
| course | C1 |
|
||||
And I am on the "Course 1" "course > activities" page logged in as "teacher1"
|
||||
And I am on the "Course 1" "course > activities > resource" page logged in as "student1"
|
||||
When I am on the "Course 1" "course" page logged in as "teacher1"
|
||||
And I navigate to "Reports" in current page administration
|
||||
And I click on "Logs" "link"
|
||||
Then I set the field "Select a user" to "Teacher 1"
|
||||
And I click on "Get these logs" "button"
|
||||
And I should see "Course activities overview page viewed"
|
||||
And I should not see "viewed the list of resources"
|
||||
And I set the field "Select a user" to "Student 1"
|
||||
And I click on "Get these logs" "button"
|
||||
And I should see "Course activities overview page viewed"
|
||||
And I should see "viewed the list of resources"
|
||||
|
||||
@javascript
|
||||
Scenario: The course overview page should log reource list event when loading the overview table
|
||||
Given the following "activity" exists:
|
||||
| activity | folder |
|
||||
| name | Activity 1 |
|
||||
| course | C1 |
|
||||
And I am on the "Course 1" "course > activities" page logged in as "teacher1"
|
||||
And I click on "Expand" "link" in the "resource_overview_collapsible" "region"
|
||||
When I am on the "Course 1" "course" page logged in as "teacher1"
|
||||
And I navigate to "Reports" in current page administration
|
||||
And I click on "Logs" "link"
|
||||
And I set the field "Select a user" to "Teacher 1"
|
||||
And I click on "Get these logs" "button"
|
||||
Then I should see "Course activities overview page viewed"
|
||||
And I should see "viewed the list of resources"
|
||||
|
@ -137,4 +137,37 @@ final class events_test extends \advanced_testcase {
|
||||
$sink->close();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the course activities overview page viewed.
|
||||
*
|
||||
* There is no external API for viewing course information so the unit test will simply
|
||||
* create and trigger the event and ensure data is returned as expected.
|
||||
*
|
||||
* @covers \core\event\course_overview_viewed
|
||||
*/
|
||||
public function test_course_overview_viewed_event(): void {
|
||||
|
||||
// Create a course.
|
||||
$data = new \stdClass();
|
||||
$course = $this->getDataGenerator()->create_course($data);
|
||||
|
||||
$eventparams = [
|
||||
'context' => \context_course::instance($course->id),
|
||||
];
|
||||
$event = \core\event\course_overview_viewed::create($eventparams);
|
||||
|
||||
// Trigger and capture the event.
|
||||
$sink = $this->redirectEvents();
|
||||
$event->trigger();
|
||||
$events = $sink->get_events();
|
||||
$event = reset($events);
|
||||
|
||||
// Check that the event data is valid.
|
||||
$this->assertInstanceOf('\core\event\course_overview_viewed', $event);
|
||||
$this->assertEquals($course->id, $event->courseid);
|
||||
$this->assertDebuggingNotCalled();
|
||||
$sink->close();
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -343,6 +343,7 @@ $string['copyrightnotice'] = 'Copyright notice';
|
||||
$string['coresystem'] = 'System';
|
||||
$string['cost'] = 'Cost';
|
||||
$string['costdefault'] = 'Default cost';
|
||||
$string['count_of_total'] = '<strong>{$a->count}</strong> of {$a->total}';
|
||||
$string['counteditems'] = '{$a->count} {$a->items}';
|
||||
$string['country'] = 'Country';
|
||||
$string['course'] = 'Course';
|
||||
@ -834,6 +835,7 @@ $string['eventcoursemodulecreated'] = 'Course module created';
|
||||
$string['eventcoursemoduledeleted'] = 'Course module deleted';
|
||||
$string['eventcoursemoduleupdated'] = 'Course module updated';
|
||||
$string['eventcoursemoduleviewed'] = 'Course module viewed';
|
||||
$string['eventcourseoverviewviewed'] = 'Course activities overview page viewed';
|
||||
$string['eventcoursessearched'] = 'Courses searched';
|
||||
$string['eventcourseresetended'] = 'Course reset ended';
|
||||
$string['eventcourseresetstarted'] = 'Course reset started';
|
||||
|
77
lib/classes/event/course_overview_viewed.php
Normal file
77
lib/classes/event/course_overview_viewed.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?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 core\url;
|
||||
|
||||
/**
|
||||
* Event course_overview_viewed
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2025 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class course_overview_viewed extends \core\event\base {
|
||||
|
||||
/**
|
||||
* Set basic properties for the event.
|
||||
*/
|
||||
protected function init() {
|
||||
$this->data['crud'] = 'r';
|
||||
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of what happened.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description(): string {
|
||||
return "The user with id '$this->userid' viewed the course activity overview for the course with id '$this->courseid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name(): string {
|
||||
return get_string('eventcourseoverviewviewed', 'core');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL related to the action.
|
||||
*
|
||||
* @return url|null
|
||||
*/
|
||||
public function get_url() {
|
||||
return new url('/course/overview.php', ['id' => $this->courseid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom validation.
|
||||
*
|
||||
* @throws \coding_exception
|
||||
* @return void
|
||||
*/
|
||||
protected function validate_data() {
|
||||
parent::validate_data();
|
||||
if ($this->contextlevel != CONTEXT_COURSE) {
|
||||
throw new \coding_exception('Context level must be CONTEXT_COURSE.');
|
||||
}
|
||||
}
|
||||
}
|
@ -1481,6 +1481,12 @@ class cm_info implements IteratorAggregate {
|
||||
*/
|
||||
private $iconcomponent;
|
||||
|
||||
/**
|
||||
* The instance record form the module table
|
||||
* @var stdClass
|
||||
*/
|
||||
private $instancerecord;
|
||||
|
||||
/**
|
||||
* Name of module e.g. 'forum' (this is the same name as the module's main database
|
||||
* table) - from cached data in modinfo field
|
||||
@ -2216,6 +2222,25 @@ class cm_info implements IteratorAggregate {
|
||||
return $cmrecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the activity database table record.
|
||||
*
|
||||
* The instance record will be cached after the first call.
|
||||
*
|
||||
* @return stdClass
|
||||
*/
|
||||
public function get_instance_record() {
|
||||
global $DB;
|
||||
if (!isset($this->instancerecord)) {
|
||||
$this->instancerecord = $DB->get_record(
|
||||
table: $this->modname,
|
||||
conditions: ['id' => $this->instance],
|
||||
strictness: MUST_EXIST,
|
||||
);
|
||||
}
|
||||
return $this->instancerecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the section delegated by this module, if any.
|
||||
*
|
||||
|
@ -2240,4 +2240,34 @@ final class modinfolib_test extends advanced_testcase {
|
||||
$cms = $sectioninfo->get_sequence_cm_infos();
|
||||
$this->assertCount(0, $cms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for cm_info::get_instance_record
|
||||
*
|
||||
* @covers \cm_info::get_instance_record
|
||||
* @return void
|
||||
*/
|
||||
public function test_section_get_instance_record(): void {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course(['numsections' => 2]);
|
||||
$activity = $this->getDataGenerator()->create_module('page', ['course' => $course], ['section' => 0]);
|
||||
|
||||
$modinfo = get_fast_modinfo($course->id);
|
||||
$cminfo = $modinfo->get_cm($activity->cmid);
|
||||
|
||||
$instancerecord = $DB->get_record('page', ['id' => $activity->id]);
|
||||
|
||||
$instance = $cminfo->get_instance_record();
|
||||
$this->assertEquals($instancerecord, $instance);
|
||||
|
||||
// The instance record should be cached.
|
||||
$DB->delete_records('page', ['id' => $activity->id]);
|
||||
|
||||
$instance2 = $cminfo->get_instance_record();
|
||||
$this->assertEquals($instancerecord, $instance);
|
||||
$this->assertEquals($instance, $instance2);
|
||||
}
|
||||
}
|
||||
|
137
mod/feedback/classes/courseformat/overview.php
Normal file
137
mod/feedback/classes/courseformat/overview.php
Normal file
@ -0,0 +1,137 @@
|
||||
<?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 mod_feedback\courseformat;
|
||||
|
||||
use core_courseformat\local\overview\overviewitem;
|
||||
use core\output\action_link;
|
||||
use core\output\local\properties\button;
|
||||
use core\output\local\properties\text_align;
|
||||
use core\url;
|
||||
use core\output\pix_icon;
|
||||
|
||||
/**
|
||||
* Class overview
|
||||
*
|
||||
* @package mod_feedback
|
||||
* @copyright 2025 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class overview extends \core_courseformat\activityoverviewbase {
|
||||
#[\Override]
|
||||
public function get_extra_overview_items(): array {
|
||||
return [
|
||||
'submitted' => $this->get_extra_submitted_overview(),
|
||||
];
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function get_actions_overview(): ?overviewitem {
|
||||
global $CFG, $USER;
|
||||
|
||||
if (!has_capability('mod/feedback:viewreports', $this->context)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
require_once($CFG->dirroot . '/mod/feedback/lib.php');
|
||||
|
||||
$submissions = feedback_get_completeds_group_count(
|
||||
$this->cm->get_instance_record()
|
||||
);
|
||||
// Normalize the value.
|
||||
if (!$submissions) {
|
||||
$submissions = 0;
|
||||
}
|
||||
$total = $submissions + feedback_count_incomplete_users($this->cm);
|
||||
|
||||
$content = new action_link(
|
||||
url: new url('/mod/feedback/show_entries.php', ['id' => $this->cm->id]),
|
||||
text: get_string(
|
||||
'count_of_total',
|
||||
'core',
|
||||
['count' => $submissions, 'total' => $total]
|
||||
),
|
||||
attributes: ['class' => button::SECONDARY_OUTLINE->classes()],
|
||||
);
|
||||
|
||||
return new overviewitem(
|
||||
name: get_string('responses', 'mod_feedback'),
|
||||
value: $submissions,
|
||||
content: $content,
|
||||
textalign: text_align::CENTER,
|
||||
);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function get_due_date_overview(): ?overviewitem {
|
||||
$duedate = null;
|
||||
if (isset($this->cm->customdata['timeclose'])) {
|
||||
$duedate = $this->cm->customdata['timeclose'];
|
||||
}
|
||||
|
||||
if (empty($duedate)) {
|
||||
return new overviewitem(
|
||||
name: get_string('feedbackclose', 'mod_feedback'),
|
||||
value: null,
|
||||
content: '-',
|
||||
);
|
||||
}
|
||||
return new overviewitem(
|
||||
name: get_string('feedbackclose', 'mod_feedback'),
|
||||
value: $duedate,
|
||||
content: userdate($duedate),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the submitted status overview item.
|
||||
*
|
||||
* @return overviewitem|null The overview item (or null if the user cannot complete the feedback).
|
||||
*/
|
||||
private function get_extra_submitted_overview(): ?overviewitem {
|
||||
global $USER;
|
||||
|
||||
if (!has_capability('mod/feedback:complete', $this->context)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$structure = new \mod_feedback_structure(
|
||||
feedback: $this->cm->get_instance_record(),
|
||||
cm: $this->cm,
|
||||
courseid: $this->course->id,
|
||||
userid: $USER->id,
|
||||
);
|
||||
|
||||
$value = false;
|
||||
$content = '-';
|
||||
|
||||
if ($structure->is_already_submitted()) {
|
||||
$value = true;
|
||||
$content = new pix_icon(
|
||||
'i/checkedcircle',
|
||||
alt: get_string('this_feedback_is_already_submitted', 'mod_feedback'),
|
||||
attributes: ['class' => 'text-success'],
|
||||
);
|
||||
}
|
||||
|
||||
return new overviewitem(
|
||||
name: get_string('responded', 'mod_feedback'),
|
||||
value: $value,
|
||||
content: $content,
|
||||
textalign: text_align::CENTER,
|
||||
);
|
||||
}
|
||||
}
|
@ -134,7 +134,11 @@ if ($feedbackcompletion->is_empty()) {
|
||||
}
|
||||
} else {
|
||||
echo $OUTPUT->box_start('generalbox boxaligncenter');
|
||||
echo $OUTPUT->notification(get_string('this_feedback_is_already_submitted', 'feedback'));
|
||||
echo $OUTPUT->notification(
|
||||
get_string('this_feedback_is_already_submitted', 'feedback'),
|
||||
\core\output\notification::NOTIFY_INFO,
|
||||
closebutton: false,
|
||||
);
|
||||
echo $OUTPUT->continue_button(course_get_url($courseid ?: $course->id));
|
||||
echo $OUTPUT->box_end();
|
||||
}
|
||||
|
@ -23,107 +23,7 @@
|
||||
*/
|
||||
|
||||
require_once("../../config.php");
|
||||
require_once("lib.php");
|
||||
|
||||
$id = required_param('id', PARAM_INT);
|
||||
$courseid = required_param('id', PARAM_INT);
|
||||
|
||||
$url = new moodle_url('/mod/feedback/index.php', array('id'=>$id));
|
||||
|
||||
$PAGE->set_url($url);
|
||||
|
||||
if (!$course = $DB->get_record('course', array('id'=>$id))) {
|
||||
throw new \moodle_exception('invalidcourseid');
|
||||
}
|
||||
|
||||
$context = context_course::instance($course->id);
|
||||
|
||||
require_login($course);
|
||||
$PAGE->set_pagelayout('incourse');
|
||||
$PAGE->add_body_class('limitedwidth');
|
||||
|
||||
// Trigger instances list viewed event.
|
||||
$event = \mod_feedback\event\course_module_instance_list_viewed::create(array('context' => $context));
|
||||
$event->add_record_snapshot('course', $course);
|
||||
$event->trigger();
|
||||
|
||||
/// Print the page header
|
||||
$strfeedbacks = get_string("modulenameplural", "feedback");
|
||||
$strfeedback = get_string("modulename", "feedback");
|
||||
|
||||
$PAGE->navbar->add($strfeedbacks);
|
||||
$PAGE->set_heading($course->fullname);
|
||||
$PAGE->set_title(get_string('modulename', 'feedback').' '.get_string('activities'));
|
||||
echo $OUTPUT->header();
|
||||
if (!$PAGE->has_secondary_navigation()) {
|
||||
echo $OUTPUT->heading($strfeedbacks);
|
||||
}
|
||||
|
||||
/// Get all the appropriate data
|
||||
|
||||
if (! $feedbacks = get_all_instances_in_course("feedback", $course)) {
|
||||
$url = new moodle_url('/course/view.php', array('id'=>$course->id));
|
||||
notice(get_string('thereareno', 'moodle', $strfeedbacks), $url);
|
||||
die;
|
||||
}
|
||||
|
||||
$usesections = course_format_uses_sections($course->format);
|
||||
|
||||
/// Print the list of instances (your module will probably extend this)
|
||||
|
||||
$timenow = time();
|
||||
$strname = get_string("name");
|
||||
$strresponses = get_string('responses', 'feedback');
|
||||
|
||||
$table = new html_table();
|
||||
|
||||
if ($usesections) {
|
||||
$strsectionname = course_get_format($course)->get_generic_section_name();
|
||||
if (has_capability('mod/feedback:viewreports', $context)) {
|
||||
$table->head = array ($strsectionname, $strname, $strresponses);
|
||||
$table->align = array ("center", "left", 'center');
|
||||
} else {
|
||||
$table->head = array ($strsectionname, $strname);
|
||||
$table->align = array ("center", "left");
|
||||
}
|
||||
} else {
|
||||
if (has_capability('mod/feedback:viewreports', $context)) {
|
||||
$table->head = array ($strname, $strresponses);
|
||||
$table->align = array ("left", "center");
|
||||
} else {
|
||||
$table->head = array ($strname);
|
||||
$table->align = array ("left");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach ($feedbacks as $feedback) {
|
||||
//get the responses of each feedback
|
||||
$viewurl = new moodle_url('/mod/feedback/view.php', array('id'=>$feedback->coursemodule));
|
||||
|
||||
if (has_capability('mod/feedback:viewreports', $context)) {
|
||||
$completed_feedback_count = intval(feedback_get_completeds_group_count($feedback));
|
||||
}
|
||||
|
||||
$dimmedclass = $feedback->visible ? '' : 'class="dimmed"';
|
||||
$link = '<a '.$dimmedclass.' href="'.$viewurl->out().'">'.$feedback->name.'</a>';
|
||||
|
||||
if ($usesections) {
|
||||
$tabledata = array (get_section_name($course, $feedback->section), $link);
|
||||
} else {
|
||||
$tabledata = array ($link);
|
||||
}
|
||||
if (has_capability('mod/feedback:viewreports', $context)) {
|
||||
$tabledata[] = $completed_feedback_count;
|
||||
}
|
||||
|
||||
$table->data[] = $tabledata;
|
||||
|
||||
}
|
||||
|
||||
echo "<br />";
|
||||
|
||||
echo html_writer::table($table);
|
||||
|
||||
/// Finish the page
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
\core_courseformat\activityoverviewbase::redirect_to_overview_page($courseid, 'feedback');
|
||||
|
@ -114,7 +114,7 @@ $string['eventresponsedeleted'] = 'Response deleted';
|
||||
$string['eventresponsesubmitted'] = 'Response submitted';
|
||||
$string['feedbackcompleted'] = '{$a->username} completed {$a->feedbackname}';
|
||||
$string['feedback:addinstance'] = 'Add a new feedback';
|
||||
$string['feedbackclose'] = 'Allow answers to';
|
||||
$string['feedbackclose'] = 'Allow answers until';
|
||||
$string['feedback:complete'] = 'Complete a feedback';
|
||||
$string['feedback:createprivatetemplate'] = 'Create private template';
|
||||
$string['feedback:createpublictemplate'] = 'Create public template';
|
||||
@ -256,6 +256,7 @@ $string['required'] = 'Required';
|
||||
$string['resetting_data'] = 'Responses';
|
||||
$string['resetting_delete'] = 'Delete responses';
|
||||
$string['resetting_feedbacks'] = 'Resetting feedbacks';
|
||||
$string['responded'] = 'Responded';
|
||||
$string['response_nr'] = 'Response number';
|
||||
$string['responses'] = 'Responses';
|
||||
$string['responsetime'] = 'Responses time';
|
||||
@ -295,7 +296,7 @@ $string['textfield'] = 'Short text answer';
|
||||
$string['textfield_maxlength'] = 'Maximum characters accepted';
|
||||
$string['textfield_size'] = 'Textfield width';
|
||||
$string['there_are_no_settings_for_recaptcha'] = 'There are no settings for captcha';
|
||||
$string['this_feedback_is_already_submitted'] = 'You\'ve already completed this activity.';
|
||||
$string['this_feedback_is_already_submitted'] = 'You have already submitted this feedback.';
|
||||
$string['typemissing'] = 'Missing value "type"';
|
||||
$string['update_item'] = 'Save changes to question';
|
||||
$string['url_for_continue'] = 'Link to next activity';
|
||||
|
89
mod/feedback/tests/behat/overview_report.feature
Normal file
89
mod/feedback/tests/behat/overview_report.feature
Normal file
@ -0,0 +1,89 @@
|
||||
@mod @mod_feedback
|
||||
Feature: Testing overview integration in mod_feedback
|
||||
In order to list all feedbacks in a course
|
||||
As a user
|
||||
I need to be able to see the feedback overview
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname |
|
||||
| student1 | Username | 1 |
|
||||
| student2 | Username | 2 |
|
||||
| student3 | Username | 3 |
|
||||
| student4 | Username | 4 |
|
||||
| student5 | Username | 5 |
|
||||
| student6 | Username | 6 |
|
||||
| student7 | Username | 7 |
|
||||
| student8 | Username | 8 |
|
||||
| teacher1 | Teacher | T |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | groupmode |
|
||||
| Course 1 | C1 | 1 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
| student3 | C1 | student |
|
||||
| student4 | C1 | student |
|
||||
| student5 | C1 | student |
|
||||
| student6 | C1 | student |
|
||||
| student7 | C1 | student |
|
||||
| student8 | C1 | student |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And the following "activities" exist:
|
||||
| activity | name | course | idnumber | timeclose |
|
||||
| feedback | Date feedback | C1 | feedback1 | ##1 Jan 2040 08:00## |
|
||||
| feedback | Not responded feedback | C1 | feedback2 | ##1 Jan 2040 08:00## |
|
||||
| feedback | No date feedback | C1 | feedback3 | |
|
||||
Given the following "mod_feedback > question" exists:
|
||||
| activity | feedback1 |
|
||||
| name | Do you like this course? |
|
||||
| questiontype | multichoice |
|
||||
| label | multichoice1 |
|
||||
| subtype | r |
|
||||
| hidenoselect | 1 |
|
||||
| values | Yes of course\nNot at all\nI don't know |
|
||||
And the following "mod_feedback > responses" exist:
|
||||
| activity | user | Do you like this course? |
|
||||
| feedback1 | student1 | Not at all |
|
||||
| feedback1 | student2 | I don't know |
|
||||
| feedback1 | student3 | Not at all |
|
||||
| feedback1 | student4 | Yes of course |
|
||||
| feedback3 | student1 | Not at all |
|
||||
| feedback3 | student2 | I don't know |
|
||||
| feedback3 | student3 | Not at all |
|
||||
|
||||
Scenario: Teacher can see the feedback relevant information in the feedback overview
|
||||
When I am on the "Course 1" "course > activities > feedback" page logged in as "teacher1"
|
||||
Then I should see "Responses" in the "feedback_overview_collapsible" "region"
|
||||
And I should not see "Responded" in the "feedback_overview_collapsible" "region"
|
||||
And I should see "Allow answers until" in the "feedback_overview_collapsible" "region"
|
||||
And I should see "1 January 2040" in the "Date feedback" "table_row"
|
||||
And I should see "4 of 8" in the "Date feedback" "table_row"
|
||||
And I should see "1 January 2040" in the "Not responded feedback" "table_row"
|
||||
And I should see "0 of 8" in the "Not responded feedback" "table_row"
|
||||
And I should see "-" in the "No date feedback" "table_row"
|
||||
And I should see "3 of 8" in the "No date feedback" "table_row"
|
||||
And I click on "4 of 8" "link" in the "Date feedback" "table_row"
|
||||
And I should see "Show responses"
|
||||
|
||||
Scenario: Students can see the feedback relevant information in the feedback overview
|
||||
When I am on the "Course 1" "course > activities > feedback" page logged in as "student1"
|
||||
Then I should not see "Responses" in the "feedback_overview_collapsible" "region"
|
||||
And I should see "Responded" in the "feedback_overview_collapsible" "region"
|
||||
And I should see "Allow answers until" in the "feedback_overview_collapsible" "region"
|
||||
And I should see "1 January 2040" in the "Date feedback" "table_row"
|
||||
And "You have already submitted this feedback" "icon" should exist in the "Date feedback" "table_row"
|
||||
And I should see "1 January 2040" in the "Not responded feedback" "table_row"
|
||||
And I should see "-" in the "Not responded feedback" "table_row"
|
||||
And I should see "-" in the "No date feedback" "table_row"
|
||||
And "You have already submitted this feedback" "icon" should exist in the "No date feedback" "table_row"
|
||||
|
||||
Scenario: The feedback overview report should generate log events
|
||||
Given I am on the "Course 1" "course > activities > feedback" page logged in as "teacher1"
|
||||
When I am on the "Course 1" "course" page logged in as "teacher1"
|
||||
And I navigate to "Reports" in current page administration
|
||||
And I click on "Logs" "link"
|
||||
And I click on "Get these logs" "button"
|
||||
Then I should see "Course activities overview page viewed"
|
||||
And I should see "viewed the instance list for the module 'feedback'"
|
269
mod/feedback/tests/courseformat/overview_test.php
Normal file
269
mod/feedback/tests/courseformat/overview_test.php
Normal file
@ -0,0 +1,269 @@
|
||||
<?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 mod_feedback\courseformat;
|
||||
|
||||
use core_courseformat\local\overview\overviewfactory;
|
||||
|
||||
/**
|
||||
* Tests for Feedback
|
||||
*
|
||||
* @covers \mod_feedback\courseformat\overview
|
||||
* @package mod_feedback
|
||||
* @category test
|
||||
* @copyright 2025 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
final class overview_test extends \advanced_testcase {
|
||||
#[\Override]
|
||||
public static function setUpBeforeClass(): void {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/feedback/lib.php');
|
||||
parent::setUpBeforeClass();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test get_actions_overview.
|
||||
*
|
||||
* @covers ::get_actions_overview
|
||||
* @dataProvider provider_test_get_actions_overview
|
||||
*
|
||||
* @param string $user
|
||||
* @param bool $expectnull
|
||||
* @param bool $hasresponses
|
||||
* @return void
|
||||
*/
|
||||
public function test_get_actions_overview(string $user, bool $expectnull, bool $hasresponses): void {
|
||||
$this->resetAfterTest();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
|
||||
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
|
||||
$activity = $this->getDataGenerator()->create_module(
|
||||
'feedback',
|
||||
['course' => $course->id],
|
||||
);
|
||||
$cm = get_fast_modinfo($course)->get_cm($activity->cmid);
|
||||
|
||||
$feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
|
||||
$itemcreated = $feedbackgenerator->create_item_multichoice($activity, ['values' => "y\nn"]);
|
||||
|
||||
$expectedresonses = 0;
|
||||
if ($hasresponses) {
|
||||
$this->setUser($student);
|
||||
$feedbackgenerator->create_response([
|
||||
'userid' => $student->id,
|
||||
'cmid' => $cm->id,
|
||||
'anonymous' => false,
|
||||
$itemcreated->name => 'y',
|
||||
]);
|
||||
$expectedresonses = 1;
|
||||
}
|
||||
|
||||
$currentuser = ($user == 'teacher') ? $teacher : $student;
|
||||
$this->setUser($currentuser);
|
||||
|
||||
$item = overviewfactory::create($cm)->get_actions_overview();
|
||||
|
||||
// Students should not see item.
|
||||
if ($expectnull) {
|
||||
$this->assertNull($item);
|
||||
return;
|
||||
}
|
||||
|
||||
// Teachers should see item.
|
||||
$this->assertEquals(get_string('responses', 'mod_feedback'), $item->get_name());
|
||||
$this->assertEquals($expectedresonses, $item->get_value());
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for test_get_actions_overview.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function provider_test_get_actions_overview(): array {
|
||||
return [
|
||||
'Teacher with responses' => [
|
||||
'user' => 'teacher',
|
||||
'expectnull' => false,
|
||||
'hasresponses' => true,
|
||||
],
|
||||
'Student with responses' => [
|
||||
'user' => 'student',
|
||||
'expectnull' => true,
|
||||
'hasresponses' => true,
|
||||
],
|
||||
'Teacher without responses' => [
|
||||
'user' => 'teacher',
|
||||
'expectnull' => false,
|
||||
'hasresponses' => false,
|
||||
],
|
||||
'Student without responses' => [
|
||||
'user' => 'student',
|
||||
'expectnull' => true,
|
||||
'hasresponses' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_due_date_overview.
|
||||
* @covers ::get_due_date_overview
|
||||
* @dataProvider provider_test_get_due_date_overview
|
||||
* @param string $user
|
||||
* @param bool $hasduedate
|
||||
* @return void
|
||||
*/
|
||||
public function test_get_due_date_overview(string $user, bool $hasduedate): void {
|
||||
$this->resetAfterTest();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
|
||||
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
|
||||
$moddata = [
|
||||
'course' => $course->id,
|
||||
'timeclose' => $hasduedate ? time() + 3600 : 0,
|
||||
];
|
||||
|
||||
$activity = $this->getDataGenerator()->create_module('feedback', $moddata);
|
||||
$cm = get_fast_modinfo($course)->get_cm($activity->cmid);
|
||||
|
||||
$currentuser = ($user == 'teacher') ? $teacher : $student;
|
||||
$this->setUser($currentuser);
|
||||
|
||||
$item = overviewfactory::create($cm)->get_due_date_overview();
|
||||
|
||||
// Teachers should see item.
|
||||
$this->assertEquals(get_string('feedbackclose', 'mod_feedback'), $item->get_name());
|
||||
$expectedvalue = $hasduedate ? $moddata['timeclose'] : null;
|
||||
$this->assertEquals($expectedvalue, $item->get_value());
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for test_get_due_date_overview.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function provider_test_get_due_date_overview(): array {
|
||||
return [
|
||||
'Teacher with due date' => [
|
||||
'user' => 'teacher',
|
||||
'hasduedate' => true,
|
||||
],
|
||||
'Student with due date' => [
|
||||
'user' => 'student',
|
||||
'hasduedate' => true,
|
||||
],
|
||||
'Teacher without due date' => [
|
||||
'user' => 'teacher',
|
||||
'hasduedate' => false,
|
||||
],
|
||||
'Student without due date' => [
|
||||
'user' => 'student',
|
||||
'hasduedate' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_extra_submitted_overview.
|
||||
*
|
||||
* @covers ::get_extra_submitted_overview
|
||||
* @dataProvider provider_test_get_extra_submitted_overview
|
||||
*
|
||||
* @param string $user
|
||||
* @param bool $expectnull
|
||||
* @param bool $hasresponses
|
||||
* @return void
|
||||
*/
|
||||
public function test_get_extra_submitted_overview(string $user, bool $expectnull, bool $hasresponses): void {
|
||||
$this->resetAfterTest();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
|
||||
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
|
||||
$activity = $this->getDataGenerator()->create_module(
|
||||
'feedback',
|
||||
['course' => $course->id],
|
||||
);
|
||||
$cm = get_fast_modinfo($course)->get_cm($activity->cmid);
|
||||
|
||||
$feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
|
||||
$itemcreated = $feedbackgenerator->create_item_multichoice($activity, ['values' => "y\nn"]);
|
||||
|
||||
$expectedresonses = 0;
|
||||
if ($hasresponses) {
|
||||
$this->setUser($student);
|
||||
$feedbackgenerator->create_response([
|
||||
'userid' => $student->id,
|
||||
'cmid' => $cm->id,
|
||||
'anonymous' => false,
|
||||
$itemcreated->name => 'y',
|
||||
]);
|
||||
$expectedresonses = 1;
|
||||
}
|
||||
|
||||
$currentuser = ($user == 'teacher') ? $teacher : $student;
|
||||
$this->setUser($currentuser);
|
||||
|
||||
$overview = overviewfactory::create($cm);
|
||||
$reflection = new \ReflectionClass($overview);
|
||||
$method = $reflection->getMethod('get_extra_submitted_overview');
|
||||
$method->setAccessible(true);
|
||||
$item = $method->invoke($overview);
|
||||
|
||||
// Students should not see item.
|
||||
if ($expectnull) {
|
||||
$this->assertNull($item);
|
||||
return;
|
||||
}
|
||||
|
||||
// Teachers should see item.
|
||||
$this->assertEquals(get_string('responded', 'mod_feedback'), $item->get_name());
|
||||
$this->assertEquals($hasresponses, $item->get_value());
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for test_get_extra_submitted_overview.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function provider_test_get_extra_submitted_overview(): array {
|
||||
return [
|
||||
'Teacher with responses' => [
|
||||
'user' => 'teacher',
|
||||
'expectnull' => true,
|
||||
'hasresponses' => true,
|
||||
],
|
||||
'Student with responses' => [
|
||||
'user' => 'student',
|
||||
'expectnull' => false,
|
||||
'hasresponses' => true,
|
||||
],
|
||||
'Teacher without responses' => [
|
||||
'user' => 'teacher',
|
||||
'expectnull' => true,
|
||||
'hasresponses' => false,
|
||||
],
|
||||
'Student without responses' => [
|
||||
'user' => 'student',
|
||||
'expectnull' => false,
|
||||
'hasresponses' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
@ -141,7 +141,11 @@ if ($feedbackcompletion->can_complete()) {
|
||||
echo $OUTPUT->continue_button(course_get_url($courseid ?: $course->id));
|
||||
} else if (!$feedbackcompletion->can_submit()) {
|
||||
// Feedback was already submitted.
|
||||
echo $OUTPUT->notification(get_string('this_feedback_is_already_submitted', 'feedback'));
|
||||
echo $OUTPUT->notification(
|
||||
get_string('this_feedback_is_already_submitted', 'feedback'),
|
||||
\core\output\notification::NOTIFY_INFO,
|
||||
closebutton: false,
|
||||
);
|
||||
$OUTPUT->continue_button(course_get_url($courseid ?: $course->id));
|
||||
}
|
||||
echo $OUTPUT->box_end();
|
||||
|
Loading…
x
Reference in New Issue
Block a user