Customization example of Moodle database activity: Implement a Leave Management System


The Moodle database activity module is extremely versatile. I have implemented an e-book library, an application form, a simple employee management system (acting as a simple HR module to keep track of employee documents and leave management), etc. All of these use stock Moodle code and just need some JavaScript templating.

I wanted to make the leave management system a little bit more functional but I couldn’t quite achieve the increased functionality using the core or any of the 3rd party plugins available .

This meant that: either I had to write my own plugin, or modify an existing plugin, or modify core. Writing a new plugin in Moodle is not a trivial task especially where extensive user interaction with forms is required.  Modification of a 3rd party plugin such as say the dataform plugin has several issues: the plugin may not track Moodle core changes and or be potentially abandoned by the plugin creator. Modification of existing core code is dangerous since it impacts all instances of this module and it will be difficult to maintain these changes with future Moodle core releases .

In light of all these considerations I decided to modify the database activity module core itself. However, I wanted to ensure that stock functionality of this module would not be impacted and that my custom functionality requirements for a leave management system will be achieved and code maintenance will be possible with future releases of Moodle core code. I will describe how I accomplished this below.

Description of my Leave Management System

An employee gets X days of general leave called Casual leave and Y days of Sick leave at the beginning of every academic year and this gets updated as the leave is taken. The employee submits a leave request as a database record with details such as: number of days of leave, type of leave, dates of absence, and finally, reason for leave. The problem I faced was: How to get information about the available leave bank X, Y of the employee? In the Moodle database activity module, there is really no way to manipulate form data except using JavaScript and perhaps, by looking at all submitted applications of each employee, one can adjust the leave banks but this seemed too complicated and clunky. An easier solution was to modify the core code. I was lazy to make this into a plugin (where I would have to change all the names, etc. Also this would add yet another item to the already busy add activity menu).

Architecture of implementation

  1. Every employee will use 2 user profile fields (new or re-purposed existing unused fields) to store values of her leave banks. Leave bank information is read from these fields and after adjusting for requested leave these values are updated. This data is “outside” the database activity module.
  2. Every instance of the database activity module shall have “leave management” as part of its name. This will be the condition used for conditional processing. An example name of an instance would be: School XYZ leave management system
  3. We add 2 new functions called adjust_existing_leave and adjust_new_leave in the file /locallib.php.
  4. Adding a new entry as well as editing a new entry takes place in a file called /edit.php. So we make 2 very small edits to this file which are basically conditional calls to our new functions added to the locallib.php. The condition is mentioned in item 2 above.
  5. By keeping changes to the core (edit.php) minimal and by adding all our functionality as separate functions to our local library (locallib.php) and only calling them conditionally, we ensure that this activity module can be used as you normally would (without being perturbed by our additions). In addition, by making the conditional calls to our functions we accomplish our custom functionality.
  6. These changes are available in this repository that attempts to track Moodle’s latest release. We basically merge our changes with every upstream Moodle official release that we are interested in.

As illustrations, here are the 2 conditional calls inserted into the edit.php file (shown in red).

} else {
// No recordid was specified - creating a new entry

// Retrieve the format for the fields.
$fields = $DB->get_records('data_fields', array('dataid' => $datarecord->d));

// Validate the form to ensure that enough data was submitted.
$processeddata = data_process_submission($data, $fields, $datarecord);

// Add the new notification data.
$generalnotifications = array_merge($generalnotifications, $processeddata->generalnotifications);
$fieldnotifications = array_merge($fieldnotifications, $processeddata->fieldnotifications);
// customization START

if (stripos($data->name, ' leave management ') !== false) {

// before inserting the submitted data as record adjust leave banks based on form

adjust_new_leave($data, $datarecord);


// customization END

// Add instance to data_record.
if ($processeddata->validated && $recordid = data_add_record($data, $currentgroup)) {

The above is the 1st conditional call for a new application. It is placed at an appropriate place in the edit.php file as can be seen (just before form is added as new database record)

The 2nd conditional call is shown below:

if ($rid) {
// Updating an existing record.

// Retrieve the format for the fields.
$fields = $DB->get_records('data_fields', array('dataid' => $datarecord->d));

// Validate the form to ensure that enough data was submitted.
$processeddata = data_process_submission($data, $fields, $datarecord);

// Add the new notification data.
$generalnotifications = array_merge($generalnotifications, $processeddata->generalnotifications);
$fieldnotifications = array_merge($fieldnotifications, $processeddata->fieldnotifications);

if ($processeddata->validated) {
// Enough data to update the record.
// customization START

if (stripos($data->name, ' leave management ') !== false) {

// I am assuming that the owner of the record is editing her entry and not the manager :-)

adjust_existing_leave($data, $datarecord);


// Customization END

data_update_record_fields_contents($data, $record, $context, $datarecord, $processeddata);
core_tag_tag::set_item_tags('mod_data', 'data_records', $rid, $context, $tags);

$viewurl = new moodle_url('/mod/data/view.php', array(
'd' => $data->id,
'rid' => $rid,

Code development and debugging

I learned several things while doing this customization:

It is very important to know Moodle’s objects in the code that you are dealing with. Just reading the code is difficult. It is best to run using a debugger to examine variables at any point desired. This is not that easy. You need an IDE and use xdebug and these add another learning curve. I do my debugging by logging variables to my code wherever I want to see the structure and value of my variables.

For example, in the present case, to understand the structure of $datarecord I used: error_log(print_r($datarecord,true));

and I got the following output in my var/log/apache2/error.log file:


stdClass Object


[d] => 3

[rid] => 23

[sesskey] => k8b2L7FfO6

[field_44] => 3

[field_45] => 2

[field_30] => 1

[field_31_day] => 2

[field_31_month] => 11

[field_31_year] => 2018

[field_32_day] => 2

[field_32_month] => 11

[field_32_year] => 2018

[field_34] => sick

[field_46] => 3

[field_47] => 1

[field_39] => Sick leave

[field_41] => admin001

[saveandview] => Save and view


I was able to determine that the fieldid was concatenated with “field_” to built the “keys” holding the content of submitted data and that gave me the clue I needed.

This kind of investigation together with looking at Moodle’s tables using something like phpMyAdmin enabled me understand the data structures and put together my code. I used Notepad++ as the code editor, GitHub desktop as my version control with the above debugging techniques, nothing fancy!


Posted in customization, moodle, plugin and tagged , , .