Moodle

Moodle plugin development built the way core is built

We write plugins that use Moodle's own APIs for data, permissions, events, tasks, privacy and backup, so they install cleanly and keep working after an upgrade.

  • Moodle coding style enforced
  • PHPUnit and Behat tests
  • Yours to host and modify
What is included
  • The right plugin type
  • Capabilities and contexts
  • Database through XMLDB
  • Events and tasks
  • Privacy and backup
  • PHPUnit and Behat coverage
Get a free quote Reply within one business day. NDA on request.
The problem

Why custom Moodle plugins go wrong

You have reached the point where settings and existing plugins do not cover the requirement. Maybe the plugins directory has something close, but it was last updated years ago. Maybe the feature is specific to how your organization approves, schedules or reports training. Either way you need code, and you need it to behave like a part of Moodle instead of something bolted on.

That is harder than it sounds. A plugin that only "works" can still skip capability checks, write straight to tables, ignore course backup and leave personal data behind when a user is deleted. Those gaps show up months later as a security finding, a failed restore or a blocked upgrade. This page explains how we build plugins so that they do not. If you are not yet sure a plugin is needed at all, read Moodle customization first.

  • The plugin you rely on was abandoned

    A contributed plugin does the job, but its maintainer stopped updating it. It throws deprecation notices on your current release and nobody knows whether it will install on the next one.

  • It works, but only for admins

    The feature was tested with an administrator account. Teachers see errors, or worse, students can open a page they should never reach because no capability was checked.

  • Backups lose the custom data

    A course is backed up and restored for the new term, and everything stored by the custom activity is missing, because backup and restore support was never written.

  • Upgrades stall on one plugin

    The site cannot move forward because a custom plugin uses functions that were removed. There are no tests, so nobody can say what a fix might break.

What we do

What goes into a Moodle plugin we ship

Whatever the plugin type, the same parts have to be present before we call it finished.

The right plugin type

Local, block, activity module, authentication, enrolment, report, course format, theme or question type. Choosing correctly decides where the feature appears and which APIs it gets.

Capabilities and contexts

Each action is guarded by a capability defined in the plugin and checked in the right context, with sensible defaults per role that your administrators can change.

Database through XMLDB

Tables are defined in install.xml, changed through versioned upgrade steps, and read and written with the DB API, so the plugin runs on every database Moodle supports.

Events and tasks

The plugin fires its own events for logs and reports, observes core events such as course completion, and moves slow work to scheduled or adhoc tasks.

Privacy and backup

A privacy provider that can export and delete one user's data on request, and backup and restore steps so activity data travels with the course.

PHPUnit and Behat coverage

PHPUnit tests for the logic and Behat scenarios for the screens, run on each commit against the Moodle releases you need to support.

Typical projects

Moodle plugins we get asked to write

01

A custom report

Completion by department, overdue training by manager or seat usage by client, with filters, CSV and spreadsheet export, a capability per audience and scheduled email delivery.

02

A new activity type

An activity with its own settings form, grading and completion rules, for example a practical sign-off or an attendance record, that appears in the activity chooser like any other.

03

An enrolment or authentication plugin

Access granted by rules Moodle does not ship with: an approval chain, a voucher code, a purchase in another system or a sign-in against your own user store.

04

Adopting an abandoned plugin

We fork a contributed plugin your site depends on, replace deprecated calls, add tests and keep it compatible with new releases, all in your repository.

In depth

Inside a well-built Moodle plugin

Pick the type before writing a line

Moodle has dozens of plugin types and each one plugs into a different part of the system. An activity module (mod) lives inside courses, has grades and completion, and must support backup. A block is a panel that can sit on the dashboard or a course page. An enrol plugin controls how users get into courses, and an auth plugin controls how they log in. Reports, course formats and question types each have their own base classes. A local plugin is the catch-all for site-wide rules and integrations. Putting a feature into the wrong type is the most common design mistake we see, and it is expensive to move later.

Files Moodle expects

A plugin announces itself in version.php with its component name, its version and the Moodle release it requires. Language strings sit in the lang folder, never hard-coded. The db folder holds install.xml for tables, upgrade.php for schema changes, access.php for capabilities, events.php for observers, tasks.php for scheduled tasks and services.php for web service functions. Classes are namespaced and autoloaded. Output goes through Mustache templates and renderers so that a theme can restyle it. We follow that layout exactly, because it is what lets administrators, other developers and Moodle itself understand the plugin.

Security is the capability check you did not skip

Every page starts by requiring login and checking a capability in a context. Every form carries a session key. Input arrives through typed parameters, and queries use placeholders through the DB API instead of concatenated SQL. Output is escaped by the template. Moodle treats all of this as mandatory, and it is the reason a student cannot open the teacher view by changing a URL.

The parts that get skipped

Three pieces are often missing from plugins we are asked to fix. The privacy API, which tells Moodle what personal data the plugin stores and how to export or delete it for a data request. Backup and restore, without which a course copy silently drops the plugin's data. And upgrade steps with savepoints, so that a schema change applies once and in order on every site. We scope all three from the start. When the plugin has to be reachable from other systems or from the app, we also expose external functions, as described on our Moodle API development page.

Testing and style

Code is checked with Moodle's code sniffer rules and PHPDoc checks on every commit, alongside PHPUnit tests that use Moodle's data generators and Behat scenarios that click through the feature as a teacher and as a student. The same pipeline runs against each Moodle release and database you want supported. That is how we can tell you, before an upgrade, whether your plugin is ready.

Process

From specification to installed plugin

  1. 1

    Role-by-role specification

    We write down what the plugin does for each role, what data it stores and which Moodle releases it must support, then quote against that document.

  2. 2

    Plugin design review

    Plugin type, tables, capabilities, events and screens are sketched and agreed, so there are no surprises about where the feature will appear.

  3. 3

    Coding against tests

    Code is written in a Git repository you can see, with style checks, PHPUnit and Behat running on each push.

  4. 4

    Staging install

    The plugin is installed on a copy of your site. You test it with real courses and roles, and we check backup, restore and privacy export.

  5. 5

    Versioned release

    You receive a versioned zip and the repository. We can keep the plugin compatible with future Moodle releases under a maintenance agreement.

Deliverables

In the plugin package you receive

  • A plugin that installs through the standard Moodle installer
  • Capabilities your administrators can assign per role
  • Privacy export and deletion support
  • Automated tests you can run yourself
  • Source code, repository and release notes
FAQ

Moodle plugin questions answered plainly

Which Moodle plugin type do we need?
It depends on where the feature should live. Something learners do inside a course is an activity module, a panel of information is a block, a site-wide rule or integration is a local plugin, and custom ways of joining courses or signing in are enrolment and authentication plugins. We recommend a type during the design review and explain why.
Will the plugin work on our Moodle release and the next one?
We build for the releases named in the specification and test against each one automatically. New releases sometimes deprecate functions, so some upkeep is normal over the years. Because the plugin has tests, that work is predictable, and we can handle it for you as part of Moodle maintenance.
Can you fix or extend a plugin from the Moodle plugins directory?
Yes. Contributed plugins are open source, so we can fork one, fix compatibility problems or add what you need. Where the original maintainer is active we offer the change back upstream, which saves you from carrying a private fork forever.
Who owns the code of a plugin you write for us?
You do. The source code is handed over in your repository and you are free to host, modify or share it. We do not add license keys, call-home checks or encoded files, and we sign an NDA on request if the logic is commercially sensitive.
Could our plugin be published in the Moodle plugins directory?
It can be submitted if you want that. The directory runs its own review, and plugins that follow coding style, declare privacy data and avoid core changes are in a much better position. Approval is decided by the reviewers and not by us, so we prepare the submission and respond to their feedback.
What affects the cost of a Moodle plugin?
The number of screens, the amount of stored data, whether it needs grading, backup support and web services, and how many Moodle releases and databases it must run on. A report or a small local plugin is a modest job. A full activity module is a larger one. The quote fixes the scope and the milestones.
Can we hire a developer for ongoing plugin work?
Yes. If you have a backlog of plugin tasks instead of one defined project, you can hire a Moodle developer from our team part time, full time or hourly, with a project manager on our side keeping the work on track.
Start a project

Describe the plugin you have in mind

A paragraph about who uses it and what it should do is enough to start. We will suggest a plugin type, ask about anything unclear and prepare a free quote.

  • Free consultation and quote
  • NDA on request
  • You own the source code
  • Reply within one business day
Add budget and timeline optional, helps us quote faster

This form is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.