No after_restore() for course formats in Moodle 2?

No after_restore() for course formats in Moodle 2?

by Urs Hunkler -
Number of replies: 7
Picture of Core developers

After adding a table to a new Moodle 2 course format which stores activity instance IDs these activity instance IDs need to be updated after a course restore to the new activity instance IDs.

Backup works as expected and restore creates the new course database rows. Only the activity instance IDs are the old and therefore wrong ones.

In the docs the after_restore() function is mentioned for the task to update activity IDs. For example quiz blocks use this function. After digging deep into the restore process I come to the conclusion that the after_restore() function is not implemented in the restore_plugin class and therefore is not available.

How can I update the activity instance IDs in a course format in the restore process?

Thank you very much for your help.

Average of ratings: -
In reply to Urs Hunkler

Re: No after_restore() for course formats in Moodle 2?

by Urs Hunkler -
Picture of Core developers
Can anybody please help?
In reply to Urs Hunkler

Re: No after_restore() for course formats in Moodle 2?

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Backup and restore hooks were not present in the original 2.0 release. Eloy added them in 2.0.? when prompted by my colleague Jenny Gray.

Let me find the tracker issue ... MDL-22146

I don't know how much docs there is.

In reply to Tim Hunt

Re: No after_restore() for course formats in Moodle 2?

by Urs Hunkler -
Picture of Core developers
Thank you very much for the background information Tim. There is no documentation about plugin backup as far as I found out. There is the OU Theme DB backup docu. But that doesn't cover adopting ids of course modules after backup.

So Eloy is the only one who knows the details - I'll ping him and hope he may find the time to answer.
In reply to Urs Hunkler

Re: No after_restore() for course formats in Moodle 2?

by Eloy Lafuente (stronk7) -
Picture of Core developers Picture of Documentation writers Picture of Moodle HQ Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Hi Urs,

thanks for pinging!

Yes, all restore steps (modules, blocks), plugins (themes, course formats, qtypes...) and subplugins (assignment types, workshop evaluations, data fields...) have support, on restore, for hooking code in the "after_execute" methods.

So, once one XML file has been completely parsed, all the available "after_execute" methods are invoked, first the ones present in plugins and subplugins and, finally the one in the restore_step itself (that you have not access to modify, it is core).

So I guess that you will need (for course formats, having 3 "hooks" defined) one/some methods like:

  • after_execute_course()
  • after_execute_section()
  • after_execute_module()

in your course format restore implementation, and all them will be executed, if available, at the end of the process of the XML file, when everything is already loaded in DB, ready for your extra handling. The "course" one will be executed (once) after loading the course.xml information, the "section" (multiple) after loading each section.xml and the "module" (multiple) after loading each module.xml.

For reference, I think that, right now, we are using those after_execute_xxx() methods only in some qtype plugins, look for "after_execute_question" for them.

Also, you can take a look to "launch_after_execute_methods" functions, where the hook mechanism is somehow explained, just in case you are interested.

And that's all I can say, note "after_execute" methods are different from "after_restore" ones because the formers are executed after one XML (restore step) has been processed, so you only can handle information processed by that XML. In the other side, the laters are executed at the end of the whole restore process (but plugins & subplugins have not access to them at all, only modules - complete restore_step citizens - have). At least until now. wink

And that's all, hope it helps...ciao smile

Average of ratings: Useful (2)
In reply to Eloy Lafuente (stronk7)

Re: No after_restore() for course formats in Moodle 2?

by Urs Hunkler -
Picture of Core developers
Thanks for your detailed answer Eloy.

After I had checked the "after_execute_question" method before asking here I tried the "after_execute_" hooks but without success.

After reading your explanation I guess I need to implement "after_execute_xxx" routines for all modules and correct the database fields with the new IDs the modules have gotten and can tell about in the "after_execute_" method.

Does anybody know a way to use one method to catch all "after_execute_xxx" calls for all modules?
In reply to Urs Hunkler

Re: No after_restore() for course formats in Moodle 2?

by Eloy Lafuente (stronk7) -
Picture of Core developers Picture of Documentation writers Picture of Moodle HQ Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Hi Urs,

I think something is incorrect in your plan above: "I guess I need to implement "after_execute_xxx" routines for all modules...". You don't need to make anything within modules, but within the course format plugin.

Here it is one simple example, hope it helps to clarify the thing a bit:

Let's imagine one course format, named "pinkboard". It is one ultra-cool course format allowing you to move the activities to any position in the course area, to get things visually different. So, let's assume we need one DB table where we store the x and y positions for each module. The table would be something like this:

CREATE TABLE mdl_pinkboard_positions
    id bigint(10) unsigned NOT NULL AUTO_INCREMENT,
    cmid bigint(10) unsigned NOT NULL,
    posx int(5) unsigned NOT NULL,
    posy int(5) unsigned NOT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY cmid (cmid)
)

So, for each modid, we have one posx and posy defining the position of the module in the course page.

And now we want to backup and restore that information, let's do it.

The first (important) thing to decide is where are we going to store that information. Course format plugins have 3 possible places where information can be added (at course level, at section level and at module level). Our example is clearly module-related (modid based). So we want to store it at module level, decided.

Once we have decided that point, it's time to implement the backup plugin.  It would be something like this:

File: course/format/pinkboard/backup/moodle2/backup_format_pinkboard_plugin.class.php

<?php

class backup_format_pinkboard_plugin extends backup_format_plugin {

    /**
     * Returns the course format info to add to module hook
     */
    protected function define_module_plugin_structure() {

        // Define virtual plugin element
        $plugin = $this->get_plugin_element(null, $this->get_format_condition(), 'pinkboard');

        // Create plugin container element with standard name
        $pluginwrapper = new backup_nested_element($this->get_recommended_name());

        // Add wrapper to plugin
        $plugin->add_child($pluginwrapper);

        // Note we really don't need modid (because parent info in module.xml already has it
        // but we put it here too (dupe info for sure)
        $position = new backup_nested_element('position', null, array('cmid', 'posx', 'posy'));

        $pluginwrapper->add_child($position);

        $position->set_source_table('pinkboard_positions', array('cmid' => backup::VAR_MODID));

        return $plugin;
    }
}

And that will cause all the activities/xxxxx/module.xml file to look like this (with the plugin information attached in place):

<?xml version="1.0" encoding="UTF-8"?>
<module id="31" version="2010111500">
  <modulename>forum</modulename>
  <sectionid>13</sectionid>
  ...
  ...
  <plugin_format_pinkboard_module>
    <position>
      <cmid>31</cmid>
      <posx>100</posx>
      <posy>100</posy>
    </position>
  </plugin_format_pinkboard_module>
  ...
  ...
</module>

More yet, if you look to that code, you will notice that the modid is already being backup in the very first tag of the XML file above (id = 31), and we are (not needed at all) storing it again within our plugin data. As far as we selected the proper level where our information is stored (module), we don't need to include that info (the course module id = 31) again.

Said that, let's see how restore would be:

File: course/format/pinkboard/backup/moodle2/restore_format_pinkboard_plugin.class.php

<?php

class restore_format_pinkboard_plugin extends restore_format_plugin {

    // Define the type of information we are going to process at module level
    protected function define_module_plugin_structure() {

        $elepath = $this->get_pathfor('/position');

        return array(new restore_path_element('position', $elepath));
    }

    public function process_position($data) {
        global $DB;

        $data = (object)$data;
        // Aha, we really don't need to "after_execute" anything.
        // The mapping between original and new cmid is already
        // available via API. Let's use it!
        $data->cmid = $this->task->get_moduleid();

        $DB->insert_record('pinkboard_positions', $data);
    }

    // We really don't need this, because the process method already
    // has been able to get the new cmid using available API. Anyway,
    // this can be interesting to process "other" things.
    public function after_execute_module() {
        // Nothing to do
    }
}

Said that, we just define the paths the plugin is going to restore at module level (define_module_plugin_structure), the process function for it (process_position) and the after_execute method that will be executed at module level (after_execute_module).

But look carefully, as commented above, the cmid in our backup is completely not used, because the old to new cmid conversion has been already performed and the new cmid is available using the own restore API with (get_moduleid), making the after_execute_module method 100% not necessary.

So, summarizing, the fist step is to have a good DB implementation, with each table pointing (being linked) with one course/section/course module id. And then, create the backup & restore plugins at the correct levels always.

If that happens, then, surely, you won't need to use those after_execute methods too much, because the information you need to convert has been already converted and is available via restore API.

Of course you can need those methods to process other information like other cmids or whatever, but it is highly dependent of how you've structured your information.

And that's all, I hope this simple (but potentially real) implementation of one course format plugin is useful to understand a bit more how it works. As said at the beginning, no need to implement anything within modules nor something similar. The information should be auto-contained and processed, exclusively by the (backup and restore) plugins. If that doesn't work I'd feel initially biased to think that something in the DB design of your format needs review in order to get everything properly attached to the available hooks (course, section and module).

Ciao smile

Average of ratings: Useful (1)
In reply to Eloy Lafuente (stronk7)

Re: No after_restore() for course formats in Moodle 2?

by Urs Hunkler -
Picture of Core developers

Eloy, the tutorial you give here shows that my approach to implement the module related information on the course level in the DB and the backup/restore process was not right. But I didn't know that I can implement this information in a course format on module level. And I didn't find examples.

Following your tutorial the implementation looks so simple and logical.

Thanks so much.