Tuesday, November 22, 2011

Converting Moodle 1.9 Plug-ins to Moodle 2 - Activity Module Upgrade - Part 1

Continuing on with my Moodle 1.9 to Moodle 2 code migration series, I next want to tackle an activity module. I took a look around the "contrib" areas for a suitable module that needed attention. Alas, I could not find one that was simple enough to do in this blog and hadn't already been migrated. But, I have chosen to do David Mudrack's Stamp Collection module. I have spoken to David, and he is in the process of migrating this, so my version won't be official. Still, it is a good module to migrate as it is straightforward and will allow me to demonstrate the key areas that need to be changed.

I have created a repository for the code, forked from David's. The MOODLE_19_STABLE branch contains the code that will be modified. The MOODLE_21_STABLE branch contains my changes as I migrate the module. Start with the MOODLE_19_STABLE branch if you want to follow along.

When I did the block migration I attempted to install the block with the old code first. With an activity module, I could do this, but I already know with the amount of changes necessary, it will fail. And because of the way Moodle works, as long as that old code is there, I will not be able to get past the upgrade screen as an administrator. So instead, I am going to try and create a methodical upgrade process here.

I'm going to start by referring to the "Migrating contrib code to 2.0" document. This document is for exactly what I'm doing.

Now, here I will diverge for the first time (yes, already). One of the key things missing here is the new language string requirements.

In Moodle 1.9, all plug-ins had language subdirectories with the suffix "_utf8" in their name. In Moodle 2, this suffix is removed. So one of the first things I need to do is rename all of my language subdirectories in this fashion. For example, I rename my '/mod/stampcoll/lang/en_utf8' directory to '/mod/stampcoll/lang/en'. The other key change is to add two new strings to my language file: 'pluginname' and 'pluginadministration'. I add the following to my '/mod/stampcoll/lang/en/stampcoll.php' file:
$string['pluginadministration'] = 'Stamp collections administration';
$string['pluginname'] = 'Stamp collections';
There are some other key changes that I will have to make to my language files, but I will put that off until later.

Back to the migration document, the first thing it notes is that I need a 'requires' statement in my version file. Without this, my module will not pass the installation stage.

Looking at my 'version.php' file, I specifically change:
$module->requires = 2007101508;  // Requires this Moodle version - 1.9 Beta 4
$module->requires = 2010080300;  // Requires this Moodle version
I also upgrade the version of the module to reflect that it is new. For now, I have selected the same version number as the "requires".

Next, the document refers to the database layer. There are many changes in Moodle 2 that impact database functions. I'm going to focus on the XMLDB/DDL changes first, since they impact the actual installation and upgrading of the module.

I have opened up the "db/install.xml" file in a text editor. At the bottom of that file, there is XML code framed in <STATEMENTS> tags. In 1.9, this section was used to execute any specific data statements that needed to be executed after the data tables were set up. Primarily, this section contained records to enter into the Moodle "log_display" table. In Moodle 2, log display entries go into a separate file, "db/log.php". If there are any other data statements required for the module, they now belong in "db/install.php".

In my "db/install.xml" file, I have:
  <STATEMENT NAME="insert log_display" TYPE="insert" TABLE="log_display" COMMENT="Initial insert of records on table log_display">
      <SENTENCE TEXT="(module, action, mtable, field) VALUES ('stampcoll', 'view', 'stampcoll', 'name')" />
      <SENTENCE TEXT="(module, action, mtable, field) VALUES ('stampcoll', 'update', 'stampcoll', 'name')" />
      <SENTENCE TEXT="(module, action, mtable, field) VALUES ('stampcoll', 'add', 'stampcoll', 'name')" />
      <SENTENCE TEXT="(module, action, mtable, field) VALUES ('stampcoll', 'update stamp', 'user', 'concat(firstname, \' \', lastname)')" />
      <SENTENCE TEXT="(module, action, mtable, field) VALUES ('stampcoll', 'delete stamp', 'user', 'concat(firstname, \' \', lastname)')" />
I remove all of these statements from the file, and create a new "db/log.php" file containing:
$logs = array(
    array('module'=>'stampcoll', 'action'=>'view', 'mtable'=>'stampcoll', 'field'=>'name'),
    array('module'=>'stampcoll', 'action'=>'update', 'mtable'=>'stampcoll', 'field'=>'name'),
    array('module'=>'stampcoll', 'action'=>'add', 'mtable'=>'stampcoll', 'field'=>'name'),
    array('module'=>'stampcoll', 'action'=>'update stamp', 'mtable'=>'user', 'field'=>'concat(firstname, \' \', lastname)'),
    array('module'=>'stampcoll', 'action'=>'delete stamp', 'mtable'=>'user', 'field'=>'concat(firstname, \' \', lastname)'),
There are no other statements I need to worry about, so I don't need a "db/install.php" file.

Next, I need to remove all "ENUM" statements from the XML. "ENUM" is no longer a valid attribute in Moodle, so the presence of these statements will be flagged as a warning. I could manually remove them from the "install.xml" file right now, but I'm going to leave them. Moodle provides me a simpler method using the XMLDB editor that I will use later.

Finally, I need to make some changes to the existing "db/upgrade.php" script. As documented, there are many changes to be made, most of them straightforward. The summary of what I chang is:
  • Change the $db global declaration to $DB.
  • Add the line "$dbman = $DB->get_manager();".
  • Change all of the "XMLDB" functions to "xmldb_" functions.
  • Change all of the "setAttributes" functions to "set_attributes", removing the sixth and seventh parameters in the few places they are provided.
  • Add $dbman-> to the DDL functions.
  • Remove the use of $result from the DDL functions.
  • Remove all other uses of $result and set the return value to "true".
  • Add upgrade_mod_savepoint calls at the end of each upgrade block.
When completed, I have a "mostly" working upgrade script. There are some changes I will need to make at the start of this script, where DML function calls are used. This will not impact a fresh install though, so I will do that when I do the main DML changes later.

There seems to be one other change I need to make, that I cannot find documented anywhere. In "db/access.php", the variable name $mod_stampcoll_capabilities needs to be changed to $capabilities.
    These should be all the changes I need to have the module install correctly in Moodle 2. Time to give it a shot! Visiting the site notifications page, offers me the option to install the module. I hit the "Upgrade" button, and it installs without errors or warnings.

    Now, there is one more thing I'm going to fix before leaving the installation code. Remember that I noted that "ENUM" statements needed to be removed from the "install.xml" file? Now we're going to use the Moodle tool to do this.

    I select the "XMLDB editor" option from the "Site administration / Development" menu. From there, I look for the "mod/stampcoll/db" entry and click the "Load" link. If I click the "XML" link now, I will see the "ENUM" statements still present. Instead, I click the "Edit" link. This brings me to a screen like the image below. Clicking either of the "XML" links shows me XML without the "ENUM" statements.

    Now, at worst case, you can cut the XML from this display and paste it back into your XML file. At best, you can set your directory permissions so that the application can write to it, and save the changes right from there. Both will work fine, and both will result in XML with the "ENUM" statements gone, which is what Moodle 2 wants.

    Just to make sure my XML is okay, I un-installed my Stamp Collection module, and then re-installed it. Everything worked fine.

    That will be all I do for this part. Next, we will look at the other changes to make the module functional.