From d94f2acdae159aae42931aa184237ca89c5afeff Mon Sep 17 00:00:00 2001 From: ferran Date: Tue, 18 Feb 2025 12:50:30 +0100 Subject: [PATCH] MDL-83872 course: adding activity overview base classes The course overview page allows plugins to implementa an activity overview integration. This commit adds all the base classes for the integration, and also the "resources" integration used by default in all plugins considered resources like url, file, folder... --- .../format/classes/activityoverviewbase.php | 162 +++++++++++++++++ .../local/overview/overviewfactory.php | 49 +++++ .../classes/local/overview/overviewitem.php | 146 +++++++++++++++ .../local/overview/resourceoverview.php | 46 +++++ .../output/local/overview/activityname.php | 74 ++++++++ .../local/overview/activityname.mustache | 46 +++++ .../tests/activityoverviewbase_test.php | 169 ++++++++++++++++++ .../tests/fixtures/fake_activityoverview.php | 27 +++ .../fixtures/wrongcm_activityoverview.php | 27 +++ .../local/overview/overviewfactory_test.php | 115 ++++++++++++ .../local/overview/resourceoverview_test.php | 106 +++++++++++ lang/en/completion.php | 1 + lang/en/moodle.php | 1 + lib/apis.json | 11 +- 14 files changed, 977 insertions(+), 3 deletions(-) create mode 100644 course/format/classes/activityoverviewbase.php create mode 100644 course/format/classes/local/overview/overviewfactory.php create mode 100644 course/format/classes/local/overview/overviewitem.php create mode 100644 course/format/classes/local/overview/resourceoverview.php create mode 100644 course/format/classes/output/local/overview/activityname.php create mode 100644 course/format/templates/local/overview/activityname.mustache create mode 100644 course/format/tests/activityoverviewbase_test.php create mode 100644 course/format/tests/fixtures/fake_activityoverview.php create mode 100644 course/format/tests/fixtures/wrongcm_activityoverview.php create mode 100644 course/format/tests/local/overview/overviewfactory_test.php create mode 100644 course/format/tests/local/overview/resourceoverview_test.php diff --git a/course/format/classes/activityoverviewbase.php b/course/format/classes/activityoverviewbase.php new file mode 100644 index 00000000000..f5ff78d112d --- /dev/null +++ b/course/format/classes/activityoverviewbase.php @@ -0,0 +1,162 @@ +. + +namespace core_courseformat; + +use cm_info; +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\base as courseformat; + +/** + * Base class for activity overview. + * + * Plugins must extend this class on their \mod_PLUGINNAME\courseformat\overview + * integration to provide overview items about a course module instance. + * + * @package core_courseformat + * @copyright 2025 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class activityoverviewbase { + /** @var cm_info The course module. */ + protected module_context $context; + + /** @var \stdClass $course */ + protected \stdClass $course; + + /** @var courseformat $format the course format */ + protected courseformat $format; + + /** + * Activity Overview Base class constructor. + * + * Overview integrations are meant to use dependency injection. + * Don't create instances of this class directly, use the factory instead. + * + * \core_courseformat\local\overview\overviewfactory::create($cm); + * + * Plugins can override the constructor adding more dependencies such as: + * + * - protected readonly \moodle_database $db -> To access the database. + * - protected readonly \core\clock $clock -> The clock interface to handle time. + * + * However, it is important to note all original dependencies must be kept. + * + * @param cm_info $cm The course module information (loaded by the factory). + */ + public function __construct( + /** @var cm_info The course module. */ + protected readonly cm_info $cm, + ) { + $this->context = $cm->context; + $this->course = $cm->get_course(); + $this->format = courseformat::instance($this->course); + } + + /** + * Get the plugin specific overview items for the activity. + * + * Plugins can override this method to provide their own overview items. + * + * The resulting array must be indexed by item shortname. + * + * @return overviewitem[] Array of overview items indexed by item shortname. + */ + public function get_extra_overview_items(): array { + return []; + } + + /** + * Get the name of the activity. + */ + final public function get_name_overview(): overviewitem { + return new overviewitem( + name: get_string('name'), + value: $this->cm->name, + content: new activityname($this->cm), + ); + } + + /** + * Retrieves the due date overview for the activity. + * + * @return overviewitem|null null if module does not have a due date. + */ + public function get_due_date_overview(): ?overviewitem { + return null; + } + + /** + * Retrieves the actions overview for the activity. + * + * @return overviewitem|null null if module does not have a main action item. + */ + public function get_actions_overview(): ?overviewitem { + return null; + } + + /** + * Retrieves the completion overview for the activity. + * + * @return overviewitem|null null if completion is not enabled. + */ + public function get_completion_overview(): ?overviewitem { + global $USER; + + $showcompletionconditions = $this->course->showcompletionconditions == COMPLETION_SHOW_CONDITIONS; + + $completiondetails = cm_completion_details::get_instance( + $this->cm, + $USER->id, + $showcompletionconditions + ); + + if (!$completiondetails->is_tracked_user()) { + return null; + } + + $showcompletioninfo = $completiondetails->has_completion() && + ($showcompletionconditions || $completiondetails->show_manual_completion()); + + if (!$showcompletioninfo) { + return new overviewitem( + name: get_string('completion_status', 'completion'), + value: null, + content: '-', + ); + } + + $status = $completiondetails->get_overall_completion(); + + $completionclass = $this->format->get_output_classname('content\\cm\\completion'); + /** @var \core_courseformat\output\local\content\cm\completion $completion */ + $completion = new $completionclass( + $this->format, + $this->cm->get_section_info(), + $this->cm + ); + $completion->set_smallbutton(false); + + return new overviewitem( + name: get_string('completion_status', 'completion'), + value: $status, + content: $completion, + ); + } +} diff --git a/course/format/classes/local/overview/overviewfactory.php b/course/format/classes/local/overview/overviewfactory.php new file mode 100644 index 00000000000..ce1033500b5 --- /dev/null +++ b/course/format/classes/local/overview/overviewfactory.php @@ -0,0 +1,49 @@ +. + +namespace core_courseformat\local\overview; + +use cm_info; +use core_courseformat\activityoverviewbase; + +/** + * Plugin overview instance factory. + * + * @package core_courseformat + * @copyright 2025 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class overviewfactory { + /** + * Creates an instance of the appropriate overview class for the given course module. + * + * @param cm_info $cm The course module information. + * @return activityoverviewbase An instance of the appropriate overview class. + */ + public static function create(cm_info $cm): activityoverviewbase { + $classname = "\\mod_{$cm->modname}\\courseformat\\overview"; + if (!class_exists($classname)) { + $classname = resourceoverview::class; + } + + $result = \core\di::get_container()->make($classname, ['cm' => $cm]); + if (!($result instanceof activityoverviewbase)) { + throw new \coding_exception("Class $classname must extend " . activityoverviewbase::class); + } + + return $result; + } +} diff --git a/course/format/classes/local/overview/overviewitem.php b/course/format/classes/local/overview/overviewitem.php new file mode 100644 index 00000000000..d6aea1ff664 --- /dev/null +++ b/course/format/classes/local/overview/overviewitem.php @@ -0,0 +1,146 @@ +. + +namespace core_courseformat\local\overview; + +use core\output\renderable; +use core\output\renderer_base; +use core\output\local\properties\text_align; + +/** + * Class overviewitem + * + * @package core_courseformat + * @copyright 2025 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class overviewitem { + /** + * Overview item constructor. + * + * @param string $name The name of the activity. + * @param int|string|bool|null $value The section name. + * @param string|renderable|null $content The item content. + * @param text_align $textalign The preferred text alignment. + */ + public function __construct( + /** @var string the name of the activity */ + protected string $name, + /** @var string the section name */ + protected int|string|bool|null $value, + /** @var string the item content */ + protected string|renderable|null $content = null, + /** @var text_align the preferred text alignment. */ + protected text_align $textalign = text_align::START, + ) { + } + + /** + * Retrieves the name of the overview item. + * + * @return string + */ + public function get_name(): string { + return $this->name; + } + + /** + * Retrieves the value of the overview item. + * + * @return int|string|bool|null + */ + public function get_value(): int|string|bool|null { + return $this->value; + } + + /** + * Gets the content for this item. + * + * Items can utilize either a renderable object or a pre-rendered string as their content. + * + * - For simple items, a plain string is sufficient and can be used in any context. + * - For more complex items, a renderable object is preferable. This allows the item + * to be rendered differently depending on the context, providing greater flexibility. + * + * @return string|\core\output\renderable|null + */ + public function get_content(): string|renderable|null { + return $this->content ?? (string) $this->value ?? null; + } + + /** + * Gets the rendered content for this item. + * + * This method is used when the context does not have any specific requirements + * and could use the default item content rendering. + * + * @param \core\output\renderer_base $output + * @return string + */ + public function get_rendered_content(renderer_base $output): string { + if ($this->content instanceof renderable) { + return $output->render($this->content); + } + return $this->get_content() ?? ''; + } + + /** + * Gets the preferred text alignment of the item. + * + * @return text_align The text alignment. + */ + public function get_text_align(): text_align { + return $this->textalign; + } + + /** + * Sets the content for this item. + * + * Items can utilize either a renderable object or a pre-rendered string as their content. + * + * @param string|renderable|null $content + */ + public function set_content(string|renderable|null $content): void { + $this->content = $content; + } + + /** + * Sets the preferred text alignment of the item. + * + * @param text_align $textalign + */ + public function set_text_align(text_align $textalign): void { + $this->textalign = $textalign; + } + + /** + * Sets the value of the overview item. + * + * @param int|string|bool|null $value + */ + public function set_value(int|string|bool|null $value): void { + $this->value = $value; + } + + /** + * Sets the name of the overview item. + * + * @param string $name + */ + public function set_name(string $name): void { + $this->name = $name; + } +} diff --git a/course/format/classes/local/overview/resourceoverview.php b/course/format/classes/local/overview/resourceoverview.php new file mode 100644 index 00000000000..4889291c435 --- /dev/null +++ b/course/format/classes/local/overview/resourceoverview.php @@ -0,0 +1,46 @@ +. + +namespace core_courseformat\local\overview; + +/** + * Class resourceoverview + * + * @package core_courseformat + * @copyright 2025 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class resourceoverview extends \core_courseformat\activityoverviewbase { + #[\Override] + public function get_extra_overview_items(): array { + return [ + 'type' => $this->get_extra_type_overview(), + ]; + } + + /** + * Retrieves an overview item for the extra type of the resource. + * + * @return overviewitem|null + */ + private function get_extra_type_overview(): ?overviewitem { + return new overviewitem( + name: get_string('resource_type'), + value: $this->cm->modfullname, + content: $this->cm->modfullname, + ); + } +} diff --git a/course/format/classes/output/local/overview/activityname.php b/course/format/classes/output/local/overview/activityname.php new file mode 100644 index 00000000000..6dce126d630 --- /dev/null +++ b/course/format/classes/output/local/overview/activityname.php @@ -0,0 +1,74 @@ +. + +namespace core_courseformat\output\local\overview; + +use cm_info; +use core\output\named_templatable; +use core\output\renderable; +use core\output\renderer_base; +use core_courseformat\base as course_format; +use stdClass; + +/** + * Class activityname + * + * @package core_courseformat + * @copyright 2025 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class activityname implements renderable, named_templatable { + /** + * Constructor. + * + * @param cm_info $cm The course module. + */ + public function __construct( + /** @var cm_info The course module. */ + protected cm_info $cm, + ) { + } + + /** + * Export this data so it can be used as the context for a mustache template. + * + * @param renderer_base $output Renderer base. + * @return stdClass + */ + public function export_for_template(renderer_base $output): stdClass { + $cm = $this->cm; + $section = $this->cm->get_section_info(); + $course = $this->cm->get_course(); + $format = course_format::instance($course); + return (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(), + ]; + } + + /** + * Get the template name. + * + * @param renderer_base $renderer Renderer base. + * @return string + */ + public function get_template_name(renderer_base $renderer): string { + return 'core_courseformat/local/overview/activityname'; + } +} diff --git a/course/format/templates/local/overview/activityname.mustache b/course/format/templates/local/overview/activityname.mustache new file mode 100644 index 00000000000..7e4b30052f0 --- /dev/null +++ b/course/format/templates/local/overview/activityname.mustache @@ -0,0 +1,46 @@ +{{! + 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/activityname + + The course overview activity name cell. + + Example context (json): + { + "activityname": "Activity name", + "activityurl": "http://moodle.com", + "sectiontitle": "Section title", + "visible": true, + "stealth": false + } +}} +
+ +
+ {{sectiontitle}} +
+
+ {{#hidden}} + {{#str}}hiddenfromstudents{{/str}} + {{/hidden}} + {{#stealth}} + {{#str}}hiddenoncoursepage{{/str}} + {{/stealth}} +
+
diff --git a/course/format/tests/activityoverviewbase_test.php b/course/format/tests/activityoverviewbase_test.php new file mode 100644 index 00000000000..1c508ffbf68 --- /dev/null +++ b/course/format/tests/activityoverviewbase_test.php @@ -0,0 +1,169 @@ +. + +namespace core_courseformat; + +/** + * Tests for course + * + * @package core_courseformat + * @category test + * @copyright 2025 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \core_courseformat\activityoverviewbase + */ +final class activityoverviewbase_test extends \advanced_testcase { + #[\Override()] + public static function setUpBeforeClass(): void { + global $CFG; + require_once($CFG->libdir . '/completionlib.php'); + require_once($CFG->dirroot . '/course/format/tests/fixtures/fake_activityoverview.php'); + parent::setUpBeforeClass(); + } + + /** + * Test get_name_overview method. + * + * @covers ::get_name_overview + */ + public function test_get_name_overview(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $generator = $this->getDataGenerator(); + $course = $generator->create_course(); + $activity = $this->getDataGenerator()->create_module('assign', ['course' => $course->id, 'name' => 'Test!']); + $modinfo = get_fast_modinfo($course); + $cm = $modinfo->get_cm($activity->cmid); + + $overview = new \core_courseformat\fake_activityoverview($cm); + + $result = $overview->get_name_overview(); + $this->assertEquals(get_string('name'), $result->get_name()); + $this->assertEquals('Test!', $result->get_value()); + $this->assertInstanceOf(\core_courseformat\output\local\overview\activityname::class, $result->get_content()); + } + + /** + * Test get_completion_overview method. + * + * @covers ::get_completion_overview + * @dataProvider provider_get_completion_overview + * @param int $setcompletion the completion status + */ + public function test_get_completion_overview( + int $setcompletion, + ): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $generator = $this->getDataGenerator(); + $course = $generator->create_course(['enablecompletion' => 1]); + + $user = $generator->create_user(); + $generator->enrol_user($user->id, $course->id); + + $this->setAdminUser(); + + $activity = $this->getDataGenerator()->create_module( + 'assign', + ['course' => $course->id, 'completion' => \COMPLETION_TRACKING_AUTOMATIC] + ); + + rebuild_course_cache($course->id, true); + $modinfo = get_fast_modinfo($course); + $cm = $modinfo->get_cm($activity->cmid); + + $overview = new \core_courseformat\fake_activityoverview($cm); + + $completion = (object) [ + 'coursemoduleid' => $cm->id, + 'timemodified' => time(), + 'viewed' => \COMPLETION_NOT_VIEWED, + 'overrideby' => null, + 'id' => 0, + 'completionstate' => $setcompletion, + 'userid' => $user->id, + ]; + $comletioninfo = new \completion_info($course); + $comletioninfo->internal_set_data($cm, $completion, true); + + $this->setUser($user); + + $result = $overview->get_completion_overview(); + $this->assertEquals(get_string('completion_status', 'completion'), $result->get_name()); + $this->assertEquals($setcompletion, $result->get_value()); + $this->assertInstanceOf(\core_courseformat\output\local\content\cm\completion::class, $result->get_content()); + } + + /** + * Data provider for test_get_completion_overview. + * + * @return array the testing scenarios + */ + public static function provider_get_completion_overview(): array { + return [ + 'complet' => [ + 'setcompletion' => \COMPLETION_COMPLETE, + ], + 'incomplete' => [ + 'setcompletion' => \COMPLETION_INCOMPLETE, + ], + 'complete pass' => [ + 'setcompletion' => \COMPLETION_COMPLETE_PASS, + ], + 'complete fail' => [ + 'setcompletion' => \COMPLETION_COMPLETE_FAIL, + ], + ]; + } + + /** + * Test get_completion_overview method on an activity with no completion. + * + * @covers ::get_completion_overview + */ + public function test_get_completion_overview_no_completion(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $generator = $this->getDataGenerator(); + $course = $generator->create_course(['enablecompletion' => 1]); + + $user = $generator->create_user(); + $generator->enrol_user($user->id, $course->id); + + $this->setAdminUser(); + + $activity = $this->getDataGenerator()->create_module( + 'assign', + ['course' => $course->id] + ); + + rebuild_course_cache($course->id, true); + $modinfo = get_fast_modinfo($course); + $cm = $modinfo->get_cm($activity->cmid); + + $overview = new \core_courseformat\fake_activityoverview($cm); + + $this->setUser($user); + + $result = $overview->get_completion_overview(); + $this->assertEquals(get_string('completion_status', 'completion'), $result->get_name()); + $this->assertEquals(null, $result->get_value()); + $this->assertEquals('-', $result->get_content()); + } +} diff --git a/course/format/tests/fixtures/fake_activityoverview.php b/course/format/tests/fixtures/fake_activityoverview.php new file mode 100644 index 00000000000..57040e2c4f9 --- /dev/null +++ b/course/format/tests/fixtures/fake_activityoverview.php @@ -0,0 +1,27 @@ +. + +namespace core_courseformat; + +/** + * This is a mock class for testing the abstract class activityoverviewbase. + * + * @package core_courseformat + * @copyright 2025 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class fake_activityoverview extends activityoverviewbase { +} diff --git a/course/format/tests/fixtures/wrongcm_activityoverview.php b/course/format/tests/fixtures/wrongcm_activityoverview.php new file mode 100644 index 00000000000..b376096947b --- /dev/null +++ b/course/format/tests/fixtures/wrongcm_activityoverview.php @@ -0,0 +1,27 @@ +. + +namespace mod_wrongcm\courseformat; + +/** + * This is a mock to simulate a activity plugin with a wrong overview class. + * + * @package core_courseformat + * @copyright 2025 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class overview { +} diff --git a/course/format/tests/local/overview/overviewfactory_test.php b/course/format/tests/local/overview/overviewfactory_test.php new file mode 100644 index 00000000000..76e845823fb --- /dev/null +++ b/course/format/tests/local/overview/overviewfactory_test.php @@ -0,0 +1,115 @@ +. + +namespace core_courseformat\local\overview; + +/** + * Tests for course + * + * @package core_courseformat + * @category test + * @copyright 2025 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \core_courseformat\local\overview\overviewfactory + */ +final class overviewfactory_test extends \advanced_testcase { + #[\Override()] + public static function setUpBeforeClass(): void { + global $CFG; + require_once($CFG->dirroot . '/course/format/tests/fixtures/wrongcm_activityoverview.php'); + parent::setUpBeforeClass(); + } + + /** + * Test create method on resource activities. + * + * @covers ::create + * @dataProvider create_resource_provider + * @param string $resourcetype + */ + public function test_create_resource( + string $resourcetype, + ): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $generator = $this->getDataGenerator(); + $course = $generator->create_course(); + $activity = $this->getDataGenerator()->create_module($resourcetype, ['course' => $course->id]); + $modinfo = get_fast_modinfo($course); + $cm = $modinfo->get_cm($activity->cmid); + + $overview = overviewfactory::create($cm); + + $this->assertInstanceOf(resourceoverview::class, $overview); + } + + /** + * Data provider for test_create_resource. + * + * @return array + */ + public static function create_resource_provider(): array { + return [ + 'book' => [ + 'resourcetype' => 'book', + ], + 'folder' => [ + 'resourcetype' => 'folder', + ], + 'page' => [ + 'resourcetype' => 'page', + ], + 'resource' => [ + 'resourcetype' => 'resource', + ], + 'url' => [ + 'resourcetype' => 'url', + ], + ]; + } + + /** + * Test create method on a fake activity with a wrong class. + * + * @covers ::create + */ + public function test_create_exception( + ): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $generator = $this->getDataGenerator(); + + $course = $generator->create_course(); + $activity = $this->getDataGenerator()->create_module('page', ['course' => $course->id]); + $modinfo = get_fast_modinfo($course); + $cm = $modinfo->get_cm($activity->cmid); + + // We know the factory will only use the modname to create the overview, + // this is a small trick to make the factory to use a wrong class and + // won't happen in a real code. However, this is the easiest way to test + // the exception. + $reflection = new \ReflectionClass($cm); + $property = $reflection->getProperty('modname'); + $property->setAccessible(true); + $property->setValue($cm, 'wrongcm'); + + $this->expectException(\coding_exception::class); + $this->expectExceptionMessageMatches("/.* must extend core_courseformat\\\\activityoverviewbase.*/"); + overviewfactory::create($cm); + } +} diff --git a/course/format/tests/local/overview/resourceoverview_test.php b/course/format/tests/local/overview/resourceoverview_test.php new file mode 100644 index 00000000000..cf97591a10e --- /dev/null +++ b/course/format/tests/local/overview/resourceoverview_test.php @@ -0,0 +1,106 @@ +. + +namespace core_courseformat\local\overview; + +/** + * Tests for resource overview + * + * @package core_courseformat + * @category test + * @copyright 2025 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \core_courseformat\local\overview\resourceoverview + */ +final class resourceoverview_test extends \advanced_testcase { + /** + * Test get_extra_overview_items method. + * + * @covers ::get_extra_overview_items + */ + public function test_get_extra_overview_items(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $generator = $this->getDataGenerator(); + $course = $generator->create_course(); + $activity = $this->getDataGenerator()->create_module('url', ['course' => $course->id]); + $modinfo = get_fast_modinfo($course); + $cm = $modinfo->get_cm($activity->cmid); + + $overview = overviewfactory::create($cm); + + $result = $overview->get_extra_overview_items(); + $this->assertCount(1, $result); + $this->assertArrayHasKey('type', $result); + $this->assertInstanceOf(\core_courseformat\local\overview\overviewitem::class, $result['type']); + } + + /** + * Test get_extra_type_overview method. + * + * @covers ::get_extra_overview_items + * @covers ::get_extra_type_overview + * @dataProvider get_extra_type_overview_provider + * @param string $resourcetype + */ + public function test_get_extra_type_overview( + string $resourcetype, + ): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $generator = $this->getDataGenerator(); + $course = $generator->create_course(); + $activity = $this->getDataGenerator()->create_module($resourcetype, ['course' => $course->id]); + $modinfo = get_fast_modinfo($course); + $cm = $modinfo->get_cm($activity->cmid); + + $overview = overviewfactory::create($cm); + + $items = $overview->get_extra_overview_items(); + $result = $items['type']; + + $this->assertEquals(get_string('resource_type'), $result->get_name()); + $this->assertEquals($cm->modfullname, $result->get_value()); + $this->assertEquals($cm->modfullname, $result->get_content()); + } + + /** + * Data provider for test_get_extra_type_overview. + * + * @return array + */ + public static function get_extra_type_overview_provider(): array { + return [ + 'book' => [ + 'resourcetype' => 'book', + ], + 'folder' => [ + 'resourcetype' => 'folder', + ], + 'page' => [ + 'resourcetype' => 'page', + ], + 'resource' => [ + 'resourcetype' => 'resource', + ], + 'url' => [ + 'resourcetype' => 'url', + ], + ]; + } +} diff --git a/lang/en/completion.php b/lang/en/completion.php index e7c74af1cbc..613eb0c3e86 100644 --- a/lang/en/completion.php +++ b/lang/en/completion.php @@ -87,6 +87,7 @@ $string['completion_help'] = 'If enabled, activity completion is tracked, either $string['completion_link'] = 'activity/completion'; $string['completion_manual'] = 'Students must manually mark the activity as done'; $string['completion_none'] = 'None'; +$string['completion_status'] = 'Completion status'; $string['completionactivitydefault'] = 'Use activity default'; $string['completionanygrade_desc'] = 'Any grade'; $string['completiondisabled'] = 'Disabled, not shown in activity settings'; diff --git a/lang/en/moodle.php b/lang/en/moodle.php index de13336de7f..7ee066f32b4 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -1856,6 +1856,7 @@ $string['resortsubcategoriesby'] = 'Sort subcategories by {$a} ascending'; $string['resortsubcategoriesbyreverse'] = 'Sort subcategories by {$a} descending'; $string['resortcourses'] = 'Sort courses'; $string['resource'] = 'Resource'; +$string['resource_type'] = 'Resource type'; $string['resourcedisplayauto'] = 'Automatic'; $string['resourcedisplaydownload'] = 'Force download'; $string['resourcedisplayembed'] = 'Embed'; diff --git a/lib/apis.json b/lib/apis.json index 6fc0c4e7321..6ee8b57894a 100644 --- a/lib/apis.json +++ b/lib/apis.json @@ -84,10 +84,15 @@ "allowedlevel2": false, "allowedspread": false }, + "courseformat": { + "component": "core_courseformat", + "allowedlevel2": true, + "allowedspread": true + }, "customfield": { - "component": "core_customfield", - "allowedlevel2": true, - "allowedspread": true + "component": "core_customfield", + "allowedlevel2": true, + "allowedspread": true }, "ddl": { "component": "core",