Blocks: Difference between revisions
m (I was getting an error while first time running this code, so I have put a minor notice about language file.) |
(Replaced deprecated PARAM_MULTILANG with PARAM_TEXT) |
||
Line 163: | Line 163: | ||
$mform->addElement('text', 'config_text', get_string('blockstring', 'block_simplehtml')); | $mform->addElement('text', 'config_text', get_string('blockstring', 'block_simplehtml')); | ||
$mform->setDefault('config_text', 'default value'); | $mform->setDefault('config_text', 'default value'); | ||
$mform->setType('config_text', | $mform->setType('config_text', PARAM_TEXT); | ||
} | } |
Revision as of 09:24, 11 March 2014
A Step-by-step Guide To Creating Blocks
Original Author: Jon Papaioannou (pj@moodle.org)
Updated to Moodle 2.0 and above by: Greg J Preece (greg.preece@blackboard.com)
Moodle 2.0
The present document serves as a guide to developers who want to create their own blocks for use in Moodle. It applies to the 2.0 development version of Moodle (and any newer) only, as the blocks API changed significantly enough to warrant new documentation. Those wishing to read the old tutorial for Moodles 1.5 to 1.9 can find it under Blocks for 1.5 to 1.9.
The guide is written as an interactive course which aims to develop a configurable, multi-purpose block that displays arbitrary HTML. It's targeted mainly at people with little experience with Moodle or programming in general and aims to show how easy it is to create new blocks for Moodle. A certain small amount of PHP programming knowledge is still required, though.
Experienced developers and those who just want a programmer's reference text should refer to Appendix A because the main guide has a rather low concentration of pure information in the text.
Basic Concepts
Through this guide, we will be following the creation of an "HTML" block from scratch in order to demonstrate most of the block features at our disposal. Our block will be named "SimpleHTML". This does not constrain us regarding the name of the actual directory on the server where the files for our block will be stored, but for consistency we will follow the practice of using the lowercased form "simplehtml" in any case where such a name is required.
Whenever we refer to a file or directory name which contains "simplehtml", it's important to remember that only the "simplehtml" part is up to us to change; the rest is standardised and essential for Moodle to work correctly.
Whenever a file's path is mentioned in this guide, it will always start with a slash. This refers to the Moodle home directory; all files and directories will be referred to with respect to that directory.
Ready, Set, Go!
To define a "block" in Moodle, in the most basic case we need to provide just four PHP files. Remember, in this example we are creating a block called 'simplehtml', replace 'simplehtml' with the name of your custom block. The four files should be located in blocks/simplehtml and are:
block_simplehtml.php
This file will hold the class definition for the block, and is used both to manage it as a plugin and to render it onscreen.
We start by creating the main object file, 'block_simplehtml.php'. We then begin coding the block:
<?php
class block_simplehtml extends block_base {
public function init() {
$this->title = get_string('simplehtml', 'block_simplehtml');
}
// The PHP tag and the curly bracket for the class definition
// will only be closed after there is another function added in the next section.
The first line is our block class definition; it must be named exactly in the manner shown. Again, only the "simplehtml" part can (and indeed must) change; everything else is standardised.
Our class is then given a small method: init(). This is essential for all blocks, and its purpose is to give values to any class member variables that need instantiating.
In this very basic example, we only want to set $this->title, which is the title displayed in the header of our block. We can set it to whatever we like; in this case it's set to read the actual title from the language file mentioned below, which is then distributed along with the block. I'll skip ahead a bit here and say that if you want your block to display no title at all, then you should set this to any descriptive value you want (but not make it an empty string). We will later see how to disable the title's display.
db/access.php
This file will hold the new capabilities created by the block.
Moodle 2.4 onwards introduced the capabilities addinstance and myaddinstance for core blocks. They were introduced so that it was possible to control the use of individual blocks. These capabilities should also be added to your custom block, so in this case to the file blocks/simplehtml/db/access.php. If your block is not going to be used in the 'My Moodle page' (ie, your applicable_formats function (discussed later) has 'my' set to false.) then the myaddinstance capability is not required. The following is the capabilities array and how it should look for any new blocks.
<?php
$capabilities = array(
'block/simplehtml:myaddinstance' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => array(
'user' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/my:manageblocks'
),
'block/simplehtml:addinstance' => array(
'riskbitmask' => RISK_SPAM | RISK_XSS,
'captype' => 'write',
'contextlevel' => CONTEXT_BLOCK,
'archetypes' => array(
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/site:manageblocks'
),
);
lang/en/block_simplehtml.php
This is the English language file for your block. If you are not an English speaker, you can replace 'en' with your appropriate language code. All language files for blocks go under the /lang subfolder of the block's installation folder.
Moodle 2.0 and above require a name for our plugin to show in the upgrading page. We set this value, along with the capabilities we created and any other language strings we wish to use within the block, in a language package as previously mentioned (the same file where we put our string for the plugin title).
The capabilities added above need descriptions for pages that allow setting of capabilities. These should also be added to the language file.
<?php
$string['pluginname'] = 'Simple HTML block';
$string['simplehtml'] = 'Simple HTML';
$string['simplehtml:addinstance'] = 'Add a new simple HTML block';
$string['simplehtml:myaddinstance'] = 'Add a new simple HTML block to the My Moodle page';
version.php
This file will hold version information for the plugin, along with other advanced parameters (not covered here - see version.php if you want more details).
Prior to Moodle 2.0, version details for blocks were stored as class fields; as of Moodle 2.0 these are stored in a file called version.php, stored under /blocks/simplehtml/version.php. The version file is very simple indeed, containing only a few field definitions, depending on your needs. Here is an example:
<?php
$plugin->version = 2011062800; // YYYYMMDDHH (year, month, day, 24-hr time)
$plugin->requires = 2010112400; // YYYYMMDDHH (This is the release version for Moodle 2.0)
This file contains object field definitions that denote the version number of the block, along with the minimum version of Moodle that must be installed in order to use it. Please note that the object being used here is always called $plugin, and that you do not need to create this object yourself within the version file. Note also we don't include a closing "?>" tag. This is intentional, and a workaround for whitespace issues.
I Just Hear Static
In order to get our block to actually display something on screen, we need to add one more method to our class (before the final closing brace in our file). The new code is:
public function get_content() {
if ($this->content !== null) {
return $this->content;
}
$this->content = new stdClass;
$this->content->text = 'The content of our SimpleHTML block!';
$this->content->footer = 'Footer here...';
return $this->content;
}
} // Here's the closing bracket for the class definition
Add your block to the front page! Don't forget (especially if you're a new Moodle user) to add your block to the front page. Turn Editing On, and add your block to the page.
It can't get any simpler than that, can it? Let's dissect this method to see what's going on...
First of all, there is a check that returns the current value of $this->content if it's not NULL; otherwise we proceed with "computing" it. Since the computation is potentially a time-consuming operation and it will be called several times for each block (Moodle works that way internally), we take a precaution and include this time-saver. Supposing the content had not been computed before (it was NULL), we then define it from scratch. The code speaks for itself there, so there isn't much to say. Just keep in mind that we can use HTML both in the text and in the footer, if we want to.
It's worth mentioning that this is not the only type of content a block can output. You can also create lists and hierarchical trees, which are better suited for certain types of output, such as menus. These different content types have an impact on the content object and how it is constructed. For more information, see Appendix A
At this point our block should be capable of being automatically installed in Moodle and added to courses; visit your administration page to install it (Click "Notifications" under the Site Administration Block) and after seeing it in action come back to continue our tutorial.
Configure That Out
The current version of our block doesn't really do much; it just displays a fixed message, which is not very useful. What we'd really like to do is allow the teachers to customize what goes into the block. This, in block-speak, is called "instance configuration". Basic instance configuration is automatic in Moodle 2.0; if you put any page with blocks on it into "editing mode", you will notice that each block has an edit button in its title bar. Clicking on this will take you to the block configuration form. The settings that Moodle adds to this form by default relate to the block's appearance and position on Moodle pages.
We can extend this configuration form, and add custom preferences fields, so that users can better tailor our block to a given task or page. To extend the configuration form, create the file /blocks/simplehtml/edit_form.php, and fill it with the following code:
<?php
class block_simplehtml_edit_form extends block_edit_form {
protected function specific_definition($mform) {
// Section header title according to language file.
$mform->addElement('header', 'configheader', get_string('blocksettings', 'block'));
// A sample string variable with a default value.
$mform->addElement('text', 'config_text', get_string('blockstring', 'block_simplehtml'));
$mform->setDefault('config_text', 'default value');
$mform->setType('config_text', PARAM_TEXT);
}
}
The first line declares a class that inherits from block_edit_form, and this allows Moodle to identify the code to execute in the configuration page. The specific_definition() method is where your form elements are defined, and these take the same format as with the standard Moodle form library. Within our specific_definition method, we have created a header, and an input field which will accept text to be used within the block.
NOTE: You might want extend language file for your block (lang/en/block_simplehtml.php) and add a value for "blockstring" defined in a code above.
IMPORTANT: All your field names need to start with "config_", otherwise they will not be saved and will not be available within the block via $this->config.
So once our instance configuration form has been saved, we can use the inputted text within the block like so:
if (! empty($this->config->text)) {
$this->content->text = $this->config->text;
}
Note that $this->config is available in all block methods except init(). This is because init() is called immediately as the block is being created, with the purpose of setting things up, so $this->config has not yet been instantiated.
Note about Checkbox: You cannot use the 'checkbox' element in the form (once set it will stay set). You must use advcheckbox instead.
The Specialists
Implementing instance configuration for the block's contents was good enough to whet our appetite, but who wants to stop there? Why not customize the block's title, too?
Why not, indeed. Well, our first attempt to achieve this is natural enough: let's add another field to /blocks/simplehtml/edit_form.php. Here goes:
// A sample string variable with a default value.
$mform->addElement('text', 'config_title', get_string('blocktitle', 'block_simplehtml'));
$mform->setDefault('config_title', 'default value');
$mform->setType('config_title', PARAM_MULTILANG);
We save the edited file, go to a course, edit the title of the block and... nothing happens! The instance configuration is saved correctly, all right (editing it once more proves that) but it's not being displayed. All we get is just the simple "SimpleHTML" title.
That's not too weird, if we think back a bit. Do you remember that init() method, where we set $this->title? We didn't actually change its value from then, and $this->title is definitely not the same as $this->config->title (to Moodle, at least). What we need is a way to update $this->title with the value in the instance configuration. But as we said a bit earlier, we can use $this->config in all methods except init()! So what can we do?
Let's pull out another ace from our sleeve, and add this small method to our block class:
public function specialization() {
if (!empty($this->config->title)) {
$this->title = $this->config->title;
} else {
$this->config->title = 'Default title ...';
}
if (empty($this->config->text)) {
$this->config->text = 'Default text ...';
}
}
Aha, here's what we wanted to do all along! But what's going on with the specialization() method?
This "magic" method has actually a very nice property: it's guaranteed to be automatically called by Moodle as soon as our instance configuration is loaded and available (that is, immediately after init() is called). That means before the block's content is computed for the first time, and indeed before anything else is done with the block. Thus, providing a specialization() method is the natural choice for any configuration data that needs to be acted upon or made available "as soon as possible", as in this case.
Now You See Me, Now You Don't
Now would be a good time to mention another nifty technique that can be used in blocks, and which comes in handy quite often. Specifically, it may be the case that our block will have something interesting to display some of the time; but in some other cases, it won't have anything useful to say. An example here would be the "Recent Activity" block, in the case where no recent activity in fact exists.
However in that case the block chooses to explicitly inform you of the lack of said activity, which is arguably useful. It would be nice, then, to be able to have our block "disappear" if it's not needed to display it.
This is indeed possible, and the way to do it is to make sure that after the get_content() method is called, the block has no content to display. This means that all fields in $this->content should be equal to the empty string (''). In the case of our HTML-based block, these fields are $this->content->text and $this->content->footer. Moodle performs this check by calling the block's is_empty() method, and if the block is indeed empty then it is not displayed at all.
Note that the exact value of the block's title and the presence or absence of a hide_header() method do not affect this behavior. A block is considered empty if it has no content, irrespective of anything else.
We Are Legion
Right now our block is fully configurable, both in title and content. It's so versatile, in fact, that we could make pretty much anything out of it. It would be really nice to be able to add multiple blocks of this type to a single course. And, as you might have guessed, doing that is as simple as adding another small method to our block class:
public function instance_allow_multiple() {
return true;
}
This tells Moodle that it should allow any number of instances of the SimpleHTML block in any course. After saving the changes to our file, Moodle immediately allows us to add multiple copies of the block without further ado!
Please note that even if a block itself allows multiple instances in the same page, the administrator still has the option of disallowing such behavior. This setting can be set separately for each block from the Administration / Configuration / Blocks page.
The Effects of Globalization
Configuring each block instance with its own personal data is cool enough, but sometimes administrators need some way to "touch" all instances of a specific block at the same time. In the case of our SimpleHTML block, a few settings that would make sense to apply to all instances aren't that hard to come up with.
For example, we might want to limit the contents of each block to only so many characters, or we might have a setting that filters HTML out of the block's contents, only allowing pure text in. Granted, such a feature wouldn't win us any awards for naming our block "SimpleHTML" but some tormented administrator somewhere might actually find it useful.
This kind of configuration is called "global configuration" and applies only to a specific block type (all instances of that block type are affected, however). Implementing such configuration for our block is quite similar to implementing the instance configuration. We will now see how to implement the second example, having a setting that only allows text and not HTML in the block's contents. To enable global configuration for the block, we create a new file, /blocks/simplehtml/settings.php, and populate it with form field definitions for each setting, which Moodle will use to generate and handle a global settings form. This is quite similar in concept to how we generated the instance configuration form earlier, but the actual code used to generate the form and fields is somewhat different.
Place the following in your settings.php file:
$settings->add(new admin_setting_heading(
'headerconfig',
get_string('headerconfig', 'block_simplehtml'),
get_string('descconfig', 'block_simplehtml')
));
$settings->add(new admin_setting_configcheckbox(
'simplehtml/Allow_HTML',
get_string('labelallowhtml', 'block_simplehtml'),
get_string('descallowhtml', 'block_simplehtml'),
'0'
));
As you can see, to generate a global configuration form, we simply create admin setting objects and add them to an array. This array is then stepped over to create the settings form, and the inputted data is automatically saved to the database. Full details of the setting types available and how to add them can be found under Admin_settings#Individual_settings.
We've now created a header and checkbox form element to accept configuration details from the site admin. You should pay extra attention to the name of our checkbox element, 'simplehtml/Allow_HTML' , as it specifies not only the name of the configuration option, but also how it should be stored. If you simply specify a name for the config option, it will be stored in the global $CFG object, and in the <prefix>_config database table. This will make your config variable available immediately via $CFG without requiring an additional database call, but will also increase the size of the $CFG object. In addition, we must prefix the variable with the name of our block, otherwise we run the risk of our config variable sharing its name with a similar variable in another plugin, or within Moodle itself.
The preferred method of storing your block's configuration data is to prefix each config variable name in your settings.php file with your block's name, followed by a slash ( / ), and the name of the configuration variable, as we have done above. If you write your settings.php file in this way, then your variables will be stored in the <prefix>_config_plugin table, under your block's name. Your config data will still be available via a get_config() call, and name collision will be impossible between plugins.
Finally, if you're wondering why there are two language tags specified for each element, the first tag is for the element's label or primary text, and the second tag is for its description. And that's it. Pretty easy, huh?
Enabling Global Configuration
Moodle 2.4
Since version 2.4, the following line must be added to the /blocks/simplehtml/block_simplehtml.php file in order to enable global configuration:
function has_config() {return true;}
This line tells Moodle that the block has a settings.php file.
NOTE FOR UPGRADERS
You'll notice that the settings.php file and parsed element names replaces Moodle's old storage method, wherein it would send all the configuration data to your block's config_save() method, and allow you to override how the data is saved. The config_save() method is no longer used in Moodle 2.x; however the instance_config_save() method is very much alive and well, as you will see shortly.
Accessing Global Config Data
Now that we have global data defined for the plugin, we need to know how to access it within our code. If you saved your config variables in the global namespace, you can access them from the global $CFG object, like so:
$allowHTML = $CFG->Allow_HTML;
If, as recommended, you saved your config variables in a custom namespace for your block, then you can access them via a call to get_config():
$allowHTML = get_config('simplehtml', 'Allow_HTML');
Rolling It All Together
OK, so we now have an instance configuration form that allows users to enter custom content text for a block, and a global configuration option that dictates whether or not that user should be allowed to enter HTML tags as part of the content. Now we just need to tie the two together. But if Moodle takes care of the form processing for our instance configuration in edit_form.php, how can we capture it and remove the HTML tags where required?
Well, fortunately, there is a way this can be done. By overriding the instance_config_save() method in our block class, we can modify the way in which instance configuration data is stored after input. The default implementation is as follows:
public function instance_config_save($data) {
$data = stripslashes_recursive($data);
$this->config = $data;
return set_field('block_instance',
'configdata',
base64_encode(serialize($data)),
'id',
$this->instance->id);
}
This may look intimidating at first (what's all this stripslashes_recursive() and base64_encode() and serialize() stuff?) but do not despair; we won't have to touch any of it. We will only add some extra validation code in the beginning and then instruct Moodle to additionally call this default implementation to do the actual storing of the data. Specifically, we will add a method to our class which goes like this:
public function instance_config_save($data) {
if(get_config('simplehtml', 'Allow_HTML') == '1') {
$data->text = strip_tags($data->text);
}
// And now forward to the default implementation defined in the parent class
return parent::instance_config_save($data);
}
(This example assumes you are using a custom namespace for the block.)
At last! Now the administrator has absolute power of life and death over what type of content is allowed in our "SimpleHTML" block! Absolute? Well... not exactly. In fact, if we think about it for a while, it will become apparent that if at some point in time HTML is allowed and some blocks have saved their content with HTML included, and afterwards the administrator changes the setting to "off", this will only prevent subsequent content changes from including HTML. Blocks which already had HTML in their content would continue to display it!
Following that train of thought, the next stop is realizing that we wouldn't have this problem if we had chosen the lazy approach a while back, because in that case we would "sanitize" each block's content just before it was displayed.
The only thing we can do with the eager approach is strip all the tags from the content of all SimpleHTML instances as soon as the admin setting is changed to "HTML off"; but even then, turning the setting back to "HTML on" won't bring back the tags we stripped away. On the other hand, the lazy approach might be slower, but it's more versatile; we can choose whether to strip or keep the HTML before displaying the content, and we won't lose it at all if the admin toggles the setting off and on again. Isn't the life of a developer simple and wonderful?
Exercise
We will let this part of the tutorial come to a close with the obligatory exercise for the reader: In order to have the SimpleHTML block work "correctly", find out how to strengthen the eager approach to strip out all tags from the existing configuration of all instances of our block, or go back and implement the lazy approach instead. (Hint: Do that in the get_content() method.)
Eye Candy
Our block is just about complete functionally, so now let's take a look at some of the tricks we can use to make its behavior customized in a few more useful ways.
First of all, there are a couple of ways we can adjust the visual aspects of our block. For starters, it might be useful to create a block that doesn't display a header (title) at all. You can see this effect in action in the Course Description block that comes with Moodle. This behavior is achieved by, you guessed it, adding one more method to our block class:
public function hide_header() {
return true;
}
One more note here: we cannot just set an empty title inside the block's init() method; it's necessary for each block to have a unique, non-empty title after init() is called so that Moodle can use those titles to differentiate between all of the installed blocks.
Next, we can affect some properties of the actual HTML that will be used to print our block. Each block is fully contained within a <div> or <table> elements, inside which all the HTML for that block is printed. We can instruct Moodle to add HTML attributes with specific values to that container. This is generally done to give us freedom to customize the end result using CSS (this is in fact done by default as we'll see below).
The default behavior of this feature in our case will modify our block's "class" HTML attribute by appending the value "block_simplehtml" (the prefix "block_" followed by the name of our block, lowercased). We can then use that class to make CSS selectors in our theme to alter this block's visual style (for example, ".block_simplehtml { border: 1px black solid}").
To change the default behavior, we will need to define a method which returns an associative array of attribute names and values. For example:
public function html_attributes() {
$attributes = parent::html_attributes(); // Get default values
$attributes['class'] .= ' block_'. $this->name(); // Append our class to class attribute
return $attributes;
}
This results in the block having all its normal HTML attributes, as inherited from the base block class, plus our additional class name. We can now use this class name to change the style of the block, add JavaScript events to it via YUI, and so on. And for one final elegant touch, we have not set the class to the hard-coded value "block_simplehtml", but instead used the name() method to make it dynamically match our block's name.
Authorized Personnel Only
Some blocks are useful in some circumstances, but not in others. An example of this would be the "Social Activities" block, which is useful in courses with the "social" course format, but not courses with the "weeks" format. What we need to be able to do is limit our block's availability, so that it can only be selected on pages where its content or abilities are appropriate.
Moodle allows us to declare which page formats a block is available on, and enforces these restrictions as set by the block's developer at all times. The information is given to Moodle as a standard associative array, with each key corresponding to a page format and defining a boolean value (true/false) that declares whether the block should be allowed to appear in that page format.
Notice the deliberate use of the term page instead of course in the above paragraph. This is because in Moodle 1.5 and onwards, blocks can be displayed in any page that supports them. The best example of such pages are the course pages, but we are not restricted to them. For instance, the quiz view page (the first one we see when we click on the name of the quiz) also supports blocks.
The format names we can use for the pages derive from the name of the script which is actually used to display that page. For example, when we are looking at a course, the script is /course/view.php (this is evident from the browser's address line). Thus, the format name of that page is course-view. It follows easily that the format name for a quiz view page is mod-quiz-view. This rule of thumb does have a few exceptions, however:
- The format name for the front page of Moodle is site-index.
- The format name for courses is actually not just course-view; it is course-view-weeks, course-view-topics, etc.
- Even though there is no such page, the format name all can be used as a catch-all option.
We can include as many format names as we want in our definition of the applicable formats. Each format can be allowed or disallowed, and there are also three more rules that help resolve the question "is this block allowed into this page or not?":
- Prefixes of a format name will match that format name; for example, mod will match all the activity modules. course-view will match any course, regardless of the course format. And finally, site will also match the front page (remember that its full format name is site-index).
- The more specialized a format name that matches our page is, the higher precedence it has when deciding if the block will be allowed. For example, mod, mod-quiz and mod-quiz-view all match the quiz view page. But if all three are present, mod-quiz-view will take precedence over the other two because it is a better match.
- The character * can be used in place of any word. For example, mod and mod-* are equivalent. At the time of this document's writing, there is no actual reason to utilize this "wildcard matching" feature, but it exists for future usage.
- The order that the format names appear does not make any difference.
All of the above are enough to make the situation sound complex, so let's look at some specific examples. First of all, to have our block appear only in the site front page, we would use:
public function applicable_formats() {
return array('site' => true);
}
Since all is missing, the block is disallowed from appearing in any course format; but then site is set to TRUE, so it's explicitly allowed to appear in the site front page (remember that site matches site-index because it's a prefix).
For another example, if we wanted to allow the block to appear in all course formats except social, and also to not be allowed anywhere but in courses, we would use:
public function applicable_formats() {
return array(
'course-view' => true,
'course-view-social' => false);
}
This time, we first allow the block to appear in all courses and then we explicitly disallow the social format. For our final, most complicated example, suppose that a block can be displayed in the site front page, in courses (but not social courses) and also when we are viewing any activity module, except quiz. This would be:
public function applicable_formats() {
return array(
'site-index' => true,
'course-view' => true,
'course-view-social' => false,
'mod' => true,
'mod-quiz' => false
);
}
It is not difficult to realize that the above accomplishes the objective if we remember that there is a "best match" policy to determine the end result.
Responding to Cron
It is possible to have our block respond to the global Moodle cron process; we can have a method that is run at regular intervals regardless of user interaction. There are two parts to this. Firstly we need to define a new function within our block class:
public function cron() {
mtrace( "Hey, my cron script is running" );
// do something
return true;
}
NOTE: If you don't return true in your cron function, the lastcron field in the blocks table will not be updated and as a result, your cron function will be called every time the cron runs!
Then we will need to set the (minimum) execution interval for our cron function. Prior to Moodle 2.0, this was achieved by setting the value of a block's $this->cron field, via the init() method. This is now achieved by adding an additional line to our /blocks/simplehtml/version.php file. Open it up and add the following:
$plugin->cron = 300;
Cron intervals are set in seconds, so the above line will set our minimum execution interval to 5 minutes. However, the function can only be called as frequently as cron has been set to run in the Moodle installation. So if our block is set to wait at least 5 minutes between runs, as in this example, but Moodle's cron system is only set to run every 24 hours, then our block is going to be waiting a lot longer between runs than we expected!
Remember that if we change any values in the version file or block file we must bump the version number and visit the Notifications page to upgrade the block, otherwise they will be ignored.
NOTE: The block cron is designed to call the cron script for that block type only. That is, cron does not care about individual instances of blocks. Inside your cron function $this is defined, but it has almost nothing in it (only title and content fields are populated). If you need to execute cron for individual instances it is your own responsibility to iterate over them in the block's cron function. Example:
public function cron() {
global $DB; // Global database object
// Get the instances of the block
$instances = $DB->get_records( 'block_instance', array('blockid'=>'simplehtml') );
// Iterate over the instances
foreach ($instances as $instance) {
// Recreate block object
$block = block_instance('simplehtml', $instance);
// $block is now the equivalent of $this in 'normal' block
// usage, e.g.
$someconfigitem = $block->config->item2;
}
}
TIP: This also means that creating a block is a possible way to create code that can respond to cron with a reasonably low overhead. No actual instances of the block are required.
Additional Content Types
Lists
In this final part of the guide we will briefly discuss several additional capabilities of Moodle's block system, namely the ability to create blocks that display different kinds of content to the user. The first of these creates a list of options and displays them to the user. This list is displayed with one item per line, and an optional image (icon) next to the item. An example of such a list block is the standard Moodle "admin" block, which illustrates all the points discussed in this section.
As we have seen so far, blocks use two properties of $this->content: "text" and "footer". The text is displayed as-is as the block content, and the footer is displayed below the content in a smaller font size. List blocks use $this->content->footer in the exact same way, but they ignore $this->content->text.
Instead, Moodle expects such blocks to set two other properties when the get_content() method is called: $this->content->items and $this->content->icons. $this->content->items should be a numerically indexed array containing elements that represent the HTML for each item in the list that is going to be displayed. Usually these items will be HTML anchor tags which provide links to some page. $this->content->icons should also be a numerically indexed array, with exactly as many items as $this->content->items has. Each of these items should be a fully qualified HTML <img> tag, with "src", "height", "width" and "alt" attributes. Obviously, it makes sense to keep the images small and of a uniform size. We would recommend standard 16x16 images for this purpose.
In order to tell Moodle that we want to have a list block instead of the standard text block, we need to make a small change to our block class declaration. Instead of extending class block_base, our block will extend class block_list. For example:
class block_my_menu extends block_list {
// The init() method does not need to change at all
}
In addition to making this change, we must of course also modify the get_content() method to construct the $this->content variable as discussed above:
public function get_content() {
if ($this->content !== null) {
return $this->content;
}
$this->content = new stdClass;
$this->content->items = array();
$this->content->icons = array();
$this->content->footer = 'Footer here...';
$this->content->items[] = html_writer::tag('a', 'Menu Option 1', array('href' => 'some_file.php'));
$this->content->icons[] = html_writer::empty_tag('img', array('src' => 'images/icons/1.gif', 'class' => 'icon'));
// Add more list items here
return $this->content;
}
To summarise, if we want to create a list block instead of a text block, we just need to change the block class declaration and the get_content() method. Adding the mandatory init() method as discussed earlier will then give us our first list block in no time!
Trees
As of 23rd December 2011, this functionality remains inoperable in all Moodle 2.x versions. It appears that classes are missing from the code base. This has been added to the tracker at the URL below. Please upvote this issue if you require this functionality.
Visit this issue on the tracker @ MDL28289
Database support
In case we need to have a database table that holds some specific information used within our block, we will need to create the file /blocks/simplehtml/install.xml with the table schema contained within it.
To create the install.xml file, use the XMLDB editor. See Database FAQ > XMLDB for further details.
Up-to-date documentation on upgrading our block, as well as providing new capabilities and events to the system, can be found under Installing and Upgrading Plugin Database Tables
See also
- Blocks Advanced A continuation of this tutorial.
- Unit 7 of the Introduction to Moodle Programming course is a follow up to this course. (But you should follow the forum discussions of that course closely as there are still some bugs and inconsistencies.)
- Daniel Neis Araujo's NEWBLOCK template.
Appendices
The appendices have been moved to separate pages:
- Appendix A: block_base Reference