Upgrade API

From MoodleDocs
Jump to: navigation, search

The Upgrade API is how your module installs and upgrades itself, by keeping track of its own version.


Contents

Introduction

By implementing this API in your plugin, Moodle will automatically create your database tables for you when you visit the Admin notifications page (.../admin/index.php).

This process is controlled by three files within your plugin (there are other optional files you can use for various things. These are covered at the end of this document):

version.php 
This records the version of the plugin code (Please note: In Moodle installations below v2.0, this is not required for blocks, which use the version number returned by the block's init() method - see the blocks development page). You must increase version in version.php after any change in /db/ folder, any change in javascript code and also after any change in language pack because new version triggers upgrade upgrade procedure and resets all caches.
db/install.xml 
This is used when someone installs your plugin for the first time.
db/upgrade.php 
This is used when someone who had an older version of your plugin installed upgrades to the latest version.

In addition, Moodle also stores in the database the currently installed version of each plugin.

In Moodle 1.x, this is stored in the mdl_config table, in a row with the name plugintype_pluginname_version. For example qtype_myqtype_version. The exception to this rule are modules and blocks. Installed module version numbers are stored in the mdl_modules table. Block version numbers are in mdl_block.

In Moodle 2.x, plugin version numbers are stored in the mdl_config_plugins table, with plugintype_pluginname in the plugin column, and 'version' in the name column - with the same exception for modules and blocks.

A specific example

For the rest of this document, I will use a particular example, because it should make the explanation easier. You should be able to see how to generalise it.

We will suppose you that you are making a new question type myqtype. This is plugin type qtype, and the code will be in the question/type/myqtype folder. The currently installed version number will be stored in the ('qtype_myqtype', 'version') row of the mdl_config_plugins table.

In addition, we will just consider the first two releases of the plugin. The first release will have version number 2008080100, and will just use one database table mdl_myqtype_options, with two columns col1 and col2. Then we will suppose that the second release is 2008080200, and that requires an extra column, newcol, to be added to the mdl_myqtype_options table.

The files you need for the first release

In what follows, the bits of code you need to replace are in bold.

version.php
$plugin->version  = 2008080100; // Today's date in YYYYMMDD format, plus two additional digits
$plugin->requires = XXXXXXXXXX; // Moodle version - copy from $version in the top-level version.php file.
Version numbers normally consist of the day's date in YYYYMMDD format, followed by two additional digits to allow for multiple versions in the same day, e.g. Sept 15, 2010 could have versions 2010091500, 2010091501, 2010091502, etc.
db/install.xml
This file, which you should create with the XMLDB editor, should contain the definition for your mdl_myqtype_options table, with the two columns col1 and col2.

At this stage, you do not need a db/upgrade.php file.

The files you need for the second release

version.php
$plugin->version  = 2008080200;
$plugin->requires = XXXXXXXXXX; // Copy the current value from the top-level version.php file.
db/install.xml 
This file should now contain the updated definition for your mdl_myqtype_options table, with three columns col1, col2 and newcol. You modify this file using the XMLDB editor.
db/upgrade.php
This file should contain the code that people need to run to upgrade from version 2008080100 of your plugin. That is, the code to add a column newcol to the mdl_myqtype_options table. You don't have to write this code yourself as the XMLDB editor will generate it for you. The upgrade.php file should contain a single function xmldb_qtype_myqtype_upgrade that looks a bit like:
function xmldb_qtype_myqtype_upgrade($oldversion = 0) {
    global $DB;
    $dbman = $DB->get_manager();

    $result = true;

    /// Add a new column newcol to the mdl_myqtype_options
    if ($result && $oldversion < 2008080200) {
        // Code to add the column, generated by the 'View PHP Code' option of the XMLDB editor.
    }

    return $result;
}

Hint: If you are modifying or adding a field/table, get the XMLDB editor to generate the PHP update code for you after making the changes in the editor. If you are deleting one, you need to generate the PHP code before making the change - or you won't be able to select the field/table to write the code for, because it no longer exists.

What happens when a user installs or upgrades your plugin

The process is triggered when an administrator goes to the Admin notifications page (.../admin/index.php). The code that does the work is the upgrade_plugins function in lib/adminlib.php and reading this code is the best way to find out exactly what happens. In pseudo-code, what it does is:

For each plugin of this type (e.g. all qtype plugins) {
    // For the body of this loop, suppose the current plugin being processed is myqtype.

    Check that question/type/myqtype/version.php, .../db/upgrade.php and .../db/install.xml exist.

    if (get_config('qtype_myqtype', 'version') exists, and is less than the number in version.php) {
        Call the upgrade function xmldb_qtype_myqtype_upgrade from upgrade.php,
                passing the old version number that says what is currently installed
        set_config('version', latest number from version.php, 'qtype_myqtype')
                to record what is currently installed

    } else if (get_config('qtype_myqtype', 'version') does not exist) {
        Create the tables from the definitions in install.xml
        set_config('version', latest number from version.php, 'qtype_myqtype')
                to record what is currently installed
    }
}

Of course, it is a bit more complex that than. However, the code in the upgrade_plugins is quite clear, and I encourage you to go and have a look at it so you can see all the details of how it works.

Let us now look at some worked examples:

User installs version 2008080100 of myqtype

To start with, the mdl_myqtype_options does not exist, and there will not be a ('qtype_myqtype', 'version') row in the mdl_config_plugins table.

  1. The user will unzip myqtype.zip into the question/type folder.
  2. The user will visit the Admin notifications page.
  3. This will trigger Moodle to search for plugings to upgrade. It will find that the code for the qtype_myqtype plugin is now present, but there is no trace of it in the database, so it will install the plugin from the install.xml file.

At the end of this process, the mdl_myqtype_options table will exist with the two columns col1 and col2; and the ('qtype_myqtype', 'version') row in the mdl_config_plugins table will contain 2008080100.

User upgrades from version 2008080100 to version 2008080200 of myqtype

To start with, the mdl_myqtype_options table will exist with the two columns col1 and col2; and the ('qtype_myqtype', 'version') row in the mdl_config_plugins table will contain 2008080100.

  1. The user will delete the old question/type/myqtype folder.
  2. The user will unzip the new myqtype.zip into the question/type folder.
  3. The user will visit the Admin notifications page.
  4. This will trigger Moodle to search for plugings to upgrade. It will find that the code for version 2008080200 the qtype_myqtype plugin is now present, but the installed version of the the database tables ('qtype_myqtype', 'version') is 2008080100. Therefore, it will call xmldb_qtype_myqtype_upgrade from upgrade.php, passing 2008080100 as the $oldversion argument.

At the end of this process, the mdl_myqtype_options table will now have three columns col1, col2 and newcol; and the ('qtype_myqtype', 'version') row in the mdl_config_plugins table will contain 2008080200.

User installs version 2008080200 of myqtype into a clean Moodle install

To start with, the mdl_myqtype_options does not exist, and there will not be a ('qtype_myqtype', 'version') row in the mdl_config_plugins table.

  1. The user will unzip the 2008080200 version of myqtype.zip into the question/type folder.
  2. The user will visit the Admin notifications page.
  3. This will trigger Moodle to search for plugings to upgrade. It will find that the code for the qtype_myqtype plugin is now present, but there is no trace of it in the database, so it will install the plugin from the install.xml file.

At the end of this process, the mdl_myqtype_options table will exist with three columns col1, col2 and newcol; and the ('qtype_myqtype', 'version') row in the mdl_config_plugins table will contain 2008080200.

Upgrade code restrictions

Within an upgrade, there are restrictions on the functions your code should call, because the system has not been fully updated.

  • All upgrade code can use the basic database API ($DB->... functions).
  • In a plugin, upgrade code should not call any plugin functions. (For example, if your plugin has a function that changes frog settings to 'green', and you need to do this during upgrade, then you should not call this function; instead, manually update the database rows so that the frog settings become green.) However, you should call core functions rather than making core changes in database.
  • In core, upgrade code should not even call any core functions. (For example, if you need to add a calendar event, this should be done by inserting into a database table rather than calling a function to add the event.)

The reason for this is the state of the system during upgrade.

During core upgrade the state is as follows:

  • Core data: Old.
  • Plugin data: Old.

Core functions expect core data to be in the Current state, so it is not safe to call them.

During plugin upgrade the state is as follows:

  • Core data: Current. (Because core upgrade runs before plugin upgrade.)
  • Plugin data: Old.

Core functions are now safe to call because the core data is in Current state. But plugin functions, which expect data to be in the Current state, are not safe.

Summary

The first time a user installs any version of your plugin, the install.xml file will be used to create all the required database tables. Therefore install.xml should always contain the definition of the up-to-date database structure. Moodle recognises this situation because there is a version.php file on disc, but there is no (plugintype_pluginname, version) value in the mdl_config_plugins table.

If the user already had a version of your plugin installed, and then upgrades to a newer version, Moodle will detect this because the version.php file will contain a newer version number than the (plugintype_pluginname, version) value in the mdl_config_plugins table. In this case, Moodle will run the code in the upgrade.php file, passing in the old version number, so that the correct bits of upgrade can be run, as controlled by the if ($oldversion < XXXXXXXXXX) blocks of code.

The contents of the install.xml and upgrade.php files should be generated using the XMLDB editor.

Other things that can be in the db folder

Of these, only install.php relates to creating the necessary database structure. The other files are mainly about providing the other meta-data that is required when the plugin is installed. I have tried to link to other documentation that goes into detail where possible.

install.php

(Moodle 2.0 and later)

If you define a function like xmldb_qtype_myqtype_install(), this PHP code will be executed when your plugin is installed. This is an opportunity to do things that you cannot do with install.xml. It's executed immediately after the DB schema associated with the component (install.xml) is created.

(Moodle 1.9)

An install function can be defined in lib.php in the root directory of your module. The install function must have the name qtype_install() and takes no arguments (n.b. this is called from lib/adminlib.php after install.xml is parsed)

uninstall.php

(Moodle 2.0 and later)

Balancing the previous feature, it's also possible to define one function like xmldb_qtype_myqtype_uninstall(), that will be executed when your plugin is uninstalled, before dropping its DB schema.

access.php

(Moodle 1.7 and later)

Use to define the capabilities that this plugin handles with the Access API. You must increase version in version.php after adding new capabilities.

events.php

(Moodle 1.9(?) and later)

Used to define the events this plugin sends to the Events API. You must increase version in version.php after modifying or adding new events.

log.php

(Moodle 2.0 and later)

Used to describe the type of log entries that this plugin creates. See the log.php section of the Logging API for more information. You must increase version in version.php after any change.

messages.php

(Moodle 2.0 and later)

Used to define the messages this plugin sends through the Messaging API. You must increase version in version.php after any change.

services.php

(Moodle 2.0 and later)

Used to define the functions and services this plugin publishes through the Web services API. Usually all those functions are part of the External functions API. You must increase version in version.php after any change.

upgradelib.php

Optional file where upgrade code can be grouped under some helper functions to be used in the upgrade.php file. If needed, this file must be manually required once from upgrade.php. Its main goal is to keep the upgrade.php scripts reduced and clear (this cannot contain any function themselves apart from the xmldb_xxx_upgrade() one).

It's important to note that the upgradelib.php files should always be performing any task using raw - low level - database access exclusively, avoiding any use of the Moodle APIs.

mod/.../db/subplugins.php

(Moodle 2.0 and later)

This only applies to activity modules, not other types of plugin. It is a way to allow an activity module to have its own sorts of plugins. For example workshop allocation schemes, or quiz reports.


Language packs

Do not forget to increase version number after modifying lang/en/type_yourplugin.php language file. See Languages/AMOS section AMOS script for more information.

Upgrade API Cheatsheet

The Upgrade API is related with a lot of different files and APIs (access, event, log, webservice...) as far as it's the API used to install/upgrade all those artifacts. The previous sections have tried to list all those dependencies when possible.

Also, the Upgrade API does a very intensive use of other APIs (ddl, dml) and tools (the XMLDB editor...) in order to proceed with the modifications of the Moodle system.

Once stated the above, we could summarise the API as the collection of a few files and functions, here they are:

The files: For each component we can have, under its "db" directory.

  • version.php: Where the $version is specified and, if bumped, the upgrade machinery will be launched and all caches reset. Bump up version after changing /db/, /lang/, theme or JavaScript.
  • install.xml: XMLDB definition of the DB schema. generated with the XMLDB Editor.
  • install.php: Post-install file executed once the schema has been created.
  • uninstall.php: Pre-uninstall file executed before the schema is dropped.
  • upgrade.php: Ordered list of upgrade steps, each one performing a well-defined task.
  • upgradelib.php: Library files composed of helper functions to be used by upgrade.php.

The functions: With different names for different components, but available to all them:

  • xmldb_[main|frankenstyle]_install(): to be used in install.php files.
  • xmldb_[main|frankenstyle]_uninstall(): to be used in uninstall.php files.
  • xmldb_[main|frankenstyle]_upgrade(): to be used in upgrade.php files.
  • upgrade_set_timeout(): to be used within the upgrade steps in upgrade.php files, in order to grant more time to the step.
  • upgrade_[main|mod|block|plugin]_savepoint(): to consider one upgrade step completed and reset timeouts. Avoids double executions.

Of course, for practical examples of the use of this API it's highly recommended to examine and understand current code. Everything is out there.

Database upgrades and stable branches

The simple rule is, never make any database changes on a stable branch. You only need to read this section in the rare situations where a database change on the stable branch is unavoidable.

Warning!!! advanced material follows.

Suppose, in order to fix a bug, you need to make a database change in Moodle 1.9.3+ (which must also be merged into HEAD). The root of the problem is that people may upgrade their Moodle in three different ways, which

  • Upgrade from <=1.9.3 to 1.9.4 - this executes the ugprade script on the 1.9 branch.
  • Upgrade from <=1.9.3 directly to >=2.0 - this executes the upgrade script on the HEAD branch.
  • Upgrade from 1.9.4 to >=2.0 - in this case, you must ensure that the upgrade on HEAD is not executed.

The normal way to do this is ensure that your database upgrade is idempotent. That is, it does not matter if you do it twice. So for example, instead of doing

       $dbman->create_table($table);

you should do

       if (!$dbman->table_exists($table)) {
           $dbman->create_table($table);
       }

You should also think about what version numbers to put in your version.php file on each branch. Above all, test carefully.

See also

Personal tools
User docs (English)