Merge branch 'MDL-81456-main' of https://github.com/andrewnicols/moodle
This commit is contained in:
@@ -91,7 +91,7 @@ class hook_list_table extends flexible_table {
|
||||
*/
|
||||
public function out(): void {
|
||||
// All hook consumers referenced from the db/hooks.php files.
|
||||
$hookmanager = \core\hook\manager::get_instance();
|
||||
$hookmanager = \core\di::get(\core\hook\manager::class);
|
||||
$allhooks = (array)$hookmanager->get_all_callbacks();
|
||||
|
||||
// Add any unused hooks.
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ require_once($CFG->libdir . '/tablelib.php');
|
||||
admin_externalpage_setup('hooksoverview');
|
||||
require_capability('moodle/site:config', \core\context\system::instance());
|
||||
|
||||
$hookmanager = \core\hook\manager::get_instance();
|
||||
$hookmanager = \core\di::get(\core\hook\manager::class);
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading(get_string('hooksoverview', 'core_admin'));
|
||||
|
||||
@@ -334,7 +334,7 @@ class manager {
|
||||
|
||||
// Allow plugins to callback as soon possible after user has passed MFA.
|
||||
$hook = new \tool_mfa\hook\after_user_passed_mfa();
|
||||
\core\hook\manager::get_instance()->dispatch($hook);
|
||||
\core\di::get(\core\hook\manager::class)->dispatch($hook);
|
||||
|
||||
// Add/update record in DB for users last mfa auth.
|
||||
self::update_pass_time();
|
||||
|
||||
@@ -130,7 +130,7 @@ class user_bulk_action_form extends moodleform {
|
||||
}
|
||||
|
||||
// Any plugin can append user bulk actions to this list by implementing a hook callback.
|
||||
\core\hook\manager::get_instance()->dispatch($hook);
|
||||
\core\di::get(\core\hook\manager::class)->dispatch($hook);
|
||||
|
||||
// This method may be called from 'Bulk actions' and 'Browse user list' pages. Some actions
|
||||
// may be irrelevant in one of the contexts and they can be excluded by specifying the
|
||||
|
||||
@@ -797,7 +797,7 @@ abstract class backup_cron_automated_helper {
|
||||
|
||||
// Exclude events defined by hook.
|
||||
$hook = new \core_backup\hook\before_course_modified_check();
|
||||
\core\hook\manager::get_instance()->dispatch($hook);
|
||||
\core\di::get(\core\hook\manager::class)->dispatch($hook);
|
||||
|
||||
foreach ($readers as $readerpluginname => $reader) {
|
||||
$params = [
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
use core\{
|
||||
di,
|
||||
hook,
|
||||
};
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
require_once($CFG->libdir.'/formslib.php');
|
||||
@@ -419,7 +424,7 @@ class course_edit_form extends moodleform {
|
||||
$handler->instance_form_definition($mform, empty($course->id) ? 0 : $course->id);
|
||||
|
||||
$hook = new \core_course\hook\after_form_definition($this, $mform);
|
||||
\core\hook\manager::get_instance()->dispatch($hook);
|
||||
di::get(hook\manager::class)->dispatch($hook);
|
||||
|
||||
// When two elements we need a group.
|
||||
$buttonarray = array();
|
||||
@@ -502,7 +507,7 @@ class course_edit_form extends moodleform {
|
||||
$handler->instance_form_definition_after_data($mform, empty($courseid) ? 0 : $courseid);
|
||||
|
||||
$hook = new \core_course\hook\after_form_definition_after_data($this, $mform);
|
||||
\core\hook\manager::get_instance()->dispatch($hook);
|
||||
di::get(hook\manager::class)->dispatch($hook);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -550,7 +555,7 @@ class course_edit_form extends moodleform {
|
||||
$errors = array_merge($errors, $handler->instance_form_validation($data, $files));
|
||||
|
||||
$hook = new \core_course\hook\after_form_validation($this, $data, $files);
|
||||
\core\hook\manager::get_instance()->dispatch($hook);
|
||||
di::get(hook\manager::class)->dispatch($hook);
|
||||
$pluginerrors = $hook->get_errors();
|
||||
if (!empty($pluginerrors)) {
|
||||
$errors = array_merge($errors, $pluginerrors);
|
||||
|
||||
@@ -72,8 +72,9 @@ class cmactions extends baseactions {
|
||||
|
||||
// Modules may add some logic to renaming.
|
||||
$modinfo = get_fast_modinfo($cm->course);
|
||||
$hook = new \core_courseformat\hook\after_cm_name_edited($modinfo->get_cm($cm->id), $name);
|
||||
\core\hook\manager::get_instance()->dispatch($hook);
|
||||
\core\di::get(\core\hook\manager::class)->dispatch(
|
||||
new \core_courseformat\hook\after_cm_name_edited($modinfo->get_cm($cm->id), $name),
|
||||
);
|
||||
|
||||
// Attempt to update the grade item if relevant.
|
||||
$grademodule = $DB->get_record($cm->modname, ['id' => $cm->instance]);
|
||||
|
||||
+14
-7
@@ -24,6 +24,10 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
use core\{
|
||||
di,
|
||||
hook,
|
||||
};
|
||||
use core_course\external\course_summary_exporter;
|
||||
use core_courseformat\base as course_format;
|
||||
use core_courseformat\formatactions;
|
||||
@@ -2032,10 +2036,11 @@ function create_course($data, $editoroptions = NULL) {
|
||||
$data->id = $newcourseid;
|
||||
|
||||
// Dispatch the hook for post course create actions.
|
||||
$hook = new \core_course\hook\after_course_created(
|
||||
course: $data,
|
||||
di::get(hook\manager::class)->dispatch(
|
||||
new \core_course\hook\after_course_created(
|
||||
course: $data,
|
||||
),
|
||||
);
|
||||
\core\di::get(\core\hook\manager::class)->dispatch($hook);
|
||||
|
||||
// Setup the blocks
|
||||
blocks_add_default_course_blocks($course);
|
||||
@@ -2068,8 +2073,9 @@ function create_course($data, $editoroptions = NULL) {
|
||||
$data->id = $course->id;
|
||||
$handler->instance_form_save($data, true);
|
||||
|
||||
$hook = new \core_course\hook\after_form_submission($data, true);
|
||||
\core\hook\manager::get_instance()->dispatch($hook);
|
||||
di::get(hook\manager::class)->dispatch(
|
||||
new \core_course\hook\after_form_submission($data, true),
|
||||
);
|
||||
|
||||
return $course;
|
||||
}
|
||||
@@ -2186,8 +2192,9 @@ function update_course($data, $editoroptions = NULL) {
|
||||
$handler = core_course\customfield\course_handler::create();
|
||||
$handler->instance_form_save($data);
|
||||
|
||||
$hook = new \core_course\hook\after_form_submission($data);
|
||||
\core\hook\manager::get_instance()->dispatch($hook);
|
||||
di::get(hook\manager::class)->dispatch(
|
||||
new \core_course\hook\after_form_submission($data),
|
||||
);
|
||||
|
||||
// Update with the new data
|
||||
$DB->update_record('course', $data);
|
||||
|
||||
+10
-5
@@ -27,8 +27,12 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
use \core_grades\component_gradeitems;
|
||||
use core\{
|
||||
di,
|
||||
hook,
|
||||
};
|
||||
use core_courseformat\formatactions;
|
||||
use core_grades\component_gradeitems;
|
||||
|
||||
require_once($CFG->dirroot.'/course/lib.php');
|
||||
|
||||
@@ -725,11 +729,12 @@ function update_moduleinfo($cm, $moduleinfo, $course, $mform = null) {
|
||||
}
|
||||
|
||||
if ($cm->name != $moduleinfo->name) {
|
||||
$hook = new \core_courseformat\hook\after_cm_name_edited(
|
||||
get_fast_modinfo($course)->get_cm($cm->id),
|
||||
$moduleinfo->name
|
||||
di::get(hook\manager::class)->dispatch(
|
||||
new \core_courseformat\hook\after_cm_name_edited(
|
||||
get_fast_modinfo($course)->get_cm($cm->id),
|
||||
$moduleinfo->name
|
||||
),
|
||||
);
|
||||
\core\hook\manager::get_instance()->dispatch($hook);
|
||||
}
|
||||
|
||||
$cm->name = $moduleinfo->name;
|
||||
|
||||
@@ -45,7 +45,6 @@ use Psr\EventDispatcher\StoppableEventInterface;
|
||||
final class manager implements
|
||||
EventDispatcherInterface,
|
||||
ListenerProviderInterface {
|
||||
|
||||
/** @var ?manager the one instance of listener provider and dispatcher */
|
||||
private static $instance = null;
|
||||
|
||||
@@ -81,41 +80,25 @@ final class manager implements
|
||||
/**
|
||||
* Factory method for testing of hook manager in PHPUnit tests.
|
||||
*
|
||||
* Please note that the result of this method should typically be passed to \core\di::set().
|
||||
*
|
||||
* @param array $componentfiles list of hook callback files for each component.
|
||||
* @param bool $persist If true, the test instance will be stored in self::$instance. Be sure to call $this->resetAfterTest()
|
||||
* in your test if you use this.
|
||||
* @return self
|
||||
*/
|
||||
public static function phpunit_get_instance(array $componentfiles, bool $persist = false): manager {
|
||||
public static function phpunit_get_instance(array $componentfiles): manager {
|
||||
if (!PHPUNIT_TEST) {
|
||||
throw new \coding_exception('Invalid call of manager::phpunit_get_instance() outside of tests');
|
||||
}
|
||||
$instance = new self();
|
||||
$instance->load_callbacks($componentfiles);
|
||||
if ($persist) {
|
||||
self::$instance = $instance;
|
||||
}
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset self::$instance so that future calls to ::get_instance() will return a regular instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function phpunit_reset_instance(): void {
|
||||
if (!PHPUNIT_TEST) {
|
||||
throw new \coding_exception('Invalid call of manager::phpunit_reset_instance() outside of tests');
|
||||
}
|
||||
self::$instance = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override hook callbacks for testing purposes.
|
||||
*
|
||||
* @param string $hookname
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public function phpunit_redirect_hook(string $hookname, callable $callback): void {
|
||||
if (!PHPUNIT_TEST) {
|
||||
@@ -126,8 +109,6 @@ final class manager implements
|
||||
|
||||
/**
|
||||
* Cancel all redirections of hook callbacks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function phpunit_stop_redirections(): void {
|
||||
if (!PHPUNIT_TEST) {
|
||||
@@ -167,7 +148,7 @@ final class manager implements
|
||||
* callable MUST be type-compatible with $event.
|
||||
* Please note that in Moodle the callable must be a string.
|
||||
*/
|
||||
public function getListenersForEvent(object $event): iterable {
|
||||
public function getListenersForEvent(object $event): iterable { // phpcs:ignore
|
||||
// Callbacks are sorted by priority, highest first at load-time.
|
||||
$hookclassname = get_class($event);
|
||||
$callbacks = $this->get_callbacks_for_hook($hookclassname);
|
||||
@@ -254,7 +235,7 @@ final class manager implements
|
||||
debugging(
|
||||
"Cannot execute callback '$callablename' from '$component'" .
|
||||
"Callback method not callable.",
|
||||
DEBUG_DEVELOPER
|
||||
DEBUG_DEVELOPER,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -324,8 +305,6 @@ final class manager implements
|
||||
|
||||
/**
|
||||
* Initialise list of all callbacks for each hook.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function init_standard_callbacks(): void {
|
||||
global $CFG;
|
||||
@@ -380,7 +359,6 @@ final class manager implements
|
||||
* Load callbacks from component db/hooks.php files.
|
||||
*
|
||||
* @param array $componentfiles list of all components with their callback files
|
||||
* @return void
|
||||
*/
|
||||
private function load_callbacks(array $componentfiles): void {
|
||||
$this->allcallbacks = [];
|
||||
@@ -504,14 +482,13 @@ final class manager implements
|
||||
*
|
||||
* @param string $component component where hook callbacks are defined
|
||||
* @param string $hookfile file with list of all callbacks for component
|
||||
* @return void
|
||||
*/
|
||||
private function add_component_callbacks(string $component, string $hookfile): void {
|
||||
if (!file_exists($hookfile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parsecallbacks = function($hookfile) {
|
||||
$parsecallbacks = function ($hookfile) {
|
||||
$callbacks = [];
|
||||
include($hookfile);
|
||||
return $callbacks;
|
||||
@@ -577,7 +554,7 @@ final class manager implements
|
||||
if (!str_contains($classmethod, '::')) {
|
||||
debugging(
|
||||
"Hook callback definition contains invalid 'callback' static class method string in '$component'",
|
||||
DEBUG_DEVELOPER
|
||||
DEBUG_DEVELOPER,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -599,7 +576,7 @@ final class manager implements
|
||||
public function is_deprecated_plugin_callback(string $plugincallback): bool {
|
||||
debugging(
|
||||
'is_deprecated_plugin_callback method is deprecated, use get_hooks_deprecating_plugin_callback instead.',
|
||||
DEBUG_DEVELOPER
|
||||
DEBUG_DEVELOPER,
|
||||
);
|
||||
return (bool)$this->get_hooks_deprecating_plugin_callback($plugincallback);
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class primary extends view {
|
||||
|
||||
// Allow plugins to add nodes to the primary navigation.
|
||||
$hook = new \core\hook\navigation\primary_extend($this);
|
||||
\core\hook\manager::get_instance()->dispatch($hook);
|
||||
\core\di::get(\core\hook\manager::class)->dispatch($hook);
|
||||
|
||||
// Search and set the active node.
|
||||
$this->set_active_node();
|
||||
|
||||
+15
-9
@@ -28,6 +28,11 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
use core\{
|
||||
di,
|
||||
hook,
|
||||
};
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
// CONSTANTS (Encased in phpdoc proper comments).
|
||||
@@ -3581,7 +3586,7 @@ function delete_user(stdClass $user) {
|
||||
$hook = new \core_user\hook\before_user_deleted(
|
||||
user: $user,
|
||||
);
|
||||
\core\di::get(\core\hook\manager::class)->dispatch($hook);
|
||||
di::get(hook\manager::class)->dispatch($hook);
|
||||
|
||||
// Keep user record before updating it, as we have to pass this to user_deleted event.
|
||||
$olduser = clone $user;
|
||||
@@ -4085,8 +4090,7 @@ function complete_user_login($user, array $extrauserinfo = []) {
|
||||
$event->trigger();
|
||||
|
||||
// Allow plugins to callback as soon possible after user has completed login.
|
||||
$hook = new \core\hook\user\after_complete_login();
|
||||
\core\hook\manager::get_instance()->dispatch($hook);
|
||||
di::get(\core\hook\manager::class)->dispatch(new \core\hook\user\after_complete_login());
|
||||
|
||||
// Check if the user is using a new browser or session (a new MoodleSession cookie is set in that case).
|
||||
// If the user is accessing from the same IP, ignore everything (most of the time will be a new session in the same browser).
|
||||
@@ -7436,8 +7440,8 @@ function get_plugins_with_function($function, $file = 'lib.php', $include = true
|
||||
foreach ($pluginfunctions as $plugintype => $plugins) {
|
||||
foreach ($plugins as $plugin => $unusedfunction) {
|
||||
$component = $plugintype . '_' . $plugin;
|
||||
if ($hooks = \core\hook\manager::get_instance()->get_hooks_deprecating_plugin_callback($plugincallback)) {
|
||||
if (\core\hook\manager::get_instance()->is_deprecating_hook_present($component, $plugincallback)) {
|
||||
if ($hooks = di::get(hook\manager::class)->get_hooks_deprecating_plugin_callback($plugincallback)) {
|
||||
if (di::get(hook\manager::class)->is_deprecating_hook_present($component, $plugincallback)) {
|
||||
// Ignore the old callback, it is there only for older Moodle versions.
|
||||
unset($pluginfunctions[$plugintype][$plugin]);
|
||||
} else {
|
||||
@@ -7675,8 +7679,9 @@ function component_callback($component, $function, array $params = array(), $def
|
||||
|
||||
if ($functionname) {
|
||||
if ($migratedtohook) {
|
||||
if ($hooks = \core\hook\manager::get_instance()->get_hooks_deprecating_plugin_callback($function)) {
|
||||
if (\core\hook\manager::get_instance()->is_deprecating_hook_present($component, $function)) {
|
||||
$hookmanager = di::get(hook\manager::class);
|
||||
if ($hooks = $hookmanager->get_hooks_deprecating_plugin_callback($function)) {
|
||||
if ($hookmanager->is_deprecating_hook_present($component, $function)) {
|
||||
// Do not call the old lib.php callback,
|
||||
// it is there for compatibility with older Moodle versions only.
|
||||
return null;
|
||||
@@ -7776,8 +7781,9 @@ function component_class_callback($classname, $methodname, array $params, $defau
|
||||
$functionparts = explode('\\', trim($fullfunction, '\\'));
|
||||
$component = $functionparts[0];
|
||||
$callback = end($functionparts);
|
||||
if ($hooks = \core\hook\manager::get_instance()->get_hooks_deprecating_plugin_callback($callback)) {
|
||||
if (\core\hook\manager::get_instance()->is_deprecating_hook_present($component, $callback)) {
|
||||
$hookmanager = di::get(hook\manager::class);
|
||||
if ($hooks = $hookmanager->get_hooks_deprecating_plugin_callback($callback)) {
|
||||
if ($hookmanager->is_deprecating_hook_present($component, $callback)) {
|
||||
// Do not call the old class callback,
|
||||
// it is there for compatibility with older Moodle versions only.
|
||||
return null;
|
||||
|
||||
@@ -262,6 +262,7 @@ if (PHPUNIT_UTIL) {
|
||||
|
||||
// Make sure the hook manager gets initialised before anybody tries to override callbacks,
|
||||
// this is not using caches intentionally to help with development.
|
||||
// Note: We cannot use DI at this point in the bootstrap either.
|
||||
\core\hook\manager::get_instance();
|
||||
|
||||
// Is database and dataroot ready for testing?
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use core\{
|
||||
di,
|
||||
hook,
|
||||
};
|
||||
|
||||
/**
|
||||
* Advanced PHPUnit test case customised for Moodle.
|
||||
*
|
||||
@@ -486,7 +491,7 @@ abstract class advanced_testcase extends base_testcase {
|
||||
* @return void
|
||||
*/
|
||||
public function redirectHook(string $hookname, callable $callback): void {
|
||||
\core\hook\manager::get_instance()->phpunit_redirect_hook($hookname, $callback);
|
||||
di::get(hook\manager::class)->phpunit_redirect_hook($hookname, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -495,7 +500,7 @@ abstract class advanced_testcase extends base_testcase {
|
||||
* @return void
|
||||
*/
|
||||
public function stopHookRedirections(): void {
|
||||
\core\hook\manager::get_instance()->phpunit_stop_redirections();
|
||||
di::get(hook\manager::class)->phpunit_stop_redirections();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -763,4 +768,66 @@ abstract class advanced_testcase extends base_testcase {
|
||||
|
||||
return $clock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a mocked plugintype to Moodle.
|
||||
*
|
||||
* A new plugintype name must be provided with a path to the plugintype's root.
|
||||
*
|
||||
* Please note that tests calling this method must be run in separate isolation mode.
|
||||
* Please avoid using this if at all possible.
|
||||
*
|
||||
* @param string $plugintype The name of the plugintype
|
||||
* @param string $path The path to the plugintype's root
|
||||
*/
|
||||
protected function add_mocked_plugintype(
|
||||
string $plugintype,
|
||||
string $path,
|
||||
): void {
|
||||
require_phpunit_isolation();
|
||||
|
||||
$mockedcomponent = new \ReflectionClass(\core_component::class);
|
||||
$plugintypes = $mockedcomponent->getStaticPropertyValue('plugintypes');
|
||||
|
||||
if (array_key_exists($plugintype, $plugintypes)) {
|
||||
throw new \coding_exception("The plugintype '{$plugintype}' already exists.");
|
||||
}
|
||||
|
||||
$plugintypes[$plugintype] = $path;
|
||||
$mockedcomponent->setStaticPropertyValue('plugintypes', $plugintypes);
|
||||
|
||||
$this->resetDebugging();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a mocked plugin to Moodle.
|
||||
*
|
||||
* A new plugin name must be provided with a path to the plugin's root.
|
||||
* The plugin type must already exist (or have been mocked separately).
|
||||
*
|
||||
* Please note that tests calling this method must be run in separate isolation mode.
|
||||
* Please avoid using this if at all possible.
|
||||
*
|
||||
* @param string $plugintype The name of the plugintype
|
||||
* @param string $pluginname The name of the plugin
|
||||
* @param string $path The path to the plugin's root
|
||||
*/
|
||||
protected function add_mocked_plugin(
|
||||
string $plugintype,
|
||||
string $pluginname,
|
||||
string $path,
|
||||
): void {
|
||||
require_phpunit_isolation();
|
||||
|
||||
$mockedcomponent = new \ReflectionClass(\core_component::class);
|
||||
$plugins = $mockedcomponent->getStaticPropertyValue('plugins');
|
||||
|
||||
if (!array_key_exists($plugintype, $plugins)) {
|
||||
$plugins[$plugintype] = [];
|
||||
}
|
||||
|
||||
$plugins[$plugintype][$pluginname] = $path;
|
||||
$mockedcomponent->setStaticPropertyValue('plugins', $plugins);
|
||||
$this->resetDebugging();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,11 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
use core\{
|
||||
di,
|
||||
hook,
|
||||
};
|
||||
|
||||
require_once(__DIR__.'/../../testing/classes/util.php');
|
||||
require_once(__DIR__ . "/coverage_info.php");
|
||||
|
||||
@@ -105,10 +110,7 @@ class phpunit_util extends testing_util {
|
||||
global $DB, $CFG, $USER, $SITE, $COURSE, $PAGE, $OUTPUT, $SESSION, $FULLME, $FILTERLIB_PRIVATE;
|
||||
|
||||
// Stop all hook redirections.
|
||||
\core\hook\manager::get_instance()->phpunit_stop_redirections();
|
||||
|
||||
// Reset the hook manager instance.
|
||||
\core\hook\manager::phpunit_reset_instance();
|
||||
di::get(hook\manager::class)->phpunit_stop_redirections();
|
||||
|
||||
// Stop any message redirection.
|
||||
self::stop_message_redirection();
|
||||
|
||||
+46
-107
@@ -16,6 +16,8 @@
|
||||
|
||||
namespace core\hook;
|
||||
|
||||
use core\di;
|
||||
|
||||
/**
|
||||
* Hooks tests.
|
||||
*
|
||||
@@ -49,27 +51,8 @@ final class manager_test extends \advanced_testcase {
|
||||
$componentfiles = [
|
||||
'test_plugin1' => __DIR__ . '/../fixtures/hook/hooks1_valid.php',
|
||||
];
|
||||
$testmanager = manager::phpunit_get_instance($componentfiles, true);
|
||||
$testmanager = manager::phpunit_get_instance($componentfiles);
|
||||
$this->assertSame(['test_plugin\\hook\\hook'], $testmanager->get_hooks_with_callbacks());
|
||||
// With $persist = true, get_instance() returns the test instance until reset.
|
||||
$manager = manager::get_instance();
|
||||
$this->assertSame($testmanager, $manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test resetting the manager test instance.
|
||||
*
|
||||
* @covers ::phpunit_reset_instance
|
||||
* @return void
|
||||
*/
|
||||
public function test_phpunit_reset_instance(): void {
|
||||
$testmanager = manager::phpunit_get_instance([], true);
|
||||
$manager = manager::get_instance();
|
||||
$this->assertSame($testmanager, $manager);
|
||||
|
||||
manager::phpunit_reset_instance();
|
||||
$manager = manager::get_instance();
|
||||
$this->assertNotSame($testmanager, $manager);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,42 +303,13 @@ final class manager_test extends \advanced_testcase {
|
||||
/**
|
||||
* Register a fake plugin called hooktest in the component manager.
|
||||
*
|
||||
* @return void
|
||||
* Tests consuming this helpers must run in a separate process.
|
||||
*/
|
||||
protected function setup_hooktest_plugin(): void {
|
||||
global $CFG;
|
||||
|
||||
$mockedcomponent = new \ReflectionClass(\core_component::class);
|
||||
$mockedplugintypes = $mockedcomponent->getProperty('plugintypes');
|
||||
$mockedplugintypes->setAccessible(true);
|
||||
$plugintypes = $mockedplugintypes->getValue();
|
||||
$plugintypes['fake'] = "{$CFG->dirroot}/lib/tests/fixtures/fakeplugins";
|
||||
$mockedplugintypes->setValue(null, $plugintypes);
|
||||
$mockedplugins = $mockedcomponent->getProperty('plugins');
|
||||
$mockedplugins->setAccessible(true);
|
||||
$plugins = $mockedplugins->getValue();
|
||||
$plugins['fake'] = ['hooktest' => "{$CFG->dirroot}/lib/tests/fixtures/fakeplugins/hooktest"];
|
||||
$mockedplugins->setValue(null, $plugins);
|
||||
$this->resetDebugging();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the fake plugin to avoid interference with other tests.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function remove_hooktest_plugin(): void {
|
||||
$mockedcomponent = new \ReflectionClass(\core_component::class);
|
||||
$mockedplugintypes = $mockedcomponent->getProperty('plugintypes');
|
||||
$mockedplugintypes->setAccessible(true);
|
||||
$plugintypes = $mockedplugintypes->getValue();
|
||||
unset($plugintypes['fake']);
|
||||
$mockedplugintypes->setValue(null, $plugintypes);
|
||||
$mockedplugins = $mockedcomponent->getProperty('plugins');
|
||||
$mockedplugins->setAccessible(true);
|
||||
$plugins = $mockedplugins->getValue();
|
||||
unset($plugins['fake']);
|
||||
$mockedplugins->setValue(null, $plugins);
|
||||
$this->add_mocked_plugintype('fake', "{$CFG->dirroot}/lib/tests/fixtures/hook/fakeplugins");
|
||||
$this->add_mocked_plugin('fake', 'hooktest', "{$CFG->dirroot}/lib/tests/fixtures/hook/fakeplugins/hooktest");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -363,25 +317,24 @@ final class manager_test extends \advanced_testcase {
|
||||
*
|
||||
* The original callback should be called, but a debugging message should be output.
|
||||
*
|
||||
* @covers ::get_hooks_deprecating_plugin_callback()
|
||||
* @covers ::is_deprecating_hook_present()
|
||||
* @return void
|
||||
* @throws \coding_exception
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function test_migrated_callback(): void {
|
||||
$this->resetAfterTest(true);
|
||||
// Include plugin hook discovery agent, and the hook that replaces the callback.
|
||||
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hooks.php');
|
||||
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hook/hook_replacing_callback.php');
|
||||
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hooks.php');
|
||||
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook/hook_replacing_callback.php');
|
||||
// Register the fake plugin with the component manager.
|
||||
$this->setup_hooktest_plugin();
|
||||
|
||||
// Register the fake plugin with the hook manager, but don't define any hook callbacks.
|
||||
manager::phpunit_get_instance(
|
||||
[
|
||||
'fake_hooktest' => __DIR__ . '/../fixtures/fakeplugins/hooktest/db/hooks_nocallbacks.php',
|
||||
],
|
||||
true
|
||||
di::set(
|
||||
manager::class,
|
||||
manager::phpunit_get_instance(
|
||||
[
|
||||
'fake_hooktest' => __DIR__ . '/../fixtures/hook/fakeplugins/hooktest/db/hooks_nocallbacks.php',
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
// Confirm a non-deprecated callback is called as expected.
|
||||
@@ -393,10 +346,9 @@ final class manager_test extends \advanced_testcase {
|
||||
component_callback('fake_hooktest', 'old_callback', [], null, true)
|
||||
);
|
||||
$this->assertDebuggingCalled(
|
||||
'Callback old_callback in fake_hooktest component should be migrated to new hook '.
|
||||
'Callback old_callback in fake_hooktest component should be migrated to new hook ' .
|
||||
'callback for fake_hooktest\hook\hook_replacing_callback'
|
||||
);
|
||||
$this->remove_hooktest_plugin();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,26 +356,23 @@ final class manager_test extends \advanced_testcase {
|
||||
*
|
||||
* The original callback should not be called, and no debugging should be output.
|
||||
*
|
||||
* @covers ::get_hooks_deprecating_plugin_callback()
|
||||
* @covers ::is_deprecating_hook_present()
|
||||
* @return void
|
||||
* @throws \coding_exception
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function test_migrated_callback_with_replacement(): void {
|
||||
$this->resetAfterTest(true);
|
||||
// Include plugin hook discovery agent, and the hook that replaces the callback, and a hook callback for the hook.
|
||||
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hooks.php');
|
||||
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hook/hook_replacing_callback.php');
|
||||
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hook_callbacks.php');
|
||||
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hooks.php');
|
||||
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook/hook_replacing_callback.php');
|
||||
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook_callbacks.php');
|
||||
// Register the fake plugin with the component manager.
|
||||
$this->setup_hooktest_plugin();
|
||||
|
||||
// Register the fake plugin with the hook manager, including the hook callback.
|
||||
manager::phpunit_get_instance(
|
||||
[
|
||||
'fake_hooktest' => __DIR__ . '/../fixtures/fakeplugins/hooktest/db/hooks.php',
|
||||
],
|
||||
true
|
||||
di::set(
|
||||
manager::class,
|
||||
manager::phpunit_get_instance([
|
||||
'fake_hooktest' => __DIR__ . '/../fixtures/hook/fakeplugins/hooktest/db/hooks.php',
|
||||
]),
|
||||
);
|
||||
|
||||
// Confirm a non-deprecated callback is called as expected.
|
||||
@@ -432,7 +381,6 @@ final class manager_test extends \advanced_testcase {
|
||||
// Confirm the deprecated callback is not called, as expected.
|
||||
$this->assertNull(component_callback('fake_hooktest', 'old_callback', [], null, true));
|
||||
$this->assertDebuggingNotCalled();
|
||||
$this->remove_hooktest_plugin();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -440,26 +388,23 @@ final class manager_test extends \advanced_testcase {
|
||||
*
|
||||
* The original class callback should be called, but a debugging message should be output.
|
||||
*
|
||||
* @covers ::get_hooks_deprecating_plugin_callback()
|
||||
* @covers ::is_deprecating_hook_present()
|
||||
* @return void
|
||||
* @throws \coding_exception
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function test_migrated_class_callback(): void {
|
||||
$this->resetAfterTest(true);
|
||||
// Include plugin hook discovery agent, the class containing callbacks, and the hook that replaces the class callback.
|
||||
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/callbacks.php');
|
||||
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hooks.php');
|
||||
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hook/hook_replacing_class_callback.php');
|
||||
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/callbacks.php');
|
||||
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hooks.php');
|
||||
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook/hook_replacing_class_callback.php');
|
||||
// Register the fake plugin with the component manager.
|
||||
$this->setup_hooktest_plugin();
|
||||
|
||||
// Register the fake plugin with the hook manager, but don't define any hook callbacks.
|
||||
manager::phpunit_get_instance(
|
||||
[
|
||||
'fake_hooktest' => __DIR__ . '/../fixtures/fakeplugins/hooktest/db/hooks_nocallbacks.php',
|
||||
],
|
||||
true
|
||||
di::set(
|
||||
manager::class,
|
||||
manager::phpunit_get_instance([
|
||||
'fake_hooktest' => __DIR__ . '/../fixtures/hook/fakeplugins/hooktest/db/hooks_nocallbacks.php',
|
||||
]),
|
||||
);
|
||||
|
||||
// Confirm a non-deprecated class callback is called as expected.
|
||||
@@ -474,10 +419,9 @@ final class manager_test extends \advanced_testcase {
|
||||
component_class_callback('fake_hooktest\callbacks', 'old_class_callback', [], null, true)
|
||||
);
|
||||
$this->assertDebuggingCalled(
|
||||
'Callback callbacks::old_class_callback in fake_hooktest component should be migrated to new hook '.
|
||||
'Callback callbacks::old_class_callback in fake_hooktest component should be migrated to new hook ' .
|
||||
'callback for fake_hooktest\hook\hook_replacing_class_callback'
|
||||
);
|
||||
$this->remove_hooktest_plugin();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -485,28 +429,25 @@ final class manager_test extends \advanced_testcase {
|
||||
*
|
||||
* The original callback should not be called, and no debugging should be output.
|
||||
*
|
||||
* @covers ::get_hooks_deprecating_plugin_callback()
|
||||
* @covers ::is_deprecating_hook_present()
|
||||
* @return void
|
||||
* @throws \coding_exception
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function test_migrated_class_callback_with_replacement(): void {
|
||||
$this->resetAfterTest(true);
|
||||
// Include plugin hook discovery agent, the class containing callbacks, the hook that replaces the class callback,
|
||||
// and a hook callback for the new hook.
|
||||
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/callbacks.php');
|
||||
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hooks.php');
|
||||
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hook/hook_replacing_class_callback.php');
|
||||
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hook_callbacks.php');
|
||||
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/callbacks.php');
|
||||
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hooks.php');
|
||||
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook/hook_replacing_class_callback.php');
|
||||
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook_callbacks.php');
|
||||
// Register the fake plugin with the component manager.
|
||||
$this->setup_hooktest_plugin();
|
||||
|
||||
// Register the fake plugin with the hook manager, including the hook callback.
|
||||
manager::phpunit_get_instance(
|
||||
[
|
||||
'fake_hooktest' => __DIR__ . '/../fixtures/fakeplugins/hooktest/db/hooks.php',
|
||||
],
|
||||
true
|
||||
di::set(
|
||||
manager::class,
|
||||
manager::phpunit_get_instance([
|
||||
'fake_hooktest' => __DIR__ . '/../fixtures/hook/fakeplugins/hooktest/db/hooks.php',
|
||||
]),
|
||||
);
|
||||
|
||||
// Confirm a non-deprecated class callback is called as expected.
|
||||
@@ -518,14 +459,12 @@ final class manager_test extends \advanced_testcase {
|
||||
// Confirm the deprecated class callback is not called, as expected.
|
||||
$this->assertNull(component_class_callback('fake_hooktest\callbacks', 'old_class_callback', [], null, true));
|
||||
$this->assertDebuggingNotCalled();
|
||||
$this->remove_hooktest_plugin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalise the sort order of callbacks to help with asserts.
|
||||
*
|
||||
* @param array $callbacks
|
||||
* @return void
|
||||
*/
|
||||
private function normalise_callbacks(array &$callbacks): void {
|
||||
foreach ($callbacks as &$callback) {
|
||||
|
||||
@@ -103,8 +103,6 @@ information provided here is intended especially for developers.
|
||||
* Deprecated core\hook\manager::is_deprecated_plugin_callback() in favour of ::get_hooks_deprecating_plugin_callback(),
|
||||
which will return the classnames of hooks deprecating a callback, or null if it's not deprecated. The return value can be cast
|
||||
to bool if the original functionality is desired.
|
||||
* core\hook\manager::phpunit_get_instance() now sets self::$instance to the mocked instance if the optional $persist argument is
|
||||
true, so future calls to ::get_instance() will return it.
|
||||
* The triggerSelector method in the `core/comboboxsearch/search_combobox` JS module is deprecated. It was not used.
|
||||
* PHPUnit has been upgraded to 9.6 (see MDL-81266 for details).
|
||||
The main goal of the update is to allow developers to know in advance,
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
namespace mod_quiz;
|
||||
|
||||
use coding_exception;
|
||||
use core\{
|
||||
di,
|
||||
hook,
|
||||
};
|
||||
use mod_quiz\event\quiz_grade_updated;
|
||||
use mod_quiz\hook\structure_modified;
|
||||
use question_engine_data_mapper;
|
||||
@@ -100,7 +104,7 @@ class grade_calculator {
|
||||
component_class_callback($callbackclass, 'callback', [$quiz->id], null, true);
|
||||
}
|
||||
|
||||
\core\hook\manager::get_instance()->dispatch(new structure_modified($this->quizobj->get_structure()));
|
||||
di::get(hook\manager::class)->dispatch(new structure_modified($this->quizobj->get_structure()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,6 +21,10 @@ use block_contents;
|
||||
use cm_info;
|
||||
use coding_exception;
|
||||
use context_module;
|
||||
use core\{
|
||||
di,
|
||||
hook,
|
||||
};
|
||||
use Exception;
|
||||
use html_writer;
|
||||
use mod_quiz\hook\attempt_state_changed;
|
||||
@@ -1787,7 +1791,7 @@ class quiz_attempt {
|
||||
// Trigger event.
|
||||
$this->fire_state_transition_event('\mod_quiz\event\attempt_submitted', $timestamp, $studentisonline);
|
||||
|
||||
\core\hook\manager::get_instance()->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
|
||||
di::get(hook\manager::class)->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
|
||||
// Tell any access rules that care that the attempt is over.
|
||||
$this->get_access_manager($timestamp)->current_attempt_finished();
|
||||
}
|
||||
@@ -1835,7 +1839,7 @@ class quiz_attempt {
|
||||
|
||||
$this->fire_state_transition_event('\mod_quiz\event\attempt_becameoverdue', $timestamp, $studentisonline);
|
||||
|
||||
\core\hook\manager::get_instance()->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
|
||||
di::get(hook\manager::class)->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
|
||||
$transaction->allow_commit();
|
||||
|
||||
quiz_send_overdue_message($this);
|
||||
@@ -1859,7 +1863,7 @@ class quiz_attempt {
|
||||
|
||||
$this->fire_state_transition_event('\mod_quiz\event\attempt_abandoned', $timestamp, $studentisonline);
|
||||
|
||||
\core\hook\manager::get_instance()->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
|
||||
di::get(hook\manager::class)->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
|
||||
|
||||
$transaction->allow_commit();
|
||||
}
|
||||
@@ -1890,7 +1894,7 @@ class quiz_attempt {
|
||||
|
||||
$this->fire_state_transition_event('\mod_quiz\event\attempt_reopened', $timestamp, false);
|
||||
|
||||
\core\hook\manager::get_instance()->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
|
||||
di::get(hook\manager::class)->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
|
||||
$timeclose = $this->get_access_manager($timestamp)->get_end_time($this->attempt);
|
||||
if ($timeclose && $timestamp > $timeclose) {
|
||||
$this->process_finish($timestamp, false, $timeclose);
|
||||
|
||||
@@ -35,6 +35,10 @@ require_once($CFG->libdir . '/completionlib.php');
|
||||
require_once($CFG->libdir . '/filelib.php');
|
||||
require_once($CFG->libdir . '/questionlib.php');
|
||||
|
||||
use core\{
|
||||
di,
|
||||
hook,
|
||||
};
|
||||
use core_question\local\bank\condition;
|
||||
use mod_quiz\access_manager;
|
||||
use mod_quiz\event\attempt_submitted;
|
||||
@@ -147,7 +151,7 @@ function quiz_create_attempt(quiz_settings $quizobj, $attemptnumber, $lastattemp
|
||||
$attempt->timecheckstate = $timeclose;
|
||||
}
|
||||
|
||||
\core\hook\manager::get_instance()->dispatch(new attempt_state_changed(null, $attempt));
|
||||
di::get(hook\manager::class)->dispatch(new attempt_state_changed(null, $attempt));
|
||||
|
||||
return $attempt;
|
||||
}
|
||||
@@ -459,7 +463,7 @@ function quiz_delete_attempt($attempt, $quiz) {
|
||||
component_class_callback($callbackclass, 'callback', [$quiz->id], null, true);
|
||||
}
|
||||
|
||||
\core\hook\manager::get_instance()->dispatch(new attempt_state_changed($attempt, null));
|
||||
di::get(hook\manager::class)->dispatch(new attempt_state_changed($attempt, null));
|
||||
}
|
||||
|
||||
// Search quiz_attempts for other instances by this user.
|
||||
|
||||
Reference in New Issue
Block a user