Thursday, October 11, 2012

Adding Moodle 1.9 Block Restore to Moodle 2.3 - Part 2

This is part two of my series on writing the Moodle 1.9 to 2.3 block restore conversion code for Moodle core.

When I finished part one, I had discovered that I could begin defining block restore handlers by creating a converter file and providing a minimum handler function. But, my handler was not yet providing the minimum amount of information to remove the warning from the restore logs.

Looking back at the document, I see that the get_paths() function needs to return an array for all XML elements of the restore. My "participants" block needs a path "/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK/PARTICIPANTS" defined. This can be confirmed by the warning in the log file. As an experiment, I change the function in "/blocks/participants/backup/moodle1/lib.php" to look like:
public function get_paths() {
    return array(
        new convert_path('participants', '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK/PARTICIPANTS'),
    );
}
I also note that the document indicates that one process_xxx() function must exist for every path defined in get_paths(). So I also add:
public function process_participants($data) {
}
Retrying my restore, and reviewing the restore logs, the warning is now gone for my "participants" block. I am definitely on the right track.

Next, I'm going to review the "Data structure transformations" section to see what I need to do.

This section shows that the get_paths() function also provides transformation information for data elements that have changed from 1.9 to 2.x. This documentation is specific to modules but does help understand what needs to be done for blocks. I do know that quite a bit of the block data structure changed in Moodle 2, so I will need to create a transformation. But, in general, the block data that changed is generic to all blocks. I don't think I should be creating general block data transformation code in a specific block's handler. I need to do more research.

At this point, I am going to do a bit more code searching, to see if I can figure out what is happening internally. I know that everything essentially starts in the "/backup/converter/moodle1/lib.php" file with the moodle1_converter class. Inside that class, I find the init() function, which adds the MOD and BLOCK plug-ins to the object. Much of this is done through calls to the moodle1_handlers_factory class in the "/backup/converter/moodle1/handlerlib.php" file. Specifically this code, brings in each block's conversion handler:
$handlers = array_merge($handlers, self::get_plugin_handlers('block', $converter));
...
protected static function get_plugin_handlers($type, moodle1_converter $converter) {
    global $CFG;

    $handlers = array();
    $plugins = get_plugin_list($type);
    foreach ($plugins as $name => $dir) {
        $handlerfile  = $dir . '/backup/moodle1/lib.php';
        $handlerclass = "moodle1_{$type}_{$name}_handler";
        if (!file_exists($handlerfile)) {
            continue;
        }
        require_once($handlerfile);

        if (!class_exists($handlerclass)) {
            throw new moodle1_convert_exception('missing_handler_class', $handlerclass);
        }
        $handlers[] = new $handlerclass($converter, $type, $name);
    }
    return $handlers;
}
What this is doing is looking at each block in the system, and seeing if each has a converter handler available, by looking for a "/blocks/[blockname]/backup/moodle1/lib.php" file, and then checking to see if it has a moodle1_block_[blockname]_handler class within it. If there is, then that handler gets registered for use in the conversion. If the file does not exist, then it moves on. If the file exists, but the class does not, then an error is thrown.

This code explains what I've seen in my restore tests. Originally, I was seeing the restore log indicating that none of the blocks had attached handlers. This is because there was no file with the appropriate class for any of the blocks. When I added that file and class to the "participants" block, I initially saw an error. The error occurred because the handler was registered, but the class was not providing the proper functions. Once the function was provided, I no longer saw the "no handler attached" message in the restore log, because my handler had been successfully registered in the code above.

I also notice that in the moodle1_handlers_factory::get_handlers() function, there are a number of handlers registered that are contained within the same file, using the code:
$handlers = array(
    new moodle1_root_handler($converter),
    new moodle1_info_handler($converter),
    new moodle1_course_header_handler($converter),
    new moodle1_course_outline_handler($converter),
    new moodle1_roles_definition_handler($converter),
    new moodle1_question_bank_handler($converter),
    new moodle1_scales_handler($converter),
    new moodle1_outcomes_handler($converter),
    new moodle1_gradebook_handler($converter),
);
I am now thinking, that I need to start with a more generic block handler, similar to one of the above handlers. Looking back at David's comment in the original tracker issue about following "a pattern similar to the one we used for Question bank conversion" leads me to believe this is the right track. Next, I will need to explore how questions are handled in the converter.