From 7920905430a1ba5b5da7e2e04027c357558f3127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikel=20Mart=C3=ADn?= Date: Mon, 2 Jun 2025 10:17:17 +0200 Subject: [PATCH 1/3] MDL-83895 courseformat: Add new overviewdialog for overview page Added new core_courseformat\output\local\overview\overviewdialog class to display dialog elemetns in the course overview table. It will show a combination of title, description and a list of items (label: value). --- .upgradenotes/MDL-83896-2025060212250840.yml | 9 ++ .../output/local/overview/overviewdialog.php | 124 ++++++++++++++++++ .../local/overview/overviewdialog.mustache | 65 +++++++++ 3 files changed, 198 insertions(+) create mode 100644 .upgradenotes/MDL-83896-2025060212250840.yml create mode 100644 course/format/classes/output/local/overview/overviewdialog.php create mode 100644 course/format/templates/local/overview/overviewdialog.mustache diff --git a/.upgradenotes/MDL-83896-2025060212250840.yml b/.upgradenotes/MDL-83896-2025060212250840.yml new file mode 100644 index 00000000000..90690111e0c --- /dev/null +++ b/.upgradenotes/MDL-83896-2025060212250840.yml @@ -0,0 +1,9 @@ +issueNumber: MDL-83896 +notes: + core_courseformat: + - message: >- + Added new core_courseformat\output\local\overview\overviewdialog output + class to create dialog elements in the course overview page. Overview + dialog will display a combination of title, description and a list + of items (label: value). + type: improved diff --git a/course/format/classes/output/local/overview/overviewdialog.php b/course/format/classes/output/local/overview/overviewdialog.php new file mode 100644 index 00000000000..4737cbe3a71 --- /dev/null +++ b/course/format/classes/output/local/overview/overviewdialog.php @@ -0,0 +1,124 @@ +. + +namespace core_courseformat\output\local\overview; + +use core\output\local\dropdown\dialog; +use stdClass; + +/** + * Class to render an overview dialog element. + * + * @package core_courseformat + * @copyright 2025 Mikel Martín + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class overviewdialog extends dialog { + + /** @var stdClass[] The list of the items. */ + protected array $items; + + /** + * Constructor. + * + * The definition object could contain the following keys: + * - classes: component CSS classes. + * - buttonclasses: the button CSS classes. + * - dialogwidth: the dropdown width. + * - extras: extra HTML attributes (attribute => value). + * + * @param string $buttoncontent the button content + * @param string $title the overview dialog content title + * @param string $description the overview dialog content description + * @param array $definition an optional array of the element definition + */ + public function __construct( + string $buttoncontent, + /** @var string The title of the overview dialog content. */ + protected string $title = '', + /** @var string The title of the overview dialog content. */ + protected string $description = '', + array $definition = [] + ) { + parent::__construct($buttoncontent, '', $definition); + $this->items = []; + } + + /** + * Set the items to be displayed in the overview dialog. + * + * @param string $label the label of the item + * @param string $value the value of the item + * @return overviewdialog + */ + public function add_item(string $label, string $value): self { + $this->items[] = (object) [ + 'label' => $label, + 'value' => $value, + ]; + return $this; + } + + /** + * Set the title of the overview dialog content. + * + * @param string $title the title to set + * @return overviewdialog + */ + public function set_title(string $title): self { + $this->title = $title; + return $this; + } + + /** + * Set the description of the overview dialog content. + * + * @param string $description the description to set + * @return overviewdialog + */ + public function set_description(string $description): self { + $this->description = $description; + return $this; + } + + /** + * Export this data so it can be used as the context for a mustache template. + * + * @param \renderer_base $output typically, the renderer that's calling this function + * @return array data context for a mustache template + */ + public function export_for_template(\renderer_base $output): array { + $data = [ + ...parent::export_for_template($output), + 'title' => $this->title, + 'items' => $this->items, + 'hasitems' => count($this->items), + 'description' => $this->description, + ]; + + return $data; + } + + /** + * Get the name of the template to use for this templatable. + * + * @param \renderer_base $renderer The renderer requesting the template name + * @return string the template name + */ + public function get_template_name(\renderer_base $renderer): string { + return 'core_courseformat/local/overview/overviewdialog'; + } +} diff --git a/course/format/templates/local/overview/overviewdialog.mustache b/course/format/templates/local/overview/overviewdialog.mustache new file mode 100644 index 00000000000..b44c85636c1 --- /dev/null +++ b/course/format/templates/local/overview/overviewdialog.mustache @@ -0,0 +1,65 @@ +{{! + 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 . +}} +{{! + @template core_courseformat/local/overview/overviewdialog + + Shows an overview dialog. + + Example context (json): + { + "buttoncontent" : "Trigger button", + "title": "Total Attempts", + "description": "This shows the total attempts made by the user in the course.", + "hasitems": true, + "items": [ + { + "label": "Allowed Attempts", + "value": 3 + }, + { + "label": "Average Attempts", + "value": "Max reached" + } + ] + } +}} +{{< core/local/dropdown/dialog }} + {{$ dialogcontent }} +
+ {{#title}} + {{title}} + {{/title}} + {{#description}} +
{{{description}}}
+ {{/description}} + {{#hasitems}} +
    + {{#items}} +
  • + {{#str}} + labelvalue, core, { + "label": {{#quote}}{{label}}{{/quote}}, + "value": {{#quote}}{{value}}{{/quote}} + } + {{/str}} +
  • + {{/items}} +
+ {{/hasitems}} +
+ {{/ dialogcontent }} +{{/ core/local/dropdown/dialog }} From 673a247b0dcc60d35737eafc07a3cc2e398df1f6 Mon Sep 17 00:00:00 2001 From: Amaia Anabitarte Date: Mon, 2 Jun 2025 17:31:07 +0200 Subject: [PATCH 2/3] MDL-83895 mod_h5pactivity: New overview page --- .../local/overview/overviewfactory_test.php | 2 +- .../overview/missingoverviewnotice_test.php | 2 +- .../classes/courseformat/overview.php | 204 +++++++++++++++ mod/h5pactivity/lang/en/h5pactivity.php | 6 + .../tests/behat/overview_report.feature | 94 +++++++ .../tests/courseformat/overview_test.php | 247 ++++++++++++++++++ 6 files changed, 553 insertions(+), 2 deletions(-) create mode 100644 mod/h5pactivity/classes/courseformat/overview.php create mode 100644 mod/h5pactivity/tests/behat/overview_report.feature create mode 100644 mod/h5pactivity/tests/courseformat/overview_test.php diff --git a/course/format/tests/local/overview/overviewfactory_test.php b/course/format/tests/local/overview/overviewfactory_test.php index 5982887f03c..7efa1d263f6 100644 --- a/course/format/tests/local/overview/overviewfactory_test.php +++ b/course/format/tests/local/overview/overviewfactory_test.php @@ -117,7 +117,7 @@ final class overviewfactory_test extends \advanced_testcase { ], 'h5pactivity' => [ 'resourcetype' => 'h5pactivity', - 'expected' => resourceoverview::class, + 'expected' => \mod_h5pactivity\courseformat\overview::class, ], 'lesson' => [ 'resourcetype' => 'lesson', diff --git a/course/format/tests/output/local/overview/missingoverviewnotice_test.php b/course/format/tests/output/local/overview/missingoverviewnotice_test.php index 2b1022550ee..3b4dbe5e42f 100644 --- a/course/format/tests/output/local/overview/missingoverviewnotice_test.php +++ b/course/format/tests/output/local/overview/missingoverviewnotice_test.php @@ -72,7 +72,7 @@ final class missingoverviewnotice_test extends \advanced_testcase { 'folder' => ['modname' => 'folder', 'expectempty' => false], 'forum' => ['modname' => 'forum', 'expectempty' => false], 'glossary' => ['modname' => 'glossary', 'expectempty' => true], - 'h5pactivity' => ['modname' => 'h5pactivity', 'expectempty' => false], + 'h5pactivity' => ['modname' => 'h5pactivity', 'expectempty' => true], 'imscp' => ['modname' => 'imscp', 'expectempty' => false], 'label' => ['modname' => 'label', 'expectempty' => false], 'lesson' => ['modname' => 'lesson', 'expectempty' => false], diff --git a/mod/h5pactivity/classes/courseformat/overview.php b/mod/h5pactivity/classes/courseformat/overview.php new file mode 100644 index 00000000000..5c152416c56 --- /dev/null +++ b/mod/h5pactivity/classes/courseformat/overview.php @@ -0,0 +1,204 @@ +. + +namespace mod_h5pactivity\courseformat; + +use cm_info; +use core_courseformat\local\overview\overviewitem; +use core\output\action_link; +use core\output\local\properties\text_align; +use core\output\local\properties\button; +use core\url; +use core_courseformat\output\local\overview\overviewdialog; +use mod_h5pactivity\local\manager; + +/** + * H5P activity overview integration. + * + * @package mod_h5pactivity + * @copyright 2025 Amaia Anabitarte + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class overview extends \core_courseformat\activityoverviewbase { + + /** @var manager H5P activity manager. */ + private $manager; + + /** + * Constructor. + * + * @param cm_info $cm the course module instance. + * @param \core\output\renderer_helper $rendererhelper the renderer helper. + * @param \core_string_manager $stringmanager the string manager. + */ + public function __construct( + cm_info $cm, + /** @var \core\output\renderer_helper $rendererhelper the renderer helper */ + protected readonly \core\output\renderer_helper $rendererhelper, + /** @var \core_string_manager $stringmanager the string manager */ + protected readonly \core_string_manager $stringmanager, + ) { + parent::__construct($cm); + + $this->manager = manager::create_from_coursemodule($cm); + } + + #[\Override] + public function get_actions_overview(): ?overviewitem { + + if (!$this->manager->can_view_all_attempts()) { + return null; + } + + $viewresults = $this->stringmanager->get_string('viewresults', 'mod_h5pactivity'); + $content = new action_link( + url: new url('/mod/h5pactivity/report.php', ['id' => $this->cm->id]), + text: $viewresults, + attributes: ['class' => button::SECONDARY_OUTLINE->classes()], + ); + + return new overviewitem( + name: $this->stringmanager->get_string('actions'), + value: '', + content: $content, + textalign: text_align::CENTER, + ); + } + + #[\Override] + public function get_extra_overview_items(): array { + global $USER; + + if (!$this->manager->can_view_own_attempts() && $this->manager->can_view_all_attempts()) { + return []; + } + + if (!$this->manager->can_view_all_attempts()) { + return [ + 'myattempts' => $this->get_extra_userattempts_overview($USER->id), + ]; + } + + return [ + 'h5ptype' => $this->get_extra_h5ptype_overview(), + 'attempted' => $this->get_extra_studentsattempted_overview(), + 'totalattempts' => $this->get_extra_totalattempts_overview(), + ]; + } + + /** + * Get the attempts of the given user. + * + * @param int $userid The user to return the attempts from. + * @return overviewitem The overview item. + */ + private function get_extra_userattempts_overview(int $userid): overviewitem { + + $attempts = $this->manager->count_attempts($userid); + return new overviewitem( + name: $this->stringmanager->get_string('attempts', 'mod_h5pactivity'), + value: $attempts, + content: $attempts ?? '-', + textalign: text_align::CENTER, + ); + } + + /** + * Get the students who attempted. + * + * @return overviewitem The overview item. + */ + private function get_extra_studentsattempted_overview(): overviewitem { + + $attempts = $this->manager->count_users_attempts(); + $participants = get_users_by_capability($this->context, 'mod/h5pactivity:submit'); + $params = [ + 'count' => count($attempts), + 'total' => count($participants), + ]; + return new overviewitem( + name: $this->stringmanager->get_string('attempted', 'mod_h5pactivity'), + value: count($attempts), + content: $this->stringmanager->get_string('count_of_total', 'core', $params), + textalign: text_align::CENTER, + ); + } + + /** + * Get the "Total attempts" colum data. + * + * @return overviewitem The overview item. + */ + private function get_extra_totalattempts_overview(): overviewitem { + $totalattempts = $this->manager->count_attempts(); + $totalusers = $this->manager->count_users_attempts(); + + $content = '-'; + if ($totalusers && count($totalusers) > 0 && $totalattempts) { + $averageattempts = (int) round($totalattempts / count($totalusers)); + $content = new overviewdialog( + buttoncontent: $totalattempts, + title: $this->stringmanager->get_string('totalattempts', 'mod_h5pactivity'), + definition: ['buttonclasses' => button::SECONDARY_OUTLINE->classes() . ' dropdown-toggle'], + ); + $method = $this->manager::get_grading_methods()[$this->manager->get_instance()->grademethod]; + $content->add_item($this->stringmanager->get_string('gradingmethod', 'grading'), $method); + $content->add_item($this->stringmanager->get_string('averageattempts', 'mod_h5pactivity'), $averageattempts); + } + + return new overviewitem( + name: $this->stringmanager->get_string('totalattempts', 'mod_h5pactivity'), + value: $totalattempts, + content: $content, + ); + } + + /** + * Get the H5P content type. + * + * @return overviewitem The overview item. + */ + private function get_extra_h5ptype_overview(): overviewitem { + $fs = get_file_storage(); + $files = $fs->get_area_files($this->context->id, 'mod_h5pactivity', 'package', 0, 'id', false); + $file = reset($files); + + $h5p = \core_h5p\api::get_content_from_pathnamehash($file->get_pathnamehash()); + + $unknonwoverview = new overviewitem( + name: $this->stringmanager->get_string('contenttype', 'mod_h5pactivity'), + value: $this->stringmanager->get_string('unknowntype', 'mod_h5pactivity'), + content: $this->stringmanager->get_string('unknowntype', 'mod_h5pactivity'), + ); + + if (empty($h5p)) { + return $unknonwoverview; + } + + $h5plib = \core_h5p\api::get_library($h5p->mainlibraryid); + + // If the content is not yet deployed we cannot show the content type. + if (empty($h5plib)) { + return $unknonwoverview; + } + + return new overviewitem( + name: $this->stringmanager->get_string('contenttype', 'mod_h5pactivity'), + value: $h5plib->title, + content: $h5plib->title, + ); + } +} diff --git a/mod/h5pactivity/lang/en/h5pactivity.php b/mod/h5pactivity/lang/en/h5pactivity.php index e81722784c5..fc61b467173 100644 --- a/mod/h5pactivity/lang/en/h5pactivity.php +++ b/mod/h5pactivity/lang/en/h5pactivity.php @@ -37,6 +37,7 @@ $string['answer_text'] = 'Answer text'; $string['answer_noanswer'] = 'None'; $string['areapackage'] = 'Package file'; $string['attempt'] = 'Attempt'; +$string['attempted'] = 'Students who attempted'; $string['attempt_average'] = 'Attempts average scored'; $string['attempt_answer'] = 'Attempt answer'; $string['attempt_completion_no'] = 'This attempt is not marked as completed'; @@ -53,10 +54,12 @@ $string['attempts'] = 'Attempts'; $string['attempts_report_header_label'] = 'Attempts ({$a})'; $string['attempts_report'] = 'Attempts report'; $string['attempts_none'] = 'This user has no attempts to display.'; +$string['averageattempts'] = 'Average attempts per student'; $string['choice'] = 'Choice'; $string['completion'] = 'Completion'; $string['contentbank'] = 'More information about the content bank'; $string['contentbank_help'] = 'In the content bank you can create and store content using several authoring tools, including an integrated H5P creator.'; +$string['contenttype'] = 'H5P type'; $string['correct_answer'] = 'Correct answer'; $string['deleteallattempts'] = 'All H5P attempts'; $string['displayexport'] = 'Allow download'; @@ -140,12 +143,15 @@ $string['score_out_of'] = '{$a->rawscore} out of {$a->maxscore}'; $string['search:activity'] = 'H5P - activity information'; $string['startdate'] = 'Start date'; $string['statement_received'] = 'xAPI statement received'; +$string['totalattempts'] = 'Total attempts'; $string['totalscore'] = 'Total score'; $string['trackingdisabled'] = 'Attempt tracking is not enabled for this activity.'; $string['trackingdisabled_enable'] = 'Attempt tracking is not enabled for this activity. You can enable it in Settings.'; $string['tracking_messages'] = 'Some H5P provide attempt tracking data for advanced reporting such as number of attempts, responses and grades. Note: Some H5P don\'t provide attempt tracking data. In such cases, the following settings will have no effect.'; $string['true'] = 'True'; +$string['unknowntype'] = 'Unknown H5P type'; $string['usecontentbank'] = 'Use the content bank (opens in new window) to manage your H5P files'; $string['view'] = 'View'; $string['viewattempts'] = 'View attempts ({$a})'; +$string['viewresults'] = 'View results'; $string['view_report'] = 'View report'; diff --git a/mod/h5pactivity/tests/behat/overview_report.feature b/mod/h5pactivity/tests/behat/overview_report.feature new file mode 100644 index 00000000000..535e006aaf5 --- /dev/null +++ b/mod/h5pactivity/tests/behat/overview_report.feature @@ -0,0 +1,94 @@ +@mod @mod_h5pactivity +Feature: Testing overview integration in H5P activity + In order to summarize the H5P activity + As a user + I need to be able to see the H5P activity overview + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | student1 | Student | 1 | student1@example.com | + | student2 | Student | 2 | student2@example.com | + | student3 | Student | 3 | student3@example.com | + | teacher1 | Teacher | 1 | teacher1@example.com | + And the following "courses" exist: + | fullname | shortname | category | enablecompletion | + | Course 1 | C1 | 0 | 1 | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + | student2 | C1 | student | + | student3 | C1 | student | + And the following "activity" exists: + | course | C1 | + | activity | h5pactivity | + | name | H5P activity | + | intro | description | + | packagefilepath | h5p/tests/fixtures/find-the-words.h5p | + | idnumber | h5p | + | completion | 1 | + | enabletracking | 1 | + | reviewmode | 1 | + | grademethod | 2 | + And the following "activity" exists: + | course | C1 | + | activity | h5pactivity | + | name | Empty H5P activity | + | intro | empty | + | idnumber | empty | + And the following "mod_h5pactivity > attempts" exist: + | user | h5pactivity | attempt | interactiontype | rawscore | maxscore | duration | completion | success | + # student1. + | student1 | H5P activity | 1 | choice | 2 | 2 | 1 | 1 | 1 | + | student1 | H5P activity | 1 | compound | 2 | 2 | 4 | 1 | 1 | + | student1 | H5P activity | 2 | choice | 0 | 2 | 1 | 1 | 0 | + | student1 | H5P activity | 2 | compound | 0 | 2 | 4 | 1 | 0 | + | student1 | H5P activity | 3 | matching | 2 | 2 | 1 | 1 | 1 | + | student1 | H5P activity | 3 | compound | 2 | 2 | 4 | 1 | 1 | + | student1 | H5P activity | 4 | true-false | 2 | 2 | 1 | 1 | 1 | + | student1 | H5P activity | 4 | compound | 2 | 2 | 4 | 1 | 1 | + # student2. + | student2 | H5P activity | 1 | compound | 0 | 2 | 1 | 1 | 0 | + # We need to navigate to the activity to deploy the H5P file. + And I am on the "H5P activity" "h5pactivity activity" page logged in as admin + And I log out + + Scenario: The H5P activity overview report should generate log events + Given I am on the "Course 1" "course > activities > h5pactivity" page logged in as "teacher1" + And 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" + When 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 'h5pactivity'" + + @javascript + Scenario: Students can see relevant columns in the H5P activity overview + Given I am on the "Course 1" "course > activities > h5pactivity" page logged in as student1 + # Check columns. + When I should see "Completion status" in the "h5pactivity_overview_collapsible" "region" + # Check column values. + Then the following should exist in the "Table listing all H5P activities" table: + | Name | Attempts | Grade | + | H5P activity | 4 | 75.00 | + | Empty H5P activity | 0 | - | + + @javascript + Scenario: Teachers can see relevant columns in the H5P activity overview + Given I am on the "Course 1" "course > activities > h5pactivity" page logged in as teacher1 + # Check columns. + And I should see "Total attempts" in the "h5pactivity_overview_collapsible" "region" + # Check column values. + And the following should exist in the "Table listing all H5P activities" table: + | Name | H5P type | Students who attempted | Total attempts | Actions | + | H5P activity | Find The Words | 2 of 3 | 5 | View results | + | Empty H5P activity | Unknown H5P type | 0 of 3 | - | View results | + # Check the Total attempts value. + And I should see "-" in the "Empty H5P activity" "table_row" + When I click on "5" "button" in the "H5P activity" "table_row" + Then I should see "Grading method: Average grade" + And I should see "Average attempts per student: 3" + # Check the View results link. + And I click on "View results" "link" in the "H5P activity" "table_row" + And I should see "Attempts (5)" diff --git a/mod/h5pactivity/tests/courseformat/overview_test.php b/mod/h5pactivity/tests/courseformat/overview_test.php new file mode 100644 index 00000000000..c86665dadd7 --- /dev/null +++ b/mod/h5pactivity/tests/courseformat/overview_test.php @@ -0,0 +1,247 @@ +. + +namespace mod_h5pactivity\courseformat; + +use core_courseformat\local\overview\overviewfactory; + +/** + * Tests for H5P activity overview + * + * @covers \mod_h5pactivity\courseformat\overview + * @package mod_h5pactivity + * @category test + * @copyright 2025 Amaia Anabitarte + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +final class overview_test extends \advanced_testcase { + + /** + * Test get_actions_overview. + * + * @covers ::get_actions_overview + */ + public function test_get_actions_overview(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $course = $this->getDataGenerator()->create_course(); + $activity = $this->getDataGenerator()->create_module( + 'h5pactivity', + ['course' => $course, 'enabletracking' => 1], + ); + $cm = get_fast_modinfo($course)->get_cm($activity->cmid); + + // Prepare users: 1 teacher, 2 students, 1 unenroled user. + $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher'); + $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); + + $generator = $this->getDataGenerator()->get_plugin_generator('mod_h5pactivity'); + + // Students have no action column. + $this->setUser($student); + $this->assertNull(overviewfactory::create($cm)->get_actions_overview()); + + // Teachers have a 'View results' button. + $this->setUser($teacher); + $items = overviewfactory::create($cm)->get_actions_overview(); + $this->assertNotNull($items); + $this->assertEquals(get_string('actions'), $items->get_name()); + } + + /** + * Test get_extra_h5ptype_overview. + * + * @covers ::get_extra_h5ptype_overview + * @dataProvider provider_test_get_extra_h5type_overview + * + * @param string $h5pfile + * @param bool $iscorrect + * @param string $expected + * @return void + */ + public function test_get_extra_h5ptype_overview( + string $h5pfile, + bool $iscorrect, + string $expected + ): void { + global $CFG; + + $this->resetAfterTest(); + $this->setAdminUser(); + + $course = $this->getDataGenerator()->create_course(); + $params = [ + 'course' => $course->id, + 'packagefilepath' => $CFG->dirroot.'/h5p/tests/fixtures/'.$h5pfile, + 'introformat' => 1, + ]; + $activity = $this->getDataGenerator()->create_module('h5pactivity', $params); + // Add filename and contextid to make easier the asserts. + $activity->filename = $h5pfile; + $context = \context_module::instance($activity->cmid); + $activity->contextid = $context->id; + + // Create a fake deploy H5P file. + + /** @var \core_h5p_generator $h5pgenerator */ + $h5pgenerator = $this->getDataGenerator()->get_plugin_generator('core_h5p'); + + if (!$iscorrect) { + $this->expectException(\TypeError::class); + } + $h5pgenerator->create_export_file($activity->filename, $context->id, 'mod_h5pactivity', 'package'); + + $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher'); + $this->setUser($teacher); + + $cm = get_fast_modinfo($course)->get_cm($activity->cmid); + $items = overviewfactory::create($cm)->get_extra_overview_items(); + + $this->assertEquals($expected, $items['h5ptype']->get_value()); + + $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); + $this->setUser($student); + + $cm = get_fast_modinfo($course)->get_cm($activity->cmid); + $items = overviewfactory::create($cm)->get_extra_overview_items(); + + $this->assertArrayNotHasKey('h5ptype', $items); + } + + /** + * Data provider for test h5p type overview extra. + * + * @return array + */ + public static function provider_test_get_extra_h5type_overview(): array { + return [ + 'Basic package' => [ + 'h5pfile' => 'basic_essay.h5p', + 'iscorrect' => true, + 'expected' => 'Essay', + ], + 'No json file' => [ + 'h5pfile' => 'no-json-file.h5p', + 'iscorrect' => false, + 'expected' => get_string('unknowntype', 'mod_h5pactivity'), + ], + 'Unzippable package' => [ + 'h5pfile' => 'unzippable.h5p', + 'iscorrect' => false, + 'expected' => get_string('unknowntype', 'mod_h5pactivity'), + ], + ]; + } + + /** + * Test get_extra_overview_items. + * + * @covers ::get_extra_overview_items + */ + public function test_get_extra_attempts_overview(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $course = $this->getDataGenerator()->create_course(); + $activity = $this->getDataGenerator()->create_module( + 'h5pactivity', + ['course' => $course, 'enabletracking' => 1], + ); + $cm = get_fast_modinfo($course)->get_cm($activity->cmid); + + // Prepare users: 1 teacher, 2 students, 1 unenroled user. + $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher'); + $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); + $other = $this->getDataGenerator()->create_and_enrol($course, 'student'); + + $generator = $this->getDataGenerator()->get_plugin_generator('mod_h5pactivity'); + + // No attempts yet. + $this->setUser($teacher); + $items = overviewfactory::create($cm)->get_extra_overview_items(); + $this->assertEquals(0, $items['totalattempts']->get_value()); + + $this->setUser($student); + $items = overviewfactory::create($cm)->get_extra_overview_items(); + $this->assertEquals(0, $items['myattempts']->get_value()); + + // Attempts done by other student. + $params = ['cmid' => $cm->id, 'userid' => $other->id]; + $generator->create_content($activity, $params); + $generator->create_content($activity, $params); + + $this->setUser($teacher); + $items = overviewfactory::create($cm)->get_extra_overview_items(); + $this->assertEquals(2, $items['totalattempts']->get_value()); + + $this->setUser($student); + $items = overviewfactory::create($cm)->get_extra_overview_items(); + $this->assertEquals(0, $items['myattempts']->get_value()); + + // Attempts done by the student. + $params = ['cmid' => $cm->id, 'userid' => $student->id]; + $generator->create_content($activity, $params); + $generator->create_content($activity, $params); + + $this->setUser($teacher); + $items = overviewfactory::create($cm)->get_extra_overview_items(); + $this->assertEquals(4, $items['totalattempts']->get_value()); + + $this->setUser($student); + $items = overviewfactory::create($cm)->get_extra_overview_items(); + $this->assertEquals(2, $items['myattempts']->get_value()); + } + + /** + * Test get_extra_studentsattempted_overview. + * + * @covers ::get_extra_studentsattempted_overview + */ + public function test_get_extra_studentsattempted_overview(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $course = $this->getDataGenerator()->create_course(); + $activity = $this->getDataGenerator()->create_module( + 'h5pactivity', + ['course' => $course, 'enabletracking' => 1], + ); + $cm = get_fast_modinfo($course)->get_cm($activity->cmid); + + // Prepare users: 1 teacher, 2 students, 1 unenroled user. + $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher'); + $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); + $other = $this->getDataGenerator()->create_and_enrol($course, 'student'); + + $generator = $this->getDataGenerator()->get_plugin_generator('mod_h5pactivity'); + + // No attempts yet. + $this->setUser($teacher); + $items = overviewfactory::create($cm)->get_extra_overview_items(); + $this->assertEquals(0, $items['attempted']->get_value()); + $this->assertEquals('0 of 2', $items['attempted']->get_content()); + + // With attempts. + $params = ['cmid' => $cm->id, 'userid' => $student->id]; + $generator->create_content($activity, $params); + $generator->create_content($activity, $params); + + $items = overviewfactory::create($cm)->get_extra_overview_items(); + $this->assertEquals(1, $items['attempted']->get_value()); + $this->assertEquals('1 of 2', $items['attempted']->get_content()); + } +} From 0d1e36015c3efd86a3b6434cf0496ba3c3c636cc Mon Sep 17 00:00:00 2001 From: Amaia Anabitarte Date: Wed, 4 Jun 2025 15:31:22 +0200 Subject: [PATCH 3/3] MDL-83895 mod_h5pactivity: Redirect index.php to overview page --- mod/h5pactivity/index.php | 61 +------------------ .../tests/behat/overview_report.feature | 10 +++ 2 files changed, 12 insertions(+), 59 deletions(-) diff --git a/mod/h5pactivity/index.php b/mod/h5pactivity/index.php index 03c3833e0b6..fcf0072420b 100644 --- a/mod/h5pactivity/index.php +++ b/mod/h5pactivity/index.php @@ -25,62 +25,5 @@ require(__DIR__.'/../../config.php'); require_once(__DIR__.'/lib.php'); -$id = required_param('id', PARAM_INT); - -$course = $DB->get_record('course', ['id' => $id], '*', MUST_EXIST); -require_course_login($course); - -$coursecontext = context_course::instance($course->id); - -$event = \mod_h5pactivity\event\course_module_instance_list_viewed::create(['context' => $coursecontext]); -$event->add_record_snapshot('course', $course); -$event->trigger(); - -$PAGE->set_url('/mod/h5pactivity/index.php', ['id' => $id]); -$PAGE->set_title(format_string($course->fullname)); -$PAGE->set_heading(format_string($course->fullname)); -$PAGE->set_context($coursecontext); - -echo $OUTPUT->header(); - -$modulenameplural = get_string('modulenameplural', 'mod_h5pactivity'); -echo $OUTPUT->heading($modulenameplural); - -$h5pactivities = get_all_instances_in_course('h5pactivity', $course); - -if (empty($h5pactivities)) { - notice(get_string('thereareno', 'moodle'), new moodle_url('/course/view.php', ['id' => $course->id])); - exit; -} - -$table = new html_table(); -$table->attributes['class'] = 'table generaltable mod_index'; - -$align = ['center', 'left']; -if ($course->format == 'weeks' || $course->format == 'topics') { - $table->head = [get_string('section'), get_string('name')]; - $table->align = ['center', 'left']; -} else { - $table->head = [get_string('name')]; - $table->align = ['left']; -} - -foreach ($h5pactivities as $h5pactivity) { - $attributes = []; - if (!$h5pactivity->visible) { - $attributes['class'] = 'dimmed'; - } - $link = html_writer::link( - new moodle_url('/mod/h5pactivity/view.php', ['id' => $h5pactivity->coursemodule]), - format_string($h5pactivity->name, true), - $attributes); - - if ($course->format == 'weeks' or $course->format == 'topics') { - $table->data[] = [$h5pactivity->section, $link]; - } else { - $table->data[] = [$link]; - } -} - -echo html_writer::table($table); -echo $OUTPUT->footer(); +$courseid = required_param('id', PARAM_INT); +\core_courseformat\activityoverviewbase::redirect_to_overview_page($courseid, 'h5pactivity'); diff --git a/mod/h5pactivity/tests/behat/overview_report.feature b/mod/h5pactivity/tests/behat/overview_report.feature index 535e006aaf5..029bbea0ec1 100644 --- a/mod/h5pactivity/tests/behat/overview_report.feature +++ b/mod/h5pactivity/tests/behat/overview_report.feature @@ -92,3 +92,13 @@ Feature: Testing overview integration in H5P activity # Check the View results link. And I click on "View results" "link" in the "H5P activity" "table_row" And I should see "Attempts (5)" + + Scenario: The H5P activity index redirect to the activities overview + Given I log in as "admin" + And I am on "Course 1" course homepage with editing mode on + And I add the "Activities" block + When I click on "H5P" "link" in the "Activities" "block" + Then I should see "An overview of all activities in the course" + And I should see "Name" in the "h5pactivity_overview_collapsible" "region" + And I should see "H5P type" in the "h5pactivity_overview_collapsible" "region" + And I should see "Actions" in the "h5pactivity_overview_collapsible" "region"