This commit is contained in:
Ilya Tregubov 2024-04-10 14:30:31 +08:00
commit a7d9658d55
15 changed files with 707 additions and 40 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -43,8 +43,8 @@ const findMatchingTour = (tourDetails, filters) => {
*/
export const init = async(tourDetails, filters) => {
const requirements = [];
filters.forEach(filter => {
requirements.push(import(`tool_usertours/filter_${filter}`));
filters.forEach((filter) => {
requirements.push(import(filter));
});
const filterPlugins = await Promise.all(requirements);

View File

@ -16,6 +16,7 @@
namespace tool_usertours;
use coding_exception;
use core\output\inplace_editable;
/**
@ -534,10 +535,7 @@ class helper {
];
}, $tours);
$filternames = [];
foreach ($filters as $filter) {
$filternames[] = $filter::get_filter_name();
}
$filternames = self::get_clientside_filter_module_names($filters);
$PAGE->requires->js_call_amd('tool_usertours/usertours', 'init', [
$tourdetails,
@ -546,19 +544,48 @@ class helper {
}
}
/**
* Get the JS module names for the filters.
*
* @param array $filters
* @return array
* @throws coding_exception
*/
public static function get_clientside_filter_module_names(array $filters): array {
$filternames = [];
foreach ($filters as $filter) {
if ($component = \core_component::get_component_from_classname($filter)) {
$filternames[] = sprintf(
"%s/filter_%s",
$component,
$filter::get_filter_name(),
);
} else {
throw new \coding_exception("Could not determine component for filter class {$filter}");
}
}
return $filternames;
}
/**
* Get a list of all possible filters.
*
* @return array
*/
public static function get_all_filters() {
$filters = \core_component::get_component_classes_in_namespace('tool_usertours', 'local\filter');
$filters = array_keys($filters);
$hook = new hook\before_serverside_filter_fetch(array_keys(
\core_component::get_component_classes_in_namespace('tool_usertours', 'local\filter')
));
\core\di::get(\core\hook\manager::class)->dispatch($hook);
$filters = array_filter($filters, function ($filterclass) {
$rc = new \ReflectionClass($filterclass);
return $rc->isInstantiable();
});
$filters = array_filter(
$hook->get_filter_list(),
function ($filterclass) {
$rc = new \ReflectionClass($filterclass);
return $rc->isInstantiable();
}
);
$filters = array_merge($filters, static::get_all_clientside_filters());
@ -571,13 +598,18 @@ class helper {
* @return array
*/
public static function get_all_clientside_filters() {
$filters = \core_component::get_component_classes_in_namespace('tool_usertours', 'local\clientside_filter');
$filters = array_keys($filters);
$hook = new hook\before_clientside_filter_fetch(array_keys(
\core_component::get_component_classes_in_namespace('tool_usertours', 'local\clientside_filter')
));
\core\di::get(\core\hook\manager::class)->dispatch($hook);
$filters = array_filter($filters, function ($filterclass) {
$rc = new \ReflectionClass($filterclass);
return $rc->isInstantiable();
});
$filters = array_filter(
$hook->get_filter_list(),
function ($filterclass) {
$rc = new \ReflectionClass($filterclass);
return $rc->isInstantiable();
}
);
return $filters;
}

View File

@ -0,0 +1,74 @@
<?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 tool_usertours\hook;
/**
* Provides the ability to add and remove custom client-side filters to the user tour filter list.
*
* @package tool_usertours
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Provides the ability to add and remove custom client-side filters to the user tour filter list.')]
#[\core\attribute\tags('tool_usertours')]
class before_clientside_filter_fetch {
/**
* Create a new instance of the hook.
*
* @param array $filters
*/
public function __construct(
/** @var array The list of filters applied */
protected array $filters,
) {
}
/**
* Add a filter classname to the list of filters to be processed.
*
* @param string $classname
* @return self
*/
public function add_filter_by_classname(string $classname): self {
if (!\is_a($classname, \tool_usertours\local\clientside_filter\clientside_filter::class, true)) {
throw new \coding_exception("Invalid clientside filter class {$classname}");
}
$this->filters[] = $classname;
return $this;
}
/**
* Remove a filter classname from the list of filters to be processed.
*
* @param string $classname
* @return self
*/
public function remove_filter_by_classname(string $classname): self {
$this->filters = array_filter($this->filters, fn($filter) => $filter !== $classname);
return $this;
}
/**
* Get the list of filters to be processed.
*
* @return array
*/
public function get_filter_list(): array {
return $this->filters;
}
}

View File

@ -0,0 +1,78 @@
<?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 tool_usertours\hook;
/**
* Provides the ability to add and remove custom server-side filters to the user tour filter list.
*
* @package tool_usertours
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Provides the ability to add and remove custom server-side filters to the user tour filter list.')]
#[\core\attribute\tags('tool_usertours')]
class before_serverside_filter_fetch {
/**
* Create a new instance of the hook.
*
* @param array $filters
*/
public function __construct(
/** @var array The list of filters applied */
protected array $filters,
) {
}
/**
* Add a filter classname to the list of filters to be processed.
*
* @param string $classname
* @return self
*/
public function add_filter_by_classname(string $classname): self {
if (!\is_a($classname, \tool_usertours\local\filter\base::class, true)) {
throw new \coding_exception("Invalid filter class {$classname}");
}
if (\is_a($classname, \tool_usertours\local\clientside_filter\clientside_filter::class, true)) {
throw new \coding_exception("Invalid filter class {$classname} (client-side filter for server-side hook)");
}
$this->filters[] = $classname;
return $this;
}
/**
* Remove a filter classname from the list of filters to be processed.
*
* @param string $classname
* @return self
*/
public function remove_filter_by_classname(string $classname): self {
$this->filters = array_filter($this->filters, fn($filter) => $filter !== $classname);
return $this;
}
/**
* Get the list of filters to be processed.
*
* @return array
*/
public function get_filter_list(): array {
return $this->filters;
}
}

View File

@ -0,0 +1,45 @@
<?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/>.
/**
* Hook fixtures for testing of hooks.
*
* @package tool_usertours
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_usertours\test\hook\clientside_filter_for_serverside_hook;
defined('MOODLE_INTERNAL') || die();
final class filter_class extends \tool_usertours\local\clientside_filter\clientside_filter {
}
final class callback {
public static function callme(
\tool_usertours\hook\before_serverside_filter_fetch $hook
): void {
$hook->add_filter_by_classname(filter_class::class);
}
}
$callbacks = [
[
'hook' => \tool_usertours\hook\before_serverside_filter_fetch::class,
'callback' => callback::class . '::callme',
],
];

View File

@ -0,0 +1,61 @@
<?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/>.
/**
* Hook fixtures for testing of hooks.
*
* @package tool_usertours
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_usertours\test\hook;
defined('MOODLE_INTERNAL') || die();
final class serverside_filter_fixture extends \tool_usertours\local\filter\base {
}
final class clientside_filter_fixture extends \tool_usertours\local\clientside_filter\clientside_filter {
}
final class hook_fixtures {
public static function example_serverside_hook(
\tool_usertours\hook\before_serverside_filter_fetch $hook
): void {
// Add a valid serverside and an invalid clientside filter.
$hook->add_filter_by_classname(\tool_usertours\test\hook\serverside_filter_fixture::class);
$hook->remove_filter_by_classname(\tool_usertours\local\filter\accessdate::class);
}
public static function example_clientside_hook(
\tool_usertours\hook\before_clientside_filter_fetch $hook
): void {
$hook->add_filter_by_classname(\tool_usertours\test\hook\clientside_filter_fixture::class);
$hook->remove_filter_by_classname(\tool_usertours\local\clientside_filter\cssselector::class);
}
}
$callbacks = [
[
'hook' => \tool_usertours\hook\before_serverside_filter_fetch::class,
'callback' => \tool_usertours\test\hook\hook_fixtures::class . '::example_serverside_hook',
],
[
'hook' => \tool_usertours\hook\before_clientside_filter_fetch::class,
'callback' => \tool_usertours\test\hook\hook_fixtures::class . '::example_clientside_hook',
],
];

View File

@ -0,0 +1,43 @@
<?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/>.
/**
* Hook fixtures for testing of hooks.
*
* @package tool_usertours
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
final class nocomponent_clientside_filter_fixture extends \tool_usertours\local\clientside_filter\clientside_filter {
}
final class nocomponent_clientside_hook_fixtures {
public static function example_clientside_hook(
\tool_usertours\hook\before_clientside_filter_fetch $hook
): void {
$hook->add_filter_by_classname(\nocomponent_clientside_filter_fixture::class);
}
}
$callbacks = [
[
'hook' => \tool_usertours\hook\before_clientside_filter_fetch::class,
'callback' => \nocomponent_clientside_hook_fixtures::class . '::example_clientside_hook',
],
];

View File

@ -0,0 +1,43 @@
<?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/>.
/**
* Hook fixtures for testing of hooks.
*
* @package tool_usertours
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
final class nocomponent_serverside_filter_fixture {
}
final class nocomponent_serverside_hook_fixtures {
public static function example_serverside_hook(
\tool_usertours\hook\before_serverside_filter_fetch $hook
): void {
$hook->add_filter_by_classname(\nocomponent_serverside_filter_fixture::class);
}
}
$callbacks = [
[
'hook' => \tool_usertours\hook\before_serverside_filter_fetch::class,
'callback' => \nocomponent_serverside_hook_fixtures::class . '::example_serverside_hook',
],
];

View File

@ -0,0 +1,45 @@
<?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/>.
/**
* Hook fixtures for testing of hooks.
*
* @package tool_usertours
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_usertours\test\hook\serverside_filter_for_clientside_hook;
defined('MOODLE_INTERNAL') || die();
final class filter_class extends \tool_usertours\local\filter\base {
}
final class callback {
public static function callme(
\tool_usertours\hook\before_clientside_filter_fetch $hook
): void {
$hook->add_filter_by_classname(filter_class::class);
}
}
$callbacks = [
[
'hook' => \tool_usertours\hook\before_clientside_filter_fetch::class,
'callback' => callback::class . '::callme',
],
];

View File

@ -16,17 +16,18 @@
namespace tool_usertours;
use advanced_testcase;
/**
* Tests for helper.
*
* @package tool_usertours
* @category test
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \tool_usertours\helper
* @covers \tool_usertours\helper
* @covers \tool_usertours\hook\before_serverside_filter_fetch
* @covers \tool_usertours\hook\before_clientside_filter_fetch
*/
class helper_test extends advanced_testcase {
final class helper_test extends \advanced_testcase {
/**
* Data Provider for get_string_from_input.
*
@ -71,4 +72,117 @@ class helper_test extends advanced_testcase {
public function test_get_string_from_input($string, $expected): void {
$this->assertEquals($expected, helper::get_string_from_input($string));
}
public function test_get_all_filters(): void {
$filters = helper::get_all_filters();
$this->assertIsArray($filters);
array_map(
function ($filter) {
$this->assertIsString($filter);
$this->assertTrue(class_exists($filter));
$this->assertTrue(is_a($filter, \tool_usertours\local\filter\base::class, true));
$rc = new \ReflectionClass($filter);
$this->assertTrue($rc->isInstantiable());
},
$filters,
);
$this->assertNotContains(\tool_usertours\test\hook\serverside_filter_fixture::class, $filters);
$this->assertNotContains(\tool_usertours\test\hook\clientside_filter_fixture::class, $filters);
$this->assertContains(\tool_usertours\local\filter\accessdate::class, $filters);
$this->assertContains(\tool_usertours\local\clientside_filter\cssselector::class, $filters);
$filters = helper::get_all_clientside_filters();
array_map(
function ($filter) {
$this->assertIsString($filter);
},
$filters,
);
}
public function test_get_invalid_server_filter(): void {
\core\di::set(
\core\hook\manager::class,
\core\hook\manager::phpunit_get_instance([
'test_plugin1' => __DIR__ . '/fixtures/invalid_serverside_hook_fixture.php',
]),
);
$this->expectException(\coding_exception::class);
helper::get_all_filters();
}
public function test_clientside_filter_for_serverside_hook(): void {
\core\di::set(
\core\hook\manager::class,
\core\hook\manager::phpunit_get_instance([
'test_plugin1' => __DIR__ . '/fixtures/clientside_filter_for_serverside_hook.php',
]),
);
$this->expectException(\coding_exception::class);
helper::get_all_filters();
}
public function test_serverside_filter_for_clientside_hook(): void {
\core\di::set(
\core\hook\manager::class,
\core\hook\manager::phpunit_get_instance([
'test_plugin1' => __DIR__ . '/fixtures/serverside_filter_for_clientside_hook.php',
]),
);
$this->expectException(\coding_exception::class);
helper::get_all_clientside_filters();
}
public function test_filter_hooks(): void {
\core\di::set(
\core\hook\manager::class,
\core\hook\manager::phpunit_get_instance([
'test_plugin1' => __DIR__ . '/fixtures/hook_fixtures.php',
]),
);
$filters = helper::get_all_filters();
$this->assertIsArray($filters);
// Check the modifications from the serverside hook.
$this->assertContains(\tool_usertours\test\hook\serverside_filter_fixture::class, $filters);
$this->assertNotContains(\tool_usertours\test\hook\another_clientside_filter_fixture::class, $filters);
$this->assertNotContains(\tool_usertours\local\filter\accessdate::class, $filters);
// Check the modifications from the clientside hook.
$this->assertContains(\tool_usertours\test\hook\clientside_filter_fixture::class, $filters);
$this->assertNotContains(\tool_usertours\test\hook\another_serverside_filter_fixture::class, $filters);
$this->assertNotContains(\tool_usertours\local\clientside_filter\cssselector::class, $filters);
array_map(
function ($filter) {
$this->assertIsString($filter);
$this->assertTrue(class_exists($filter));
$this->assertTrue(is_a($filter, \tool_usertours\local\filter\base::class, true));
$rc = new \ReflectionClass($filter);
$this->assertTrue($rc->isInstantiable());
},
$filters,
);
}
public function test_get_clientside_filter_module_names(): void {
\core\di::set(
\core\hook\manager::class,
\core\hook\manager::phpunit_get_instance([
'test_plugin1' => __DIR__ . '/fixtures/invalid_clientside_hook_fixture.php',
]),
);
$filters = helper::get_all_clientside_filters();
$this->expectException(\coding_exception::class);
$this->expectExceptionMessageMatches('/Could not determine component/');
helper::get_clientside_filter_module_names($filters);
}
}

View File

@ -1,5 +1,12 @@
This files describes API changes in the tool_usertours code.
=== 4.4 ===
* New hooks have been provided to allow plugins to define their own server-side, and client-side user tour filters.
The new hooks are named:
- \tool_usertours\hook\before_serverside_filter_fetch
- \tool_usertours\hook\before_clientside_filter_fetch
These hooks allow addition, and removal, of filters.
=== 4.0 ===
* The `tourconfig` property returned by the `tool_usertours_fetch_and_start_tour`
external method has also an `endtourlabel` property that contains the label to be used

View File

@ -1090,6 +1090,35 @@ $cache = ' . var_export($cache, true) . ';
return [$type, $plugin];
}
/**
* Fetch the component name from a Moodle PSR-like namespace.
*
* Note: Classnames in the flat underscore_class_name_format are not supported.
*
* @param string $classname
* @return null|string The component name, or null if a matching component was not found
*/
public static function get_component_from_classname(string $classname): ?string {
$components = static::get_component_names(true);
$classname = ltrim($classname, '\\');
// Prefer PSR-4 classnames.
$parts = explode('\\', $classname);
if ($parts) {
$component = array_shift($parts);
if (array_search($component, $components) !== false) {
return $component;
}
}
// Note: Frankenstyle classnames are not supported as they lead to false positives, for example:
// \core_typo\example => \core instead of \core_typo because it does not exist
// Please *do not* add support for Frankenstyle classnames. They will break other things.
return null;
}
/**
* Return exact absolute path to a plugin directory.
*
@ -1407,18 +1436,16 @@ $cache = ' . var_export($cache, true) . ';
}
/**
* Returns a list of frankenstyle component names.
* Returns a list of frankenstyle component names, including all plugins, subplugins, and subsystems.
*
* E.g.
* [
* 'core_course',
* 'core_message',
* 'mod_assign',
* ...
* ]
* @return array the list of frankenstyle component names.
* Note: By default the 'core' subsystem is not included.
*
* @param bool $includecore Whether to include the 'core' subsystem
* @return string[] the list of frankenstyle component names.
*/
public static function get_component_names(): array {
public static function get_component_names(
bool $includecore = false,
): array {
$componentnames = [];
// Get all plugins.
foreach (self::get_plugin_types() as $plugintype => $typedir) {
@ -1430,6 +1457,11 @@ $cache = ' . var_export($cache, true) . ';
foreach (self::get_core_subsystems() as $subsystemname => $subsystempath) {
$componentnames[] = 'core_' . $subsystemname;
}
if ($includecore) {
$componentnames[] = 'core';
}
return $componentnames;
}

View File

@ -411,6 +411,73 @@ class component_test extends advanced_testcase {
}
}
/**
* Unit tests for get_component_from_classname.
*
* @dataProvider get_component_from_classname_provider
* @param string $classname The class name to test
* @param string|null $expected The expected component
* @covers \core_component::get_component_from_classname
*/
public function test_get_component_from_classname(
string $classname,
string|null $expected,
): void {
$this->assertEquals(
$expected,
\core_component::get_component_from_classname($classname),
);
}
/**
* Data provider for get_component_from_classname tests.
*
* @return array
*/
public static function get_component_from_classname_provider(): array {
// Start off with testcases which have the leading \.
$testcases = [
// Core.
[\core\example::class, 'core'],
// A core subsystem.
[\core_message\example::class, 'core_message'],
// A fake core subsystem.
[\core_fake\example::class, null],
// A plugin.
[\mod_forum\example::class, 'mod_forum'],
// A plugin in the old style is not supported.
[\mod_forum_example::class, null],
// A fake plugin.
[\mod_fake\example::class, null],
// A subplugin.
[\tiny_link\example::class, 'tiny_link'],
];
// Duplicate the testcases, adding a nested namespace.
$testcases = array_merge(
$testcases,
array_map(
fn ($testcase) => [$testcase[0] . '\\in\\sub\\directory', $testcase[1]],
$testcases,
),
);
// Duplicate the testcases, removing the leading \.
return array_merge(
$testcases,
array_map(
fn ($testcase) => [ltrim($testcase[0], '\\'), $testcase[1]],
$testcases,
),
);
}
public function test_deprecated_get_component_directory(): void {
$plugintypes = core_component::get_plugin_types();
foreach ($plugintypes as $plugintype => $fulldir) {
@ -873,22 +940,31 @@ class component_test extends advanced_testcase {
/**
* Test the get_component_names() method.
*
* @dataProvider get_component_names_provider
* @param bool $includecore Whether to include core in the list.
* @param bool $coreexpected Whether core is expected to be in the list.
*/
public function test_get_component_names(): void {
public function test_get_component_names(
bool $includecore,
bool $coreexpected,
): void {
global $CFG;
$componentnames = \core_component::get_component_names();
$componentnames = \core_component::get_component_names($includecore);
// We should have an entry for each plugin type.
$plugintypes = \core_component::get_plugin_types();
$numplugintypes = 0;
foreach ($plugintypes as $type => $typedir) {
foreach (\core_component::get_plugin_list($type) as $plugin) {
$numplugintypes++;
}
foreach (array_keys($plugintypes) as $type) {
$numplugintypes += count(\core_component::get_plugin_list($type));
}
// And an entry for each core subsystem.
$numcomponents = $numplugintypes + count(\core_component::get_core_subsystems());
if ($coreexpected) {
// Add one for core.
$numcomponents++;
}
$this->assertEquals($numcomponents, count($componentnames));
// Check a few of the known plugin types to confirm their presence at their respective type index.
@ -896,6 +972,23 @@ class component_test extends advanced_testcase {
$this->assertContains('mod_forum', $componentnames);
$this->assertContains('tool_usertours', $componentnames);
$this->assertContains('core_favourites', $componentnames);
if ($coreexpected) {
$this->assertContains('core', $componentnames);
} else {
$this->assertNotContains('core', $componentnames);
}
}
/**
* Data provider for get_component_names() test.
*
* @return array
*/
public static function get_component_names_provider(): array {
return [
[false, false],
[true, true],
];
}
/**