Tuesday, November 1, 2011

Converting M1.9 Plug-ins to M2 - Block Part 4 - Fixing Old Code

This is part five of my series concerning porting Moodle 1.9 code to Moodle 2.

I want to tackle the display bug I see when the block updates. If you remember, when I installed the working version of the block, it displayed the tweets initially, but displayed a "404" error for each update after that.

My initial thoughts on this is that it is caused by multiple problems. Database API used in the "ajax_update.php" script, changed database table name and YUI scripts no longer located in the same places.

To begin I open up the "ajax_update.php" script.

This script is very small - it only contains the following lines of code:
<?php
require_once('../../config.php');
$block = block_instance('twitter_search', get_record_select('block_instance','id="'.required_param("blockid",PARAM_NUMBER).'"'));
echo $block->get_content()->text
?>
It is small for a reason: it gets called repeatedly by the block through its Ajax javascript functions, to reload the tweets. It does this by reinstantiating the block instance and calling its get_content method.

Looking at the code above, I see two problems: the table name for block instances has been changed from "block_instance" in Moodle 1.9, to "block_instances" in Moodle 2, and  it is using the old data API call, get_record_select. These both have to be fixed.

The new database API is similar to the old one, but with significant changes. The ones I care about here are:
  • All database API functions are now methods of the global database object $DB.
  • All parameters to the database functions are now passed as array "key => value" pairs instead of individual parameters.
I'm also going to break the old single line multiple function code line into three lines so its easier to see what is happening. When I'm done, the new code looks like this:
require_once '../../config.php';
$id = required_param('blockid', PARAM_NUMBER);
$bi = $DB->get_record('block_instances', array('id' => $id));
$block = block_instance('twitter_search', $bi);
echo $block->get_content()->text;
The main changes are in the third line. Here I have changed get_record_select to $DB->get_record, changed the table name to the new name and passed the "id" argument in a hashed array format. (Note that I don't have to declare $DB as a global, as this script is running at the top level and not in a function call.) I'm going to save this code and see if it fixes my problem.

I reload my page with the block, click the "update" link, and I see new tweets. No more "404" error. But, there are a bunch of warnings now, indicating that I have a coding error involving $PAGE->context. Specifically:
Coding problem: $PAGE->context was not set. You may have forgotten to call require_login() or $PAGE->set_context(). The page may not display correctly as a result
This is kind of a strange one, as the problem is that Moodle 2 is telling us that the page may not display correctly because I haven't called all the necessary functions that tells the $PAGE object what I am outputting. Of course, since it is outputting into an already existing block on a page using Ajax, this isn't really a problem. Still, there must be something I can do to fix this.

It looks like all I need to do is call "require_login" and "$PAGE->set_context" before doing any output. I'll add require_login right after the require_once statement. Since the $PAGE->set_context requires an actual context to be passed to it, I'll add it right after I create the $block object and pass it the block's context. So the statement is $PAGE->set_context($block->context).

Now I'll look at the page again. Success! The warnings are gone, and I'm getting tweet updates.

For the record, my code now looks like:
require_once '../../config.php';
require_login();
$id = required_param('blockid', PARAM_NUMBER);
$bi = $DB->get_record('block_instances', array('id' => $id));
$block = block_instance('twitter_search', $bi);
$PAGE->set_context($block->context);
echo $block->get_content()->text;
I still haven't tackled the YUI library location changes, but the block is working, so I'll look at those in the next post.