# Moodle Development

Bei der Entwicklung von Code für Moodle sind die Ressourcen vom Moodle HQ sehr hilfreich, zuvorderst sind hier das ["Developer](https://moodledev.io/ "Developer Resource centre")  
[Resource centre"](https://moodledev.io/ "Developer Resource centre") und die ["Moodle Academy"](https://moodle.academy/ "Moodle Academy") (Developer learning pathway) zu nennen. Zudem bietet der [Community-Workshop „Moodle Plugin-Entwicklung“](https://kb.moodle-an-hochschulen.de/books/entwicklung/page/community-workshop-moodle-plugin-entwicklung "Community-Workshop „Moodle Plugin-Entwicklung“") einen guten Überblick.

## Getting Started

Als zentraler Einstieg mit Verweis auf viele weitere Themen, wie z.B. [Developer FAQ](https://moodledev.io/general/development/abc/faq) und [Moodle Debugging](https://docs.moodle.org/en/Debugging), eignet sich [Getting Started auf moodledev.io](https://moodledev.io/general/development/gettingstarted "Getting Started (moodledev.io)"). Empfohlen werden der [Moodle PHP CodeSniffer](https://moodledev.io/general/development/tools/phpcs) zur Prüfung auf die Coding-Standards und [PHPdoc check plugin](https://moodle.org/plugins/local_moodlecheck) zur Einhaltung der Dokumentationsstandards. Zudem wird zur Nutzung der Community ermutigt, z.B. das [Developer Forum](https://moodle.org/mod/forum/view.php?id=55 "developer forum") oder der [Matrix-Kanal](https://matrix.to/#/%23moodledev:moodle.com "Moodle Dev chat").

## Lokales Moodle

Entwickelter Code, wie z.B. Plugins, sollten auf einem lokalen Moodle-System getestet werden. Die einfachste Möglichkeit Moodle lokal aufzusetzen ist es, sich den [Windows Moodle Installer ](https://download.moodle.org/windows/ "Moodle packages for Windows")in der gewünschten Version herunterzuladen und dann "Start Moodle.exe" auszuführen. Im Anschluss ist ein betriebsbereites Moodle mit MySQL, PHP und den Moodle-files unter localhost bereitgestellt. Dies hat den Vorteil, dass der Code lokal per IDE bearbeitet werden kann und nicht auf einem Server liegt. Die PHP Error-logs liegen dann unter "server/apache/logs/error.log".

Die andere Möglichkeit ist es [Docker](https://hub.docker.com/u/moodlehq "Docker-Images") zu benutzen. Hierfür kann man sich auf [moodle-docker auf github](https://github.com/moodlehq/moodle-docker "Docker Containers for Moodle Developers") ein fertiges Set an Containern herunterladen. Darüber ist es auch möglich, mehrere verschiedene Moodle-Versionen parallel zu betreiben.  
Ein weiterer Vorteil: **Behat- und PHPUnit-Tests** kann man mit diesem Docker-Setup sehr einfach laufen lassen.  
Für Windows nutzt man aus Performancegründen am besten das [WSL (Windows Subsystem für Linux)](https://learn.microsoft.com/en-us/windows/wsl/install).

Die Academy bietet zudem einen Kurs ["Set up your Moodle Development Environment"](https://moodle.academy/enrol/index.php?id=29 "Academy-Kurs "Set up your Moodle Development Environment"").

## Plugin-Typen

Plugin-Entwicklung ist die gängigste Entwicklung in der Community. Hier ist zuerst zu entscheiden, was für ein Plugin entwickelt werden soll. Davon ist abhängig, wie das Plugin aufgebaut werden muss. Admin-Tools unterscheiden sich z.B. grundlegend von Blöcken oder Activity-Plugins. Eine Auflistung aller Plugin-Typen findet sich unter [Plugin types (auf moodledev.io)](https://moodledev.io/docs/apis/plugintypes "Plugin types (auf moodledev.io)"). Fast jedes Moodle-Plugin benutzt die gleichen, standardisierten Dateitypen und -namen. So wird z.B. in der "version.php" die aktuelle Version des Plugins gespeichert. Durch eine Erhöhung der Version kann zum Beispiel ein Datenbank-Update forciert werden.

Die Moodle Docs bieten auch eine Seite zum Start in die Moodle-Entwicklung: [https://docs.moodle.org/dev/Tutorial](https://docs.moodle.org/dev/Tutorial)

### Plugin-Entwicklungsprinzipien

- Moodle-APIs statt eigener Lösungen verwenden
- Capabilities definieren
- Sprachstrings nutzen
- Upgrade-Skripte verwenden

### Plugin-Struktur

Beispielhafter Aufbau eines Plugins, siehe auch [Kapitel 5 Community-Workshop "Moodle Plugin-Entwicklung"](https://kb.moodle-an-hochschulen.de/attachments/2 "Community-Workshop "Moodle Plugin-Entwicklung""):   
"Typische Dateien, die man in jedem Plugin findet, und ihr Zweck".

> local\_myplugin/  
> ├── version.php  
> ├── settings.php  
> ├── db/  
> │ ├── access.php  
> │ ├── install.xml  
> │ └── events.php  
> ├── classes/  
> ├── lang/  
> └── lib.php

### AG Plugins

Die AG Plugins ist ein guter Ansprechpartner und sollte bei Plugin-Entwicklungen konsultiert werden, vor allem hinsichtlich der Möglichkeiten der "<span class="author-a-0w5z66z8adz67zz87zz66z1lqcz67zf">Plugin-Werkstatt" (siehe auch [Vereins-Strategie, Kaptiel "2.2 Plug-ins"](https://moodle-an-hochschulen.de/wp-content/uploads/Vereinsstrategie-2026.pdf "Vereins-Strategie mit Kapitel 2.2 Plug-ins")). </span>

## Coding-Standards

Besonders für Beiträge zum Moodle-Core ist es wichtig, sich an die Coding styles zu halten. Aber auch für Plugin-Entwicklungen ist ein einheitlicher, gut lesbarer und wartbarer Code, der die Zusammenarbeit und Code-Reviews erleichtert, hilfreich. Grundlage bilden die PHP-Standards [PSR-1](https://www.php-fig.org/psr/psr-1/) und [PSR-12](https://www.php-fig.org/psr/psr-12/). Die Richtlinien behandeln Konsistenz, Lesbarkeit, Namenskonventionen, Sicherheit, Automatische Prüfung (Code checker).   
[Coding style (auf moodledev.io)](https://moodledev.io/general/development/policies/codingstyle "Coding style (auf moodledev.io)")

### Codesniffer

Der Codesniffer analysiert den PHP Code auf Probleme und Kompatibilität mit den Moodle Coding Guidelines.   
Manche Fehler kann er auch automatisch beheben.  
[https://moodledev.io/general/development/tools/phpcs](https://moodledev.io/general/development/tools/phpcs)

**phpcs mit Composer installieren**

<span class="prismjs _2rkoglpi _1dqoglyw _1wyb1crf _k48pi7a9 _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhktkvp _syaz1fxt _ect41odn _1ozdn7od _7xinn7od _t7aun7od _1v15u2gc _ilvcu2gc _m29uu2gc _17wyu2gc _pn28u2gc _gz9fu2gc _1hmyegat _vblregat _vbulegat _wozj1dyw _1agb1dyw _lkm81dyw _tv41hkgb _1hmimyb0 _1ra01n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _j1w0ww7y _13cdh2mm _15ba126e _zvy9f705 _qcxof705 _14y71a66 _j0l11wug _1weckb7n _1na21hna _1xx2grf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909m7j4 _scgayz1z _ipl81e17 _jeky1l04 _1gec1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _1552u2gc _12afu2gc _28ddu2gc _1i8zu2gc _12tu1a66 _zu0j1a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgcpf9b _bu7zpf9b _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3moej _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eiht5x2v _t9zb5x2v _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t361fxt _137bh55r _1k7d1fxt _97lipnps _12nh9lu1 _1g0517qg _i2ig10m5 _326z1fxt _113p131l _1n6tpnps _tgu817qg _1k47pnps _g0lx1fxt _ys4e131l _7gp8h55r _1yvq10m5 _1vww10m5 _1rju10m5 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o610m5 _kxov17qg _1np517qg _m2f517qg _1b9tpnps _1tq6pnps _1rd2pnps _1pbkpnps _k3lipnps _13zt131l _2g12fb2s _k86b10m5 _b5iy131l _gti3131l _1f0gpnps _9d3e17qg _qdiapnps _72uvpnps _13dgkb7n _1i3h1ule _1huou2gc _1a9lu2gc _20bqu2gc _1oggu2gc _bympu2gc _9nnju2gc" data-code-lang="shell" data-ds--code--code-block="" style="--ads-code-line-number-width: calc(1ch + 16px); --ads-highlighted-start-text: Highlight start; --ads-highlighted-end-text: Highlight end;">`<span class="token function">composer</span><span class=""> global config minimum-stability dev</span>`</span>

<span class="prismjs _2rkoglpi _1dqoglyw _1wyb1crf _k48pi7a9 _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhktkvp _syaz1fxt _ect41odn _1ozdn7od _7xinn7od _t7aun7od _1v15u2gc _ilvcu2gc _m29uu2gc _17wyu2gc _pn28u2gc _gz9fu2gc _1hmyegat _vblregat _vbulegat _wozj1dyw _1agb1dyw _lkm81dyw _tv41hkgb _1hmimyb0 _1ra01n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _j1w0ww7y _13cdh2mm _15ba126e _zvy9f705 _qcxof705 _14y71a66 _j0l11wug _1weckb7n _1na21hna _1xx2grf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909m7j4 _scgayz1z _ipl81e17 _jeky1l04 _1gec1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _1552u2gc _12afu2gc _28ddu2gc _1i8zu2gc _12tu1a66 _zu0j1a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgcpf9b _bu7zpf9b _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3moej _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eiht5x2v _t9zb5x2v _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t361fxt _137bh55r _1k7d1fxt _97lipnps _12nh9lu1 _1g0517qg _i2ig10m5 _326z1fxt _113p131l _1n6tpnps _tgu817qg _1k47pnps _g0lx1fxt _ys4e131l _7gp8h55r _1yvq10m5 _1vww10m5 _1rju10m5 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o610m5 _kxov17qg _1np517qg _m2f517qg _1b9tpnps _1tq6pnps _1rd2pnps _1pbkpnps _k3lipnps _13zt131l _2g12fb2s _k86b10m5 _b5iy131l _gti3131l _1f0gpnps _9d3e17qg _qdiapnps _72uvpnps _13dgkb7n _1i3h1ule _1huou2gc _1a9lu2gc _20bqu2gc _1oggu2gc _bympu2gc _9nnju2gc" data-code-lang="shell" data-ds--code--code-block="" style="--ads-code-line-number-width: calc(1ch + 16px); --ads-highlighted-start-text: Highlight start; --ads-highlighted-end-text: Highlight end;">`<span class="token function">composer</span><span class=""> global require moodlehq/moodle-cs</span>`</span>

**phpcs für ein Plugin laufen lassen**

<span class="prismjs _2rkoglpi _1dqoglyw _1wyb1crf _k48pi7a9 _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhktkvp _syaz1fxt _ect41odn _1ozdn7od _7xinn7od _t7aun7od _1v15u2gc _ilvcu2gc _m29uu2gc _17wyu2gc _pn28u2gc _gz9fu2gc _1hmyegat _vblregat _vbulegat _wozj1dyw _1agb1dyw _lkm81dyw _tv41hkgb _1hmimyb0 _1ra01n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _j1w0ww7y _13cdh2mm _15ba126e _zvy9f705 _qcxof705 _14y71a66 _j0l11wug _1weckb7n _1na21hna _1xx2grf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909m7j4 _scgayz1z _ipl81e17 _jeky1l04 _1gec1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _1552u2gc _12afu2gc _28ddu2gc _1i8zu2gc _12tu1a66 _zu0j1a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgcpf9b _bu7zpf9b _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3moej _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eiht5x2v _t9zb5x2v _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t361fxt _137bh55r _1k7d1fxt _97lipnps _12nh9lu1 _1g0517qg _i2ig10m5 _326z1fxt _113p131l _1n6tpnps _tgu817qg _1k47pnps _g0lx1fxt _ys4e131l _7gp8h55r _1yvq10m5 _1vww10m5 _1rju10m5 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o610m5 _kxov17qg _1np517qg _m2f517qg _1b9tpnps _1tq6pnps _1rd2pnps _1pbkpnps _k3lipnps _13zt131l _2g12fb2s _k86b10m5 _b5iy131l _gti3131l _1f0gpnps _9d3e17qg _qdiapnps _72uvpnps _13dgkb7n _1i3h1ule _1huou2gc _1a9lu2gc _20bqu2gc _1oggu2gc _bympu2gc _9nnju2gc" data-code-lang="shell" data-ds--code--code-block="" style="--ads-code-line-number-width: calc(1ch + 16px); --ads-highlighted-start-text: Highlight start; --ads-highlighted-end-text: Highlight end;">`<span class="">~/.composer/vendor/bin/phpcs /path/to/moodle/mod/foobar/</span>`</span>

**<span class="prismjs _2rkoglpi _1dqoglyw _1wyb1crf _k48pi7a9 _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhktkvp _syaz1fxt _ect41odn _1ozdn7od _7xinn7od _t7aun7od _1v15u2gc _ilvcu2gc _m29uu2gc _17wyu2gc _pn28u2gc _gz9fu2gc _1hmyegat _vblregat _vbulegat _wozj1dyw _1agb1dyw _lkm81dyw _tv41hkgb _1hmimyb0 _1ra01n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _j1w0ww7y _13cdh2mm _15ba126e _zvy9f705 _qcxof705 _14y71a66 _j0l11wug _1weckb7n _1na21hna _1xx2grf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909m7j4 _scgayz1z _ipl81e17 _jeky1l04 _1gec1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _1552u2gc _12afu2gc _28ddu2gc _1i8zu2gc _12tu1a66 _zu0j1a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgcpf9b _bu7zpf9b _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3moej _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eiht5x2v _t9zb5x2v _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t361fxt _137bh55r _1k7d1fxt _97lipnps _12nh9lu1 _1g0517qg _i2ig10m5 _326z1fxt _113p131l _1n6tpnps _tgu817qg _1k47pnps _g0lx1fxt _ys4e131l _7gp8h55r _1yvq10m5 _1vww10m5 _1rju10m5 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o610m5 _kxov17qg _1np517qg _m2f517qg _1b9tpnps _1tq6pnps _1rd2pnps _1pbkpnps _k3lipnps _13zt131l _2g12fb2s _k86b10m5 _b5iy131l _gti3131l _1f0gpnps _9d3e17qg _qdiapnps _72uvpnps _13dgkb7n _1i3h1ule _1huou2gc _1a9lu2gc _20bqu2gc _1oggu2gc _bympu2gc _9nnju2gc" data-code-lang="shell" data-ds--code--code-block="" style="--ads-code-line-number-width: calc(1ch + 16px); --ads-highlighted-start-text: Highlight start; --ads-highlighted-end-text: Highlight end;">Auto-fix durchführen</span>**

<span class="prismjs _2rkoglpi _1dqoglyw _1wyb1crf _k48pi7a9 _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhktkvp _syaz1fxt _ect41odn _1ozdn7od _7xinn7od _t7aun7od _1v15u2gc _ilvcu2gc _m29uu2gc _17wyu2gc _pn28u2gc _gz9fu2gc _1hmyegat _vblregat _vbulegat _wozj1dyw _1agb1dyw _lkm81dyw _tv41hkgb _1hmimyb0 _1ra01n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _j1w0ww7y _13cdh2mm _15ba126e _zvy9f705 _qcxof705 _14y71a66 _j0l11wug _1weckb7n _1na21hna _1xx2grf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909m7j4 _scgayz1z _ipl81e17 _jeky1l04 _1gec1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _1552u2gc _12afu2gc _28ddu2gc _1i8zu2gc _12tu1a66 _zu0j1a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgcpf9b _bu7zpf9b _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3moej _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eiht5x2v _t9zb5x2v _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t361fxt _137bh55r _1k7d1fxt _97lipnps _12nh9lu1 _1g0517qg _i2ig10m5 _326z1fxt _113p131l _1n6tpnps _tgu817qg _1k47pnps _g0lx1fxt _ys4e131l _7gp8h55r _1yvq10m5 _1vww10m5 _1rju10m5 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o610m5 _kxov17qg _1np517qg _m2f517qg _1b9tpnps _1tq6pnps _1rd2pnps _1pbkpnps _k3lipnps _13zt131l _2g12fb2s _k86b10m5 _b5iy131l _gti3131l _1f0gpnps _9d3e17qg _qdiapnps _72uvpnps _13dgkb7n _1i3h1ule _1huou2gc _1a9lu2gc _20bqu2gc _1oggu2gc _bympu2gc _9nnju2gc" data-code-lang="shell" data-ds--code--code-block="" style="--ads-code-line-number-width: calc(1ch + 16px); --ads-highlighted-start-text: Highlight start; --ads-highlighted-end-text: Highlight end;">`~/.composer/vendor/bin/phpcbf /path/to/moodle/mod/foobar/`</span>

### ESLint

ESLint ist das Äquivalent für Javascript zu Codesniffer für PHP.

[https://moodledev.io/general/development/tools/nodejs#running-grunt](https://moodledev.io/general/development/tools/nodejs#running-grunt)

[https://docs.moodle.org/dev/Linting#Javascript\_(ESLint)](https://docs.moodle.org/dev/Linting#Javascript_(ESLint))

**ESLint für in einem Plugin Verzeichnis laufen lassen**

<span class="prismjs _2rkoglpi _1dqoglyw _1wyb1crf _k48pi7a9 _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhktkvp _syaz1fxt _ect41odn _1ozdn7od _7xinn7od _t7aun7od _1v15u2gc _ilvcu2gc _m29uu2gc _17wyu2gc _pn28u2gc _gz9fu2gc _1hmyegat _vblregat _vbulegat _wozj1dyw _1agb1dyw _lkm81dyw _tv41hkgb _1hmimyb0 _1ra01n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _j1w0ww7y _13cdh2mm _15ba126e _zvy9f705 _qcxof705 _14y71a66 _j0l11wug _1weckb7n _1na21hna _1xx2grf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909m7j4 _scgayz1z _ipl81e17 _jeky1l04 _1gec1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _1552u2gc _12afu2gc _28ddu2gc _1i8zu2gc _12tu1a66 _zu0j1a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgcpf9b _bu7zpf9b _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3moej _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eiht5x2v _t9zb5x2v _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t361fxt _137bh55r _1k7d1fxt _97lipnps _12nh9lu1 _1g0517qg _i2ig10m5 _326z1fxt _113p131l _1n6tpnps _tgu817qg _1k47pnps _g0lx1fxt _ys4e131l _7gp8h55r _1yvq10m5 _1vww10m5 _1rju10m5 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o610m5 _kxov17qg _1np517qg _m2f517qg _1b9tpnps _1tq6pnps _1rd2pnps _1pbkpnps _k3lipnps _13zt131l _2g12fb2s _k86b10m5 _b5iy131l _gti3131l _1f0gpnps _9d3e17qg _qdiapnps _72uvpnps _13dgkb7n _1i3h1ule _1huou2gc _1a9lu2gc _20bqu2gc _1oggu2gc _bympu2gc _9nnju2gc" data-code-lang="shell" data-ds--code--code-block="" style="--ads-code-line-number-width: calc(1ch + 16px); --ads-highlighted-start-text: Highlight start; --ads-highlighted-end-text: Highlight end;">`<span class="">grunt eslint --show-lint-warnings</span>`</span>

<span class="prismjs _2rkoglpi _1dqoglyw _1wyb1crf _k48pi7a9 _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhktkvp _syaz1fxt _ect41odn _1ozdn7od _7xinn7od _t7aun7od _1v15u2gc _ilvcu2gc _m29uu2gc _17wyu2gc _pn28u2gc _gz9fu2gc _1hmyegat _vblregat _vbulegat _wozj1dyw _1agb1dyw _lkm81dyw _tv41hkgb _1hmimyb0 _1ra01n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _j1w0ww7y _13cdh2mm _15ba126e _zvy9f705 _qcxof705 _14y71a66 _j0l11wug _1weckb7n _1na21hna _1xx2grf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909m7j4 _scgayz1z _ipl81e17 _jeky1l04 _1gec1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _1552u2gc _12afu2gc _28ddu2gc _1i8zu2gc _12tu1a66 _zu0j1a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgcpf9b _bu7zpf9b _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3moej _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eiht5x2v _t9zb5x2v _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t361fxt _137bh55r _1k7d1fxt _97lipnps _12nh9lu1 _1g0517qg _i2ig10m5 _326z1fxt _113p131l _1n6tpnps _tgu817qg _1k47pnps _g0lx1fxt _ys4e131l _7gp8h55r _1yvq10m5 _1vww10m5 _1rju10m5 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o610m5 _kxov17qg _1np517qg _m2f517qg _1b9tpnps _1tq6pnps _1rd2pnps _1pbkpnps _k3lipnps _13zt131l _2g12fb2s _k86b10m5 _b5iy131l _gti3131l _1f0gpnps _9d3e17qg _qdiapnps _72uvpnps _13dgkb7n _1i3h1ule _1huou2gc _1a9lu2gc _20bqu2gc _1oggu2gc _bympu2gc _9nnju2gc" data-code-lang="shell" data-ds--code--code-block="" style="--ads-code-line-number-width: calc(1ch + 16px); --ads-highlighted-start-text: Highlight start; --ads-highlighted-end-text: Highlight end;">**Auto-fix durchführen**</span>

<span class="prismjs _2rkoglpi _1dqoglyw _1wyb1crf _k48pi7a9 _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhktkvp _syaz1fxt _ect41odn _1ozdn7od _7xinn7od _t7aun7od _1v15u2gc _ilvcu2gc _m29uu2gc _17wyu2gc _pn28u2gc _gz9fu2gc _1hmyegat _vblregat _vbulegat _wozj1dyw _1agb1dyw _lkm81dyw _tv41hkgb _1hmimyb0 _1ra01n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _j1w0ww7y _13cdh2mm _15ba126e _zvy9f705 _qcxof705 _14y71a66 _j0l11wug _1weckb7n _1na21hna _1xx2grf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909m7j4 _scgayz1z _ipl81e17 _jeky1l04 _1gec1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _1552u2gc _12afu2gc _28ddu2gc _1i8zu2gc _12tu1a66 _zu0j1a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgcpf9b _bu7zpf9b _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3moej _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eiht5x2v _t9zb5x2v _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t361fxt _137bh55r _1k7d1fxt _97lipnps _12nh9lu1 _1g0517qg _i2ig10m5 _326z1fxt _113p131l _1n6tpnps _tgu817qg _1k47pnps _g0lx1fxt _ys4e131l _7gp8h55r _1yvq10m5 _1vww10m5 _1rju10m5 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o610m5 _kxov17qg _1np517qg _m2f517qg _1b9tpnps _1tq6pnps _1rd2pnps _1pbkpnps _k3lipnps _13zt131l _2g12fb2s _k86b10m5 _b5iy131l _gti3131l _1f0gpnps _9d3e17qg _qdiapnps _72uvpnps _13dgkb7n _1i3h1ule _1huou2gc _1a9lu2gc _20bqu2gc _1oggu2gc _bympu2gc _9nnju2gc" data-code-lang="shell" data-ds--code--code-block="" style="--ads-code-line-number-width: calc(1ch + 16px); --ads-highlighted-start-text: Highlight start; --ads-highlighted-end-text: Highlight end;">`grunt eslint --fix`</span>

## API Guide

Moodle bietet viele Programmierschnittstellen (APIs). Diese können bei Entwicklungen z.B. für Standardaufgaben genutzt werden und sind nachhaltiger und kompatibler als eigenen Code zu entwickeln. Häufig verwendete APIs sind die **Access API** (Rechte und Rollen), **Data Manipulation API (DML)** (Datenbankzugriffe), **File API** (Dateiverwaltung), **Form API** (Formulare), **Events API** (Ereignisse) und **External Functions API** (Webservices). Die Nutzung von Events, Hooks und weiteren Core-APIs verbessert die Wartbarkeit.   
[API Guides (auf moodledev.io)](https://moodledev.io/docs/apis "API Guides (auf moodledev.io)")

## Moodle Database Schema

Einen Überblick über das Moodle Database Schema inklusive der Beziehungen zwischen den Tabellen bietet   
[https://www.examulator.com/er/output/index.html.](https://www.examulator.com/er/output/index.html)

## Security

Durch Implementierung von Schutzmaßnahmen sollten Benutzer:innen, Daten und das Moodle-System vor unbefugtem Zugriff, Datenverlust und Angriffen geschützt werden. Zentrale Elemente hierbei sind Berechtigungen, Prüfung von Nutzereingaben, SQL-Injection verhindern durch Nutzung der Moodle Data Manipulation API (DML), Cross-Site-Scripting (XSS) vermeiden, Ändernde Aktionen absichern, Sensible Informationen schützen, Risiken von Capabilities dokumentieren. Sicherheit sollte fester Bestandteil des Entwicklungsprozesses sein.   
[Security (auf moodledev.io)](https://moodledev.io/general/development/policies/security "Security (auf moodledev.io)")

## Testing

Durch manuelle und automatische Tests sollen frühzeitig Fehler erkannt und die Stabilität über mehrere Versionen hinweg gesichert werden. Bereits während der Entwicklung sollten Tests geschrieben und regelmäßig ausgeführt werden. Moodle unterstützt u. a. [**PHPUnit** für Unit-Tests](https://moodledev.io/docs/5.3/guides/testing) und [**Behat** für Akzeptanztests](https://moodledev.io/general/development/tools/behat "Behat") der Benutzeroberfläche.

### Continuous Integration (CI)

[Moodle Plugin CI](https://moodlehq.github.io/moodle-plugin-ci/ "Moodle Plugin CI") ist das von Moodle empfohlene Werkzeug, um Plugins automatisiert zu prüfen. Es bündelt die wichtigsten Tests und Qualitätsprüfungen und lässt sich in CI-Systeme wie **GitHub Actions** integrieren. Dadurch werden Änderungen bei jedem Commit oder Pull Request automatisch validiert und Fehler frühzeitig erkannt. U.a. **PHPUnit**, **Behat**, **Moodle Code Checker**, **PHPDoc Check**, **Mustache Linting** und **PHP Linting** können automatisiert ausgeführt werden.

## Moodle an Hochschulen Plugin Guidelines

Maintaining: [https://github.com/moodle-an-hochschulen/moodle-plugin-maintaining/wiki](https://github.com/moodle-an-hochschulen/moodle-plugin-maintaining/wiki)  
Readme-Vorlage: [https://github.com/moodle-an-hochschulen/moodle-readmetemplate](https://github.com/moodle-an-hochschulen/moodle-readmetemplate)

---

Autor: [Klaus Steitz](https://forum.moodle-an-hochschulen.de/user/view.php?id=44 "Profil von Klaus Steitz"), Technische Universität Darmstadt