Moodle versions in git

All versions of Moodle are appropriately tagged in the Moodle git repository (git:// so you can use the following to show the commits that relate to each version. Just a note too that major versions are branched using the convention MOODLE_XX_STABLE where XX is the version e.g. MOODLE_36_STABLE for Moodle 3.6.

git show-ref --tags

This gives you something like this:

30e069a061296bef321b31b791a4366a953cae23 refs/tags/v3.6.1
008baea92aa20ee7ff619d37cc6568b076cda00c refs/tags/v3.6.2
ec902921430745139548095e45380ed017acd770 refs/tags/v3.6.3

Here you can see that the v3.6.1 tag has the commit hash 30e069a061296bef321b31b791a4366a953cae23 (the tag is just an alias for that hash).

Use the version.php file to find out the details of that version using the tag as a shortcut e.g.

git show v3.6.1:version.php

Sometimes you want to know what files changed between versions, you can use git diff with the --stat option for that:

git diff v3.6.2 v3.6.3 --stat

If you use a diff/merge tool (e.g. Araxis Merge) you can use the --dir-diff to get a full directory comparison of changes between tags as well. Very handy.

git difftool --dir-diff v3.6.1 v3.6.2

One other useful thing is to look at the log between two tags like so:

git log --oneline v3.6.1..v3.6.2

Handy to see what Moodle fixes were added between tags (MDL-) which you can check against the Moodle Tracker


Query the grade scale value for a grade (MySQL)

Grade scales are stored in the mdl_scale table, however they are stored as a comma-separated list in the scale column, which makes them quite difficult to query directly from the database. Because the grade is just a number (e.g. 1, 2, 3), it is far more useful to show the corresponding grade scale grade description.

For example, the grade scale might have the following value stored in the scale column (formatted for readability with index):

(1) Mostly separate knowing,
(2) Separate and connected,
(3) Mostly connected knowing

So a final grade of 2 would translate to “Separate and connected” if using this grade scale.

You can use the substring_index function in MySQL to perform a look up of the corresponding grade scale value (grade description) using the grade value.

Here’s the SQL:

scale, trim(substring_index(substring_index(scale, ',', 2), ',', -1))
from mdl_scale where id = 1;

Simply, replace the index in the inner substring_index call with the appropriate grade value (e.g. 1, 2, or 3) for this scale and it will give you corresponding grade scale grade description.


Plugin Dependencies

Plugin dependencies can be added using the $plugin->dependencies attribute in version.php.

There are quite a few examples in the code base, for example:

filter_data depends on mod_data so the following appears in the filter/data/version.php:

$plugin->dependencies = array('mod_data' => 2014050800);

Move download button and select below table

If you are working with flexibile tables (lib/tablelib.php) in Moodle and have table download enabled, you can move the “Download table data as” select and button below the table by adding the following line before finishing your table setup:


By default they appear above the table but this doesn’t always look so good and takes up screen space.


Display SQL generated by $DB methods

If you need to debug the SQL generated by any of the $DB methods (DML API), you can use the following around the statement(s) to display the SQL:




Getting the Moodle base / root directory

The moodle root directory $CFG->dirroot is established in the file lib/setup.php along with other values in the global $CFG object which combines configuraton from config.php as well as the entries in the database table mdl_config.

The value is set as follows:

$CFG->dirroot = dirname(dirname(__FILE__));

And you can use it reliably establish the base/root moodle directory absolute path and use it for things like file requires in your plugin.

Some other useful entries in lib/setup.php from the code:

$CFG->wwwroot  - Path to moodle index directory in url format.
$CFG->dataroot - Path to moodle data files directory on server's filesystem.
$CFG->dirroot  - Path to moodle's library folder on server's filesystem.
$CFG->libdir   - Path to moodle's library folder on server's filesystem.
$CFG->tempdir  - Path to moodle's temp file directory on server's filesystem.
$CFG->cachedir - Path to moodle's cache directory on server's filesystem (shared by cluster nodes).
$CFG->localcachedir - Path to moodle's local cache directory (not shared by cluster nodes).

Lots more in there have a dig around in lib/setup.php.


Make a select form field required

When adding a form rule for select (or multiple select) fields, these need to be applied on the client side (not server side). Here’s an example of how to add a rule for a select field:

            get_string("selectfieldrequiredmessage", "local_yourplugin"), 

The key is the 5th parameter above which is set to client instead of the default of server.

For more help, see the Moodle Forms library developer documentation.


Can’t create a new XMLDB file

The Moodle XMLDB plugin does some checks of the db/ folder in your plugin to ensure it has read/write access before allowing certain permissions (e.g. create and load). If it doesn’t find this, the text will appear but it won’t be a hyperlink.

Have a look in admin/tool/xmldb/actions/main_view/main_view.class.php

For example for the create button it will have:

 // The create button
                if ($dbdir->path_exists &&
                    !file_exists($key . '/install.xml') &&
                    is_writeable($key)) {                                       

So the db directory needs to exist and be writable by the user running the Moodle instance (e.g. if using apache the user that apache runs as).


Setting up a Moodle Development Environment with Cloud9 IDE

Cloud9 is a cloud based development environment. Behind the scenes it uses Docker containers to provide an awesome IDE together with a Workspace using Ubuntu. What this means it that you can you set up a fully integrated Moodle development environment on Cloud9!

This tutorial uses Moodle 3.1 for because as of May 2017 Cloud9 ships by default with PHP 5.5.9 which doesn’t meet the minimum requirements for Moodle 3.2 ( You can work around this but it is more involved so for now, lets stick with Moodle 3.1.

To start with, signup for a new (free account). Note while the account is free, it will require you to enter a credit card just to verify your identity.

Once you have an account and have logged in, add a new Workspace.

  • Enter your workspace name (e.g. Moodle)
  • A description e.g. Moodle 3.1 Development Environment
  • Choose Private (you get one free private workspace) or public
  • Choose a template and select PHP, APache & MySQL
  • Create your workspace

Note most of the steps that follow are just regular linux commands and set up instructions for installing Moodle.
Nothing particularly specific to Cloud9, but there are a few instances where the commands vary slighly e.g. for accessing the MySQL CLI.

Cloning Moodle via Git

Once you see your IDE, go to the terminal at the bottom. The first step is to clone moodle via git.

You should be in ~/workspace (if not cd ~/workspace).

git clone git:// moodle

This will clone it to ~/workspace/moodle

Once finished, checkout the MOODLE_31_STABLE branch

git checkout MOODLE_31_STABLE

Now we want to move everything in moodle to the ~/workspace folder as that is the base directory used when you choose Run Project.

cd ~/workspace/moodle
mv * ../
mv .* ../
cd ..
rmdir moodle

Note you will see the following message which can be ignored. Its just to get all the .* files into the workspace folder.

mv: cannot move ‘.’ to ‘../.’: Device or resource busy
mv: cannot move ‘..’ to ‘../..’: Device or resource busy

Now you have everything that was cloned in~/workspace/moodle in the ~/workspace folder and it should show you are in the MOODLE_31_STABLE branch

<user>~/workspace (MOODLE_31_STABLE) $ 

Setting up the Database

Next we need to set up a MySQL database. In the terminal type:

mysql-ctl cli

Note you may need to type mysql-ctl install if it is not already installed

Create your moodle database, user and give the user full access to the database.

mysql> create database moodle character set utf8 collate utf8_general_ci;
mysql> create user moodle@localhost identified by 'moodle';
mysql> grant all on moodle.* to moodle@localhost;

mysql> exit

Setting up the data directory

We’ll put this under ~/moodledata
Create this directory:

mkdir ~/moodledata

( Note you could do this via the file manager on the left hand side of the IDE if you click on the small cog in the left pane and choose “Show Home in Favourites”. )

Install Moodle

Ok, you are ready to hit “Run Project” to fire up Apache & PHP.
This will open in a new tab and give you a URL to where it is serving e.g.

Go ahead and visit that page or click on the link and choose open. Hopefully you see the Moodle installation screen. Note on the first few screens before the license the stylesheet/layout won’t look great, but it will sort itself out later on.

Follow the prompts:

  • Pick your language
  • Your web address, Moodle directory (workspace) and Data directory (moodledata) should all default
  • Database drive is MySQL (native/mysqli)
  • Database host is localhost (default) name is moodle (default) enter user as moodle and password as moodle and choose next (prefix/port/socket all stay at defaults).
  • You should see the installation screen and license, choose continue.
  • All server checks should pass, so choose continue
  • The installation will take a while, let it run through. When finished you will see a continue button on the bottom of the page (may need to scroll to the end).
  • When finished you should see a screen where you can set up your admin user account. Make sure you enter a valid email here.
  • Finally you can enter your front page settings; the full site name e.g. Moodle 3.1 Development Environment and short name e.g. moodle31dev. Update your timezone and save.

That’s it, you have a working Moodle 3.1.x environment that you can now development against with the Cloud 9 IDE!


Unknown Error Upgrading Plugin to Version

If you are getting an error like this when attempting to upgrade a plugin:

!!! Unknown error upgrading <pluginname> to version 2017042400, can not continue. !!!
Error code: upgradeerror !!
!! Stack trace: * line 340 of /lib/upgradelib.php: upgrade_exception thrown
* line 549 of /lib/upgradelib.php: call to upgrade_plugin_savepoint()
* line 1630 of /lib/upgradelib.php: call to upgrade_plugins()
* line 171 of /admin/cli/upgrade.php: call to upgrade_noncore()

Then it might be simply due to a missing line in your db/upgrade.php file.

Check that in your function xmldb_<type>_<pluginname>_upgrade($oldversion) function you have a final line that returns a true value.
That is: return true at the end of the function.


XMLDB Editor and Reserved Names

One (of the many) good reasons to use the XMLDB editor in Moodle rather than hacking the db/install.xml file directly is that it warns you if you use a reserved name. For example:

XMLDB Reserved name error

The red Reserved text after [XML] in the screenshot is indicating that the word “column” is reserved and shouldn’t be used. It would be nice if it stopped you from saving such a change (it doesn’t) but at least it indicates a problem.

The Reserved link points to the following location if you ever want to look up the currently used reserved words:


This link also shows you potential existing problems in your XMLDB that may need to be fixed.


Set User IDs that will always see debug messages

There’s a handy feature in Moodle configuration called $CFG->debugusers = '<moodle_user_ids>' that allows you to set certain user IDs to always see debug messages regardless fo the Site administration > Development > Debugging settings for the site. This can be a comma separated list of Moodle User Ids (from the user table). Set this in the config.php or see config-dist.php for more details.

Useful if you need to troubleshoot an error but don’t want to increase debug level for the entire system and catch messages for the other users in the system too.


Course file system space usage query

The following query provides a summary of the file space usage (in bytes) and number of files used by each course in the system from the mdl_files meta table for the data directory. This is just for the space used in the data directory (not the database).

    c.fullname as course_full_name,
    c.shortname as course_short_name,
    sum(f.filesize) as size_in_bytes,
    sum(case when (f.filesize > 0) then 1 else 0 end) as number_of_files
    mdl_files f inner join mdl_context x
    on f.contextid =
    and x.contextlevel = 50
    inner join mdl_course c
    on = x.instanceid
group by 
    f.contextid, x.instanceid
order by 
    sum(filesize) desc

It may need some refinement. Context level 50 refers to courses.


Overriding PHP Settings

There are a few ways to override global PHP (php.ini) settings with local settings. For example you can use a .htaccess file or local php.ini file depending on what options exist for your site hosting.

Another way to do it, is to override the settings in config.php. This has the benefit of being part of the standard moodle code configuration setup. All you need to do is use the ini_set() function. For example, to increase the max_execution_time from 30s to 60s you can do this in config.php:

ini_set('max_execution_time', 60);

The best place for a change like this is towards the end of the config.php, and perhaps wrapped in comments indicating custom configuration.


Getting PHP Info for your Moodle Site

There is a handy link under Site adminstration > Server > PHP Info ( or browse to ) that will return the standard PHP Info phpinfo() results for your server.

Very handy for checking things like installed modules and core parameters if you are overriding them ( e.g. error_log, max_execution_time, post_max_size, upload_max_size etc )


Moodle Debug Log Level Values

These are the values you can set for the debug level under Site administration > Development > Debugging for Debug message (debug).
You can also query this from the mdl_config table looking at the key debug.

  • 0 = NONE: Do not show any errors or warnings
  • 5 = MINIMAL: Show only fatal errors
  • 15 = NORMAL: Show errors, warnings and notices
  • 30719 = ALL: Show all reasonable PHP debug messages
  • 32767 = DEVELOPER: extra Moodle debug messages for developers

Those are values you can set in the database for the debug entry in mdl_config if you need to check or change this on the database. Good idea to purge the cache when you do too.

These are defined with PHP error constants in lib/setuplib.php as follows (note this might change depending on versions):


Extended characters in moodle usernames

If you need to use extended characters in usernames, there is a setting under Site Administration > Security > Site Policies called Allow extended characters in usernames extendedusernamechars.

By default moodle usernames must be lowercase and are restricted to alphanumeric characters, the underscore, hypen, period and @ symbol.

You don’t need this if you are using email addresses as usernames (as the @ symbol is allowed) but you might run into a few other edge cases with email addresses:

  • Emails that are mixed case e.g. need to be lowercased to
  • Emails can have other characters such as an apostrophe (e.g. jack.o’ and if you want to preserve such characters you’ll need to turn this setting on. Alternatively you can remove such characters from the username.
  • Emails might have extended language characters in them which required this setting to be turned on as well if you want to match the username to the email addresse exactly.

Also worth noting is that the lib/classes/user.php definition uses PARAM_USERNAME (not PARAM_RAW) anymore in recent versions of moodle. This is used for things like the core user web services. So you will hit invalid parameter exceptions if you specify a username in say mixed case format or with extended characters (and the extendedusernamechars setting is off). This can be confusing it seems like you have specified the wrong parameters but what’s happening is that your parameter does not follow the definition of PARAM_USERNAME. To see the code used to test PARAM_USERNAME have a look in lib/moodlelib.php -> clean_param()


Verify your Moodle Database Schema against XMLDB

There is a very handy CLI tool admin/cli/check_database_schema.php which compares the structure of your Moodle database against the XMLDB metadata and looks for any issues.

These might includes issues such as:

  • Missing tables – they exist in XMLDB but not in your DB (not good!)
  • Unexpected tables – tables in your database not defined in XMLDB (e.g. created outside of Moodle)
  • Mismatches between table column definitions in the database and XMLDB

Some things may be acceptable to ignore, but it pays to do a check, particularly after an upgrade to make sure the upgrade process worked correctly and in particular you aren’t missing any core or plugin database tables that are defined in XMLDB, as these will pop up as database errors in your application sooner or later (e.g. “Error reading from database”).

If you need to build any missing core tables, remember they are defined under Developer > XMLDB Editor. From here find lib/db (core database tables) and choose Load, followed by Edit. Find the table you need and choose Edit. You can then use View SQL code to get the relevant SQL commands to create the table if it is missing from your database.

Also as a developer, make sure you are correctly utilising XMLDB for defining your plugin’s database tables. Taking shortcuts such as directly creating tables in the database without defining them in XMLDB will mean that they will pop up as unexpected when checking the database schema.


Web service Access control exception

If you are creating or using a web service in Moodle and see the following message:

    "exception": "webservice_access_exception",
    "errorcode": "accessexception",
    "message": "Access control exception"

It can be a bit hard to troubleshoot. Generally there is a 4th item with debugging info, but this will only appear if you have the correct Moodle debugging level set in Site administration > Development > Debugging.

At minimum you’ll need to set Debug messages to NORMAL: Show errors, warnings and notices. That is, it won’t apear if you have it set to NONE or MINIMAL.

Once you do that you should get that 4th parameter:

    "exception": "webservice_access_exception",
    "errorcode": "accessexception",
    "message": "Access control exception",
    "debuginfo": "You are not allowed to use the {$a} protocol (missing capability: webservice/rest:use)"

In this instance, user that is associated with the web service token didn’t have the capability webservice/rest:use in their system role. Adding the capability to the role fixed the issue.


Plugin is defective or outdated, Self test failed

You may see an error like this when upgrading a block:

Default exception handler: Plugin "{block name}" is defective or outdated, can not continue, sorry. Debug: Self test failed.
Error code: detectedbrokenplugin
* line 833 of /lib/upgradelib.php: plugin_defective_exception thrown
* line 425 of /lib/upgradelib.php: call to upgrade_plugins_blocks()
* line 1639 of /lib/upgradelib.php: call to upgrade_plugins()
* line 465 of /admin/index.php: call to upgrade_noncore()

To see what self tests are occuring check the blocks/moodleblock.class.php file at the _self_test() function and check that hte block matches the criteria for having a title, appropriate content, applicable formats, preferred width etc.


Finding specific Moodle versions in the Moodle Git Repository

The key to working with the Moodle git repository ( is to look at changes to the version.php file in the main moodle directory which tells you the moodle version.

You can use the whatchanged git command to get a summary of all changes and tags.

git whatchanged -p version.php

But more than likely if you are upgrading or patching you want to find a specific Moodle version. For example, I want to find the commit that relates to “Moodle release 3.1.2” which has the following in the version variable:


To find that use whatchanged to search for that value like so:

git whatchanged -p -S2016052302.00 version.php

This gets you the results that revolve around that version change, e.g. perhaps you want the change from version v3.1.1 to v3.1.2.
Once you have the commit hash e.g. f851201 you can then checkout that hash for example to see that specific version. Make sure you confirm you have what you need by looking at version.php first.

git checkout f851201-B moodle

Updating the Moodle Frontpage Summary through the database

You might have the need to update the Moodle front page summary through the database rather than through the UI. For example you might get an error editing the summary after a clone, or you might want to script the cloning process and change the frontpage text through the database.

Here are the steps:

Update the moodle course sections summary field for the relevant course and summary (you might need to find these first) for your frontpage.
The course ID is probably 1 for your frontpage, but you’ll need to confirm this. You can find the section id by looking at the URL that is used to edit the section (e.g. http://localhost/course/editsection.php?id=xxx).

update mdl_course_sections 
set summary = 'Your text/HTML' 
where course = x 
and section = y;

You’ll probably find that even after you purge the moodle cache this doesn’t take effect. You’ll need to also clear your course section cache with the following SQL.

update mdl_course set sectioncache = '' where id = x;

Alternatively with newer versions of moodle you may also need to purge your moodle cache.


Upgrading database schema using upgrade.php

If you need to upgrade your database schema for a custom plugin, e.g. adding a new table or changing the properties of an existing table, you should do it through db/upgrade.php per the suggestions in the MoodleDocs UpgradeAPI.

This isn’t a bad idea to do even when iterating through development as it isn’t a lot of work to keep things synchronised through the XMLDB editor and you won’t miss something later.

These are the basic steps involved:

  • Use the Moodle XMLDB editor to adjust your schema: Site adminstration > Development > XMLDB editor
  • Copy the relevant changes into your install.xml and upgrade.php straight from the editor
  • Update your plugin version and test.

The Moodle XMLDB editor actually makes this very easy.

  • Open the XMLDB editor
  • Find your plugin (assuming it is already installed etc). If you haven’t managed to get this far, at least install what you have so it will show up in XMLDB editor. Note you do not need to have anything DB related defined for this to work.
    Load the DB for you plugin or if nothing is defined using Create.
  • Follow the UI to create your table, fields, keys and indexes
  • When you are ready use View PHP code to get the code to go into db/upgrade.php
  • Go all the way back to the main XMLDB editor screen (where you can see all plugin databases) and use save to save the XML to db/install.xml as well.

How to get a list of the Moodle Core Git Branches

Use the following git command to get a list of all the Moodle core git branches.

git ls-remote git:// | grep head

This returns a bunch of hashes and the refs/heads/[MOODLE_XX_STABLE] branches. You can guess these, so this is just to check what they are without having to clone the entire repository. Plus you’ll know when a new stable branch is available in the repository.

To clone a specific branch (e.g. say I only want Moodle 2.2), use this command in the relevant target directory (note the -t to include tags so you can go to specific tags like v2.2.2):

git clone -b MOODLE_22_STABLE git://

Disable plugin installation

A really simple way to disable the installation of a plugin is to set the value:

$plugin->version = 0;

In the plugin’s version.php file.


$DB->get_record_sql only returning a single row

So you’ve written a query, passed it to $DB->get_records_sql() and, what’s this – only one row? What’s going on, where’s the rest of the query data?

First off, make sure you are using the plural form (get_records_sql() and not get_record_sql()).

If that’s not the issue then read on.

The reason this can happen is that Moodle uses the first column in your query as a unique id and then filters results down to that id field. However, if your first column isn’t unique across your data set, you won’t get every row.

For example, your query may select user enrolments for a course. But if you put your course id as the first column, you’ll only get one row, even if there are 30 enrolments in that course, because all of those enrolments belong to that one course id and that is the unique identifier used.

The fix is simple, find (or if you need to, create) a unique id so that the first column is unique for every row in your query. In the user enrolments example you might use the user ID or the user_enrolments ID.


Uninstalling a Moodle Plugin and forcing reinstallation

The easiest way to get moodle to re-run installation is to update the version in version.php. However, if you want to be sure it makes any schema changes etc then remove the plugin version from the mdl_config_plugins table. Note you can leave the other configuration there if required to save re-setting up the plugin.

The SQL to do this:

delete from mdl_config_plugins 
where plugin = 'plugin_name' 
and name = 'version';

For schema changes, you may need to drop the table and have it rebuild it just to ensure that the changes are made correctly.

For production deployments you should use an upgrade.php script, but this is a handy shortcut if you are developing a plugin as you really just want to iterate to the final schema version which you would then put into upgrade.php.


Themes and Page Layouts

The types of page layouts – which determine how your moodle page is rendered via:


These are defined in the base theme (and can be extended by custom themes): Have a look in theme/base/config.php in the $THEME->layouts array for options.


Convert a Moodle URL object to string

There’s a method in the moodle_url class in lib/weblib.php called __toString which will convert a Moodle URL object back to a string if you just need the URL string itself.


Hide Navigation for My Profile > Messages

To hide the navigation: My Profile > Messages for all users:

1. This is set by the configuration value messaging
2. You can either update the value of this in the database (config table) from 1 to 0 OR you can add an entry in config.php where you set $CFG->messaging = 0;
3. Clear the moodle cache


Adding a local plugin to site admin navigation

If you want to add a local plugin navigation under the Site Administration block (under Plugins > Local Plugins) add the following to your local plugin settings.php file:

// Add the settings page to the navigation block

$settings = new admin_settingpage(
    get_string('pluginname', 'local_yourplugin')

$ADMIN->add('localplugins', $settings);