Als Moodle iets niet standaard kan, is de eerste reflex om de Moodle-plugindirectory te doorzoeken. Er staan bijna 2.800 plugins in, iemand heeft jouw probleem vast al opgelost. En soms is dat zo. Maar na het bouwen van meer dan 300 maatwerkaplugins over vijftien jaar kennen we de echte kosten van afhankelijkheid van community-plugins voor enterprise-eisen, en die zijn zelden zo laag als ze er aanvankelijk uitzien.
Dit is geen pleidooi tegen open source community-plugins. Veel zijn uitstekend, en we gebruiken ze zelf ook. Dit is een pleidooi voor bewuste keuzes: wanneer is maatwerkontwikkeling het juiste antwoord, en wat krijg je precies als je een plugin laat bouwen die specifiek voor jouw organisatie is gemaakt?
De verborgen kosten van standaard plugins
Versiecompatibiliteit: de upgradebelasting
Moodle brengt ruwweg elke zes maanden een hoofdversie uit. Elke releasecyclus worden organisaties die community-plugins gebruiken geconfronteerd met wat wij de upgradebelasting noemen: de tijd en het risico van controleren of elke geïnstalleerde plugin voor de nieuwe Moodle-versie is bijgewerkt, het testen van plugins die compatibiliteit claimen maar anders gedragen bij jouw data, en het omgaan met plugins waarvan de beheerder al lang is vertrokken.
We hebben Moodle-installaties geaudit bij klanten als KLM, Nedap en BSL Media & Learning, waarbij tien tot dertig procent van de geïnstalleerde plugins ofwel niet meer onderhouden werd, ofwel draaide op versies die twee of drie hoofdreleases achterliepen. Die plugins zorgen niet alleen voor technische schuld. Ze worden de reden dat Moodle-upgrades worden uitgesteld, wat op termijn leidt tot grotere en ingrijpendere migraties.
Een maatwerk-plugin die is gebouwd naar de specificaties van jouw organisatie, wordt onderhouden door een partij met een directe contractuele verantwoordelijkheid om hem werkend te houden. Als Moodle 5.x breaking API-wijzigingen introduceert, updaten wij de plugins van onze klanten als onderdeel van de samenwerking, niet als nagedachte als de oorspronkelijke ontwikkelaar toevallig beschikbaar is.
Onnodige complexiteit en trage performance
Community-plugins zijn gebouwd voor veel verschillende organisaties met uiteenlopende wensen. Dat betekent: configuratie-opties die je nooit gebruikt, databasetabellen die data opslaan die je niet nodig hebt, en event-listeners die bij iedere paginaweergave vuren, ongeacht of die functie in jouw context relevant is.
We hebben Moodle-omgevingen geprofileerd waarbij één community-activiteitsmodule verantwoordelijk was voor dertig tot veertig procent van de totale databasequerytijd. De queries waren geoptimaliseerd voor flexibiliteit, niet voor het specifieke toegangspatroon van de organisatie.
Wanneer we een maatwerk-activiteitsmodule of rapportage-plugin bouwen, ontwerpen we het schema rondom de queries die het daadwerkelijk gaat uitvoeren. Indexen zitten waar ze gebruikt worden. Caching is geïmplementeerd voor de specifieke data die duur is om te berekenen. Het resultaat is niet alleen sneller, het is voorspelbaar snel, omdat de plugin niets doet wat hij niet hoeft te doen.
Beveiligingsrisico's die je niet kunt beheersen
Het Moodle-securityteam levert uitstekend werk, maar kan niet de volledige plugindirectory continu reviewen. Community-plugins variëren enorm in hun benadering van beveiliging: sommige volgen de richtlijnen van Moodle nauwgezet, andere zijn snel geschreven om een specifiek probleem op te lossen en sindsdien niet meer aangeraakt.
We hebben meer dan vijftig beveiligingsaudits uitgevoerd bij Nederlandse Moodle-omgevingen. De bevindingen vertonen een patroon. De Moodle-core is doorgaans in redelijke staat. De plugins zijn waar de kwetsbaarheden zich ophopen, SQL-injectie via ongesaniteerde parameters, cross-site scripting via ontbroken output-escaping, toegangscontroles die te omzeilen zijn met een aangepaste URL.
Met een maatwerk-plugin heb je controle over de codebase. We passen de volledige Moodle-beveiligings-API toe: required_capability(), $DB->get_records() met geparameteriseerde queries, clean_param() op elke externe invoer, en correcte sessiesleutelvalidatie bij formulierindieningen. En omdat wij het geschreven hebben, weten we precies waar we moeten kijken als een beveiligingsreview een punt van aandacht oplevert.
Het ondersteuningsgat
Als een community-plugin kapot gaat tijdens een Moodle-upgrade, zijn je opties beperkt. Je kunt wachten tot de beheerder een fix uitbrengt. Je kunt de plugin forken en zelf repareren, waarmee je de onderhoudsverantwoordelijkheid overneemt. Of je kunt een ontwikkelaar betalen, mogelijk ons, om zich in een codebase te verdiepen die we niet kennen.
Geen van deze opties is goed als de betreffende plugin kritieke functionaliteit afhandelt: cursusafrondingendie doorgaan naar HR-systemen, certificering voor gereguleerde sectoren, of de aangepaste inschrijfworkflow waar vijftienduizend gebruikers elke dag mee werken.
Hoe maatwerk-pluginontwikkeling er in de praktijk uitziet
Het bouwen van een maatwerk-Moodle-plugin is gestructureerder dan mensen soms verwachten. Het is niet een kwestie van wat PHP schrijven en in de pluginmap gooien. Moodle heeft een goed gedefinieerde plugin-API, en daar correct binnen werken is wat plugins onderhoudbaar, upgradeable en veilig maakt.
Fase 1: Requirements en architectuur
Elk pluginproject begint met een requirementssessie waarbij we doorvragen op vage specificaties. "We hebben een maatwerk-certificaatplugin nodig" is een startpunt, geen spec. We willen weten: welke data staat op het certificaat, wie kan het genereren, wat triggert de uitgifte, waar worden de PDF's opgeslagen, hoe worden certificaten geverifieerd, en wat gebeurt er als een cursus wordt aangepast nadat een certificaat is uitgegeven?
Deze gesprekken brengen beperkingen naar boven die de architectuur veranderen. Een certificaatplugin voor een zorgopleidingsaanbieder zoals Dentallect heeft andere eisen dan één voor een corporate trainingsplatform, gereguleerde beroepen kunnen vereisen dat certificaten verwijzen naar specifieke kwalificatiekaders, verifieerbare identificatoren bevatten, en blijven bestaan als de Moodle-installatie van de organisatie wordt vervangen.
De uitkomst van deze fase is een technische specificatie die het plugintype identificeert (activiteitsmodule, blok, lokale plugin, auth-plugin, format, rapport, beheertool), het databaseschema, de capability-definities en de integratiepunten met Moodle-core.
Fase 2: Pluginstructuur
Elke Moodle-plugin begint met een version.php die Moodle vertelt wie hem heeft gemaakt, welke versie het is, en welke Moodle-versie vereist is:
<?php
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'mod_klantactiviteit';
$plugin->version = 2026021500;
$plugin->requires = 2024042200; // Moodle 4.4
$plugin->maturity = MATURITY_STABLE;
$plugin->release = '1.2.0';Het versienummer volgt de datumconventie die Moodle gebruikt: JJJJMMDDXX. Elke keer dat we een update uitbrengen, wordt dit nummer verhoogd, zodat het upgradesysteem van Moodle weet dat er databasewijzigingen toegepast moeten worden.
Voor een activiteitsmodule definieert lib.php de verplichte functies die Moodle aanroept om de activiteit in de cursusflow te integreren:
<?php
function klantactiviteit_add_instance($moduleinstance, $mform = null): int {
global $DB;
$moduleinstance->timecreated = time();
$moduleinstance->timemodified = time();
return $DB->insert_record('klantactiviteit', $moduleinstance);
}
function klantactiviteit_supports(int $feature): ?bool {
return match ($feature) {
FEATURE_MOD_INTRO => true,
FEATURE_COMPLETION_TRACKS_VIEWS => true,
FEATURE_GRADE_HAS_GRADE => true,
FEATURE_BACKUP_MOODLE2 => true,
default => null,
};
}De functie klantactiviteit_supports() vertelt Moodle met welke platformfuncties deze activiteit samenwerkt, voltooiingsbeheer, beoordelingen, back-up en herstel. Dit goed doen is belangrijk: als je FEATURE_BACKUP_MOODLE2 claimt zonder de back-up-API correct te implementeren, leveren cursussen met jouw activiteit beschadigde back-upbestanden op.
Fase 3: Database en upgrades
Het bestand db/install.xml definieert het beginschema in het XMLDB-formaat van Moodle. Bij de eerste installatie maakt het upgradesysteem van Moodle deze tabellen automatisch aan. Als we een update uitbrengen die het schema wijzigt, voegen we een stap toe aan db/upgrade.php:
<?php
function xmldb_klantactiviteit_upgrade(int $oldversion): bool {
global $DB;
$dbman = $DB->get_manager();
if ($oldversion < 2026021500) {
$table = new xmldb_table('klantactiviteit');
$field = new xmldb_field('externreferentie', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'timemodified');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
upgrade_mod_savepoint(true, 2026021500, 'klantactiviteit');
}
return true;
}Zo overleven schemawijzigingen upgrades zonder dataverlies. De versiecontrole zorgt ervoor dat elke migratie slechts één keer wordt uitgevoerd, ook als een beheerder het upgradeproces meerdere keren uitvoert.
Fase 4: Testen
We testen plugins op meerdere niveaus, afhankelijk van de scope en het budget van het project. Soms vragen klanten om unit tests, waarbij PHP-klassen die de kernfunctionaliteit van de plugin implementeren worden getest met PHPUnit, met Moodle's testdatagenerator voor realistische testdata. In andere gevallen is het budget beperkt of is de plugin eenvoudig genoeg dat unit tests geen evenredige waarde toevoegen, en richten we de testinspanning op waar het het meest telt. Integratietests draaien op een echte Moodle-database om te verifiëren dat de plugin correct installeert, het databaseschema goed aangemaakt wordt, en kern-Moodle-API's op de verwachte manier met de plugin interacteren. En vóór elke plugin naar productie gaat, doen we handmatige acceptatietests op een stagingomgeving die de productieconfiguratie van de klant spiegelt.
We gebruiken ook de ingebouwde codecontrolestools van Moodle: phpcbf voor codestijl, phplint voor syntaxfouten, en de moodlecheck-plugin voor API-compliance. De plugindirectory vereist dat deze controles slagen, maar voor maatwerk-plugins passen we ze toe omdat ze echte problemen opsporen.
Dit alles is gekoppeld aan CI-pipelines. Elke commit triggert automatische controles: codestijl, linting en eventueel geconfigureerde tests draaien automatisch voordat code gemerged kan worden. Voor deployment gebruiken we CI/CD-pipelines die staging- en productiereleases afhandelen, zodat updates de juiste omgeving bereiken zonder handmatige tussenkomst of menselijke fouten.
De soorten plugins die wij bouwen
Activiteitsmodules
Het meest complexe plugintype. Activiteitsmodules verschijnen in de cursussectielijst naast de ingebouwde activiteiten van Moodle (Opdracht, Toets, Forum). Concrete voorbeelden uit ons portfolio:
- KLM Travel Journal voor cabinetraining, waarin bemanningsleden praktijkervaringen documenteren als gestructureerde leeractiviteiten
- Open Webinar voor live en opgenomen webinarsessies met aanwezigheidsregistratie en voltooiingsintegratie
- Aangepaste certificaten met automatische generatie op basis van cursusvoltooiing en configureerbare templates
- Zelfbeoordelingsmodules en 360-gradenfeedback tools voor professionele ontwikkelingsprogramma's
- Tijdregistratieactiviteiten voor het bijhouden van studieuren in gereguleerde trainingsomgevingen
- Canvas-gebaseerde interactieve activiteiten voor creatieve en visuele leeropdrachten
Authenticatie- en inschrijfplugins
Auth-plugins bepalen hoe gebruikers zich aanmelden. Inschrijfplugins bepalen hoe gebruikers in cursussen komen. Dit zijn de twee gebieden waar enterprise-systeemintegratie plaatsvindt.
We hebben tientallen authenticatie-integraties gebouwd voor organisaties als KLM, AFAS, SURFconext, Fontys en UWV. Deze variëren van SAML2- en OpenID Connect (OIDC)-implementaties tot volledig maatwerk SSO-oplossingen. Elke integratie handelt organisatiespecifieke attribuutmapping af: attributen van de identiteitsprovider worden omgezet naar Moodle-profielvelden, cohorten worden toegewezen of inschrijving in specifieke cursuscategorieën wordt getriggerd. Voor HR-systeemintegraties (SAP, AFAS, Workday) hebben we inschrijfsynchronisatieplugins gebouwd die op een schema een API bevragen, gebruikers aanmaken die nieuw zijn binnengekomen, gebruikers deactiveren die vertrokken zijn, en profieldata bijhouden. Daarnaast bouwen we inschrijfplugins voor betaalintegraties (Mollie, cursusbetalingsgateways) en vouchergebaseerde toegang.
Rapportages en analyses
De ingebouwde rapportage van Moodle is toereikend voor eenvoudige gevallen. Voor enterprise-klanten die vragen moeten beantwoorden als "welke van onze 8.000 medewerkers heeft dit kwartaal de verplichte veiligheidstraining afgerond, uitgesplitst naar afdeling en arbeidscontract", bereiken de ingebouwde tools snel hun grenzen.
We bouwen rapportage-plugins via de report-API van Moodle, die ze integreert in de navigatie op beheer- en cursusniveau. Voorbeelden zijn certificaatrapporten, coachview-voltooiingsdashboards, groepsvoortgangsoverzichten en SCORM-pogingsanalyses. Deze rapporten kunnen voltooiingsdata combineren met profielvelden, aangepaste gebruikersinformatievelden en cohortlidmaatschap om precies de overzichten te genereren die HR- en complianceteams nodig hebben.
Cursusformaten
Cursusformaten bepalen hoe de sectielijst van een cursus aan studenten wordt gepresenteerd. Naast de ingebouwde formaten Onderwerpen, Weken en Sociaal hebben we gebouwd:
- VSF (Vertical Section Format) voor overzichtelijke, scrollbare cursusindelingen
- Tabgebaseerde formaten zoals TabTiles voor VUmc, die secties organiseren in navigeerbare tabbladen
- Grid-formaten op maat gemaakt voor KLM, UWV en andere enterprise-klanten
- ANWB Streetwise-formaat voor verkeerseducatie op basisscholen
- Game-dashboardformaten met interactieve voortgangselementen
Blokken
Blokken zijn zijbalkcomponenten die aan elke pagina in Moodle kunnen worden toegevoegd. We gebruiken ze voor dashboardwidgets, meldingssystemen, sneltoegangsmenu's en het inbedden van inhoud uit externe systemen. Concrete voorbeelden zijn voucherbeheersblokken, hercertificeringstrackers, gebruikersavatar (webcam-snapshot) blokken, cursusnotitiewidgets, Streetwise-navigatie voor ANWB, en aangepaste dashboardblokken voor diverse enterprise-klanten. We hebben ook Commander gebouwd, een snelnavigatieblok waarmee beheerders en docenten direct pagina's kunnen vinden.
Thema's
Het thema-systeem van Moodle bepaalt de volledige presentatie van de front-end. Voor enterprise-klanten betekent dit doorgaans een maatwerk-thema gebouwd op het Boost-basisthema van Moodle, waarbij het designsysteem van de organisatie wordt geïmplementeerd, de navigatiestructuur wordt aangepast, en wordt gewaarborgd dat aan toegankelijkheidsvereisten wordt voldaan. We hebben thema's gebouwd voor Nedap, De Schoolschrijver, Vakmedia, Citaverde en anderen, elk passend bij strikte huisstijlrichtlijnen.
Beschikbaarheidscondities en lokale plugins
Naast de belangrijkste plugintypes bouwen we beschikbaarheidscondities die toegang tot activiteiten regelen op basis van criteria zoals IP-adresrestricties, vouchercodes of betaalstatus. Onze lokale plugins handelen alles af van GDPR-compliancetools en OneRoster-datasynchronisatie tot CSV-gebruikersimport en toetsmonitoring.
De kosten-batenanalyse: wanneer is maatwerk zinvol?
Community-plugins zijn zinvol wanneer de functionaliteit nauw aansluit bij wat je nodig hebt, wanneer ze actief worden onderhouden, en wanneer hun beveiligingsniveau acceptabel is voor jouw omgeving.
Maatwerkontwikkeling is zinvol wanneer:
- De community-plugin bestaat maar past niet. Als je een community-plugin significant moet aanpassen, heb je hem in feite geforkt, en nu ben je verantwoordelijk voor het onderhouden van een afwijkende codebase bij elke Moodle-upgrade. Het is vaak goedkoper om te bouwen wat je daadwerkelijk nodig hebt.
- De functionaliteit gaat over gevoelige data of toegangscontrole. Inschrijflogica, berekeningen van beoordelingen en certificaatuitgifte zijn gebieden waarbij een bug echte gevolgen heeft. Dit zijn niet de plekken om code te accepteren die je niet volledig kunt auditen.
- De plugin moet integreren met jouw systemen. Community-plugins zijn gebouwd voor een breed publiek. Een plugin die synchroniseert met jouw specifieke HR-systeem, gebruikmakend van jouw specifieke API-authenticatieschema en jouw specifieke datamodel, moet voor jouw situatie worden geschreven.
- Je opereert op schaal. Een plugin die acceptabel presteert voor 500 gebruikers, presteert mogelijk niet acceptabel voor 50.000. Maatwerk-plugins kunnen worden geoptimaliseerd voor jouw specifieke belastingsprofiel en toegangspatronen.
- Je hebt gegarandeerde ondersteuningscontinuïteit nodig. Voor functionaliteit die cruciaal is voor je bedrijfsvoering, heb je de zekerheid nodig dat iemand met grondige kennis van de code beschikbaar is als er iets misgaat.
Hoe beoordeel je of je een maatwerk-plugin nodig hebt?
Werk vóór het inschakelen van maatwerkontwikkeling deze vragen door:
Bestaat er een community-plugin die tachtig procent of meer van je eisen dekt zonder aanpassing?
Als ja, beoordeel dan of het resterende deel echt noodzakelijk is of dat je jouw proces kunt aanpassen.
Wordt de community-plugin actief onderhouden?
Controleer de datum van de laatste update, het aantal openstaande issues, en of de beheerder heeft gereageerd op recente bugrapporten. Een plugin die voor het laatst is bijgewerkt voor Moodle 3.9 is in de praktijk niet meer onderhouden.
Heb je de code van de community-plugin gelezen?
Voor elke plugin die authenticatie, inschrijving, beoordelingen of financiële transacties afhandelt, zou je de code moeten reviewen of iemand die review voor je moeten laten doen. De plugindirectory van Moodle garandeert geen beveiliging.
Wat zijn de gevolgen als de plugin uitvalt?
Als het antwoord is "we verliezen complianceregistraties" of "de inschrijving van tienduizend gebruikers loopt vast," verschuift de afweging naar maatwerkontwikkeling met gegarandeerd onderhoud.
Moet de plugin jouw organisatie meerdere Moodle-versies meegaan?
Langetermijneisen pleiten voor maatwerkontwikkeling met een onderhoudsovereenkomst.
Realistische verwachtingen
Maatwerk-pluginontwikkeling kost meer tijd en een hogere initiële investering dan het installeren van een community-plugin. Dat is een reëel verschil, en het telt voor organisaties met strakke deadlines of beperkte budgetten.
Wat je ervoor terugkrijgt is een plugin die precies doet wat je nodig hebt, goed presteert op jouw schaal, geaudit en onderhouden kan worden door mensen die de code kennen, en jouw Moodle-upgrade niet zal vertragen omdat de beheerder is verdwenen.
Voor de klanten van Ldesign Media, organisaties zoals KLM die training verzorgen voor duizenden medewerkers, of Yuverta dat studentvoortgang beheert over een netwerk van mbo-scholen, is die betrouwbaarheid het punt. Het trainingsplatform is infrastructuur, en infrastructuur moet betrouwbaar zijn.
De 300+ plugins die we hebben gebouwd sinds 2010 variëren van kleine hulpprogramma's die één specifiek probleem oplossen, tot complexe systemen met meerdere componenten die centraal staan in de volledige leeromgeving van een klant. De rode draad is niet complexiteit, het is dat de plugin in elk geval precies doet wat de organisatie werkelijk nodig heeft, en blijft werken als Moodle wordt bijgewerkt.



