AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Moodle Hooks Api

skill-saadrahman01-claude-moodle-dev-moodle-hooks-api · by SaadRahman01

Use when implementing or migrating to the Moodle 4.4+ Hooks API. Covers hook class authoring (db/hooks.php), callback registration, dispatching, replacing legacy magic callbacks (extend_navigation, before_http_headers, etc.), and testing hook listeners.

No reviews yet
0 installs
48 views
0.0% view→install

Install

$ agentstack add skill-saadrahman01-claude-moodle-dev-moodle-hooks-api

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-saadrahman01-claude-moodle-dev-moodle-hooks-api)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Moodle Hooks Api? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Moodle Hooks API

Overview

Moodle 4.4 introduced a typed Hooks API (core\hook\manager) replacing the unmaintainable jungle of magic callback functions like _extend_navigation, _before_http_headers, _extend_settings_navigation, etc. Hooks are real classes with typed payloads, dispatched through \core\di::get(\core\hook\manager::class). Plugins register interest via db/hooks.php.

When to Use

  • Adding cross-cutting behavior triggered by core (navigation, page output, user login, course events) without monkey-patching
  • Migrating a plugin off legacy magic callbacks (deprecated 4.4+, will be removed)
  • Authoring a hook class in core or in a plugin that other plugins can listen to
  • Writing tests for hook listeners

Skip when: the event you care about is a \core\event\* (Events 2 API — different system, used for audit/logging). Hooks are for modifying behavior; Events are for reacting to facts.

Core concepts

| Concept | Where it lives | Purpose | |---|---|---| | Hook class | classes/hook/.php | Typed payload, optional setters for listeners to mutate | | Listener registration | db/hooks.php | Maps hook class -> callback (Class::method) + priority | | Dispatcher call | Core or plugin code | \core\di::get(\core\hook\manager::class)->dispatch(new \plugin\hook\thing(...)); | | Listener method | Any class | Static or instance method taking the hook instance |

Listening to a core hook

db/hooks.php:

 \core\hook\output\before_standard_top_of_body_html_generation::class,
        'callback' => \local_example\hook_listener::class . '::inject_banner',
        'priority' => 100, // higher runs first
    ],
];

classes/hook_listener.php:

add_html('Hello, ' . s($USER->firstname) . '');
    }
}

After adding or changing db/hooks.php, purge caches: php admin/cli/purge_caches.php.

Authoring your own hook

classes/hook/before_widget_render.php:

html .= $html; }
    public function get_html(): string { return $this->html; }

    public function stop(): void { $this->stopped = true; }
    public function isPropagationStopped(): bool { return $this->stopped; }
}

Dispatch:

$hook = new \local_example\hook\before_widget_render($widgetid, $context);
\core\di::get(\core\hook\manager::class)->dispatch($hook);
if ($hook->isPropagationStopped()) {
    return ''; // veto
}
echo $hook->get_html();

Migrating magic callbacks

| Legacy callback | Replacement hook | |---|---| | _extend_navigation | \core\hook\navigation\primary_extend (4.5+) — check core for current name | | _before_http_headers | \core\hook\output\before_http_headers | | _before_standard_top_of_body_html | \core\hook\output\before_standard_top_of_body_html_generation | | _before_footer | \core\hook\output\before_footer_html_generation | | _after_config | \core\hook\after_config | | _extend_settings_navigation | check \core\hook\navigation\* for current name |

Migration steps:

  1. Search for legacy callbacks: grep -rn "function.*_extend_navigation\|_before_http_headers\|_after_config" .
  2. For each, find the matching hook class in lib/classes/hook/ of your Moodle install.
  3. Create db/hooks.php mapping; move the callback body into a listener class.
  4. Delete the legacy function from lib.php.
  5. Bump version.php, purge caches, run tests.

Testing hook listeners

resetAfterTest();
        $this->setUser($this->getDataGenerator()->create_user());

        $hook = new \core\hook\output\before_standard_top_of_body_html_generation();
        \core\di::get(\core\hook\manager::class)->dispatch($hook);

        $this->assertStringContainsString('Hello,', $hook->get_output());
    }

    public function test_skipped_for_guest(): void {
        $this->resetAfterTest();
        $this->setGuestUser();

        $hook = new \core\hook\output\before_standard_top_of_body_html_generation();
        \core\di::get(\core\hook\manager::class)->dispatch($hook);

        $this->assertStringNotContainsString('Hello,', $hook->get_output());
    }
}

CLI: list registered hooks

php admin/cli/hooks_list.php          # all hooks + listeners
php admin/cli/hooks_list.php --hook=core\\hook\\output\\before_http_headers

Gotchas

  • Purge caches after any db/hooks.php change. Listener registration is cached.
  • Priority is a hint — order between equal priorities is undefined. Don't rely on it for correctness.
  • Stoppable hooks: only implement stoppable_event_interface if vetoing is meaningful. Most output hooks are not stoppable.
  • Don't dispatch hooks from constructors or setUp() — they can have side effects.
  • DI container: always resolve manager via \core\di::get(...). Don't new it.
  • Backporting: pre-4.4 plugins still need legacy callbacks. Either keep both with a version check, or drop pre-4.4 support and bump requires in version.php.

Checklist

  • [ ] db/hooks.php exists with correct hook class FQCN
  • [ ] Listener class is autoloadable (under classes/ with PSR-4)
  • [ ] Caches purged after edit
  • [ ] Tests cover both the action and the no-op branch
  • [ ] Legacy callback removed (or version-gated) after migration
  • [ ] version.php bumped

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.