Install
$ agentstack add skill-saadrahman01-claude-moodle-dev-moodle-mobile-app ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo 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.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
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 →About
Moodle Mobile App Integration
Overview
Moodle Mobile app (Ionic/Angular) loads plugin UI as remote templates declared in db/mobile.php. The server returns Mustache-like templates + JS that the app renders inline. No app rebuild required — works on any user's installed app once the site declares the addon.
When to Use
- Adding plugin UI to mobile app
- Surfacing notifications, dashboard items, or activity views in the app
- Offline-capable content
- Push notifications for plugin events
Skip when: plugin only used in browser admin UI.
Architecture
Moodle server Moodle Mobile app
───────────── ─────────────────
db/mobile.php ────────────► Discovers addons via
classes/output/mobile.php tool_mobile_get_plugins_supporting_mobile
Returns ────────────► Renders Ionic components
templates (.html)
initial JS
styles
App calls a server function which returns:
- A Mustache-like template (Ionic + custom directives)
- Optional JS to run client-side
- Optional initial data
- Optional offline functions
db/mobile.php
[
'handlers' => [
'mainmenu' => [
'displaydata' => [
'title' => 'pluginname',
'icon' => 'list',
'class' => '',
],
'delegate' => 'CoreMainMenuDelegate',
'method' => 'mobile_main_menu_view',
'styles' => [
'url' => '/local/example/mobile/styles.css',
'version' => 1,
],
'offlinefunctions' => [
'mobile_main_menu_view' => [],
],
],
'courseoption' => [
'displaydata' => [
'title' => 'attendance',
'class' => '',
],
'delegate' => 'CoreCourseOptionsDelegate',
'method' => 'mobile_course_view',
],
],
'lang' => [
['pluginname', 'local_example'],
['attendance', 'local_example'],
],
],
];
Delegates
| Delegate | Where it shows | |----------|----------------| | CoreMainMenuDelegate | Main menu (bottom tab bar) | | CoreUserDelegate | User profile page | | CoreCourseOptionsDelegate | Course menu | | CoreCourseModuleDelegate | Activity in course (for mod_* plugins) | | CoreBlockDelegate | Sidebar block (for block_*) | | CoreSettingsDelegate | App settings page | | CoreMessageOutputDelegate | Message output handler |
classes/output/mobile.php
get_records('local_example_items',
['userid' => $USER->id], 'timecreated DESC', '*', 0, 20);
return [
'templates' => [
[
'id' => 'main',
'html' => self::render_main_template(),
],
],
'javascript' => self::get_javascript(),
'otherdata' => [
'items' => json_encode(array_values($items)),
'sesskey' => sesskey(),
],
'files' => [],
];
}
private static function render_main_template(): string {
return '
{{ \'plugin.local_example.attendance\' | translate }}
{{ item.name }}
{{ item.timecreated | coreFormatDate }}
{{ \'plugin.local_example.markpresent\' | translate }}
';
}
private static function get_javascript(): string {
return "
this.markPresent = (id) => {
const params = {sessionid: id, userid: this.CoreSitesProvider.getCurrentSite().getUserId()};
return this.CoreSitesProvider.getCurrentSite()
.write('local_example_mark_present', params)
.then((result) => {
this.CoreDomUtilsProvider.showToast('plugin.local_example.marked', true, 2000);
});
};";
}
}
Required web services
The function name in db/mobile.php ('method' => 'mobile_main_menu_view') must be exposed as a web service in db/services.php:
$functions = [
'local_example_mobile_main_menu_view' => [
'classname' => 'local_example\output\mobile',
'methodname' => 'mobile_main_menu_view',
'description' => 'Main menu mobile view',
'type' => 'read',
'capabilities' => '',
'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE],
],
];
Plus the AJAX/REST functions used inside the template (local_example_mark_present).
Template syntax
Mobile templates use Angular + Ionic with Moodle directives:
| Directive | Use | |-----------|-----| | {{ 'plugin.local_example.foo' \| translate }} | Lang string | | {{ value \| coreFormatDate }} | Format unix timestamp | | {{ html \| coreFormatText }} | Format Moodle text | | ` | Same, component form | | ngFor="let item of CONTENT_OTHERDATA.items" | Loop | | ngIf="condition" | Conditional | | (click)="handler()" | Event | | , , | Ionic UI | | ` | "Nothing here" placeholder |
CONTENT_OTHERDATA = data passed via 'otherdata'. Strings JSON-decoded automatically.
JavaScript scope
Helpers injected into JS scope:
| Object | Use | |--------|-----| | this.CoreSitesProvider | Current site, current user, web service calls | | this.CoreDomUtilsProvider | Toasts, alerts, modals | | this.CoreFilepoolProvider | Cache files for offline | | this.CoreUtilsProvider | Misc helpers | | this.refreshContent(false) | Reload current view |
Offline support
Add functions to 'offlinefunctions':
'offlinefunctions' => [
'mobile_main_menu_view' => [],
'local_example_get_items' => [],
],
App pre-fetches results during sync — they survive offline. For mutations (mark present), use the offline write API:
this.CoreSitesProvider.getCurrentSite().write(
'local_example_mark_present',
params,
{forceOffline: false, getFromCache: false}
).catch((error) => {
if (this.CoreUtilsProvider.isWebServiceError(error)) {
return Promise.reject(error);
}
// Queue for later sync
return this.CoreCourseProvider.storeOfflineAction(...);
});
Push notifications
Push delivery via Moodle's airnotifier service. Plugin events trigger notifications via \core\message\manager::send_message(). Mobile app receives them automatically when:
- Site has airnotifier configured (Site admin > Mobile > Push notifications)
- User logged into mobile app
- User has the addon's notification preference enabled
Testing
- Local dev: install Moodle Mobile app, point at
http://10.0.2.2:8000(Android emulator) or your LAN IP - Site admin > Mobile > Enable web services for mobile devices
- Log in to app — addon appears
- To force template re-fetch after server change: pull-to-refresh, or app settings > Synchronisation > Synchronise now
- Browser-based dev:
npx ionic serveagainstmoodle-mobile-appsource pointing at your site
Styles
local/example/mobile/styles.css (referenced from db/mobile.php):
.local_example-marked {
color: var(--ion-color-success);
font-weight: bold;
}
Bumping 'version' => N in db/mobile.php invalidates app's cached styles.
Common Mistakes
| Mistake | Fix | |---------|-----| | Method not exposed as web service | Declare in db/services.php with MOODLE_OFFICIAL_MOBILE_SERVICE | | Lang strings not declared in db/mobile.php | App can't render {{ 'plugin...' | translate }} | | ` instead of Ionic | Use Ionic components for native look | | Forgetting MOODLEOFFICIALMOBILESERVICE | Web service callable but not for mobile | | Heavy DB queries on every refresh | Cache via MUC + invalidate on update | | Hand-formatting timestamps | Use coreFormatDate filter | | Embedding HTML directly | Use coreFormatText for formattext-equivalent escaping | | No offlinefunctions for read views | Pre-fetch fails — declare them | | Not bumping styles version | App keeps old CSS | | Calling fetch() directly | Use CoreSitesProvider` — handles auth + tokens |
References
- Mobile addons: https://moodledev.io/general/app/development/plugins-development-guide
- Templates spec: https://moodledev.io/general/app/development/plugins-development-guide/templates
- Delegates list: https://moodledev.io/general/app/development/plugins-development-guide/api-reference
- Offline support: https://moodledev.io/general/app/development/plugins-development-guide/offline
- Push notifications: https://moodledev.io/general/app/development/plugins-development-guide/notifications
- App source: https://github.com/moodlehq/moodleapp
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: SaadRahman01
- Source: SaadRahman01/claude-moodle-dev
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.