Local plugin doesn't fire callback function

Local plugin doesn't fire callback function

by Nicola Vallinoto -
Number of replies: 19


Dear Moodle developers,

I'm searching to write a local plugin whose aim is to update an external database (with user, course and date of completion) after a user completes a course.

I followed instructions from https://moodledev.io/docs/apis/plugintypes/local and other sources. After having checked if other discussion on the topic are available I'm asking you where I'm doing a mistake in writing the code. 

Here are the steps.  Fyi I see my local plugin as a listener of the course completed event. But when I complete a course nothing is fired and no error is displayed. 

Perhaps I missed one step or what else. If you can have a look to the code it will be an help.

Thank you.

- Definition of the event observer

In the file /plugin_folder/db/events.php is defined the hook to the event.


$observers = array (

array (

'eventname' => '\core\event\course_completed',

'callback' => 'local_coursecompletion\local_coursecompletion_observer::external_table_update',

));


- Definition of the callback function called after event triggering


The callback function is defined in the file /plugin_folder/db/observer.php

namespace local_coursecompletion;

include("/plugin_folder/coursecompletion/lib.php");

class local_coursecompletion_observer {

public static function external_table_update(\core\event\course_completed $eventdata) {

global $CFG;

global $DB;

$courseid=$eventdata->courseid;

$userid=$eventdata->userid;

$date=$eventdata→datecompletion;

// external db update

$sync_ext_db=local_coursecompletion_sync_external_table($userid, $courseid, $date);

// event triggering external db updates to log system

$event= \local_coursecompletion\event\external_db_updated::create(array(

'userid' => $userid,

'courseid' => $courseid

));

$event->add_record_snapshot('user', $userid);

$event->add_record_snapshot('course', $courseid);

$event->trigger();

}


- Definition of the function updating external database

In the file /plugin_folder/lib.php

function local_coursecompletion_sync_external_table($userid, $courseid, $datecompletion) : int {

error_reporting( -1);

ini_set('display_errors', 1 );

$conn = oci_connect('user', 'pwd', 'db');

if (!$conn) {

$e = oci_error();

trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);

return -1;

}

$sql = "UPDATE prova_geco_moodle SET campo3 = '12-JAN-23' WHERE codice_corso = 'a1' AND utente = 'vallinoto_ld' ";

$stid = oci_parse($conn, $sql);

if (!$stid) {

$e = oci_error();

trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);

return -2;

}

oci_free_statement($stid);

oci_close($conn);

return 0;

}


- Definition of the event \local_coursecompletion\event\external_db_updated

In the file plugin_folder/classes/event/external_db_updated.php

namespace local_coursecompletion\event;

class external_db_updated extends \core\event\base {

protected function init() {

$this->data['crud'] = 'u'; // c(reate), r(ead), u(pdate), d(elete)

$this->data['edulevel'] = self::LEVEL_OTHER;

$this->data['objecttable'] = 'GECO_MOODLE';

$this->data['description'] = 'Descrizione evento';

}

public static function get_name() {

return get_string('external_db_updated', 'local_coursecompletion');

}

public function get_description() {

return "The user with id {$this->userid} updated the external table GECO_MOODLE with id {$this->objectid}.";

}


}


Average of ratings: -
In reply to Nicola Vallinoto

Re: Local plugin doesn't fire callback function

by Renaat Debleu -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers
The observer file is not in the right folder (db/observer.php), it should be in the classes directory (classes/observer.php)

A working observer that listens to the course_completed event can be seen in the enrol_coursecompleted plugin (https://github.com/ewallah/moodle-enrol_coursecompleted)
Average of ratings: Useful (2)
In reply to Renaat Debleu

Ri: Re: Local plugin doesn't fire callback function

by Nicola Vallinoto -
Hi Renat, yes it is in classes directory. I made a mistake recopying it.
In reply to Renaat Debleu

Ri: Re: Local plugin doesn't fire callback function

by Nicola Vallinoto -
Hi Renaat,
thank you for sharing the working observer to the course_completed event. I checked your code and mine. They are the same but still my callback function is not fired. I summarized in the following lines the hook point and the callback function. Hope you can find give me any suggestion to go forward. As already told you I see my local observer to course completed event. And I see the course completed event in live log. But nothing happened after that.
Thanks again.
Nicola


1)in local/coursecompletion/db/events.php

$observer = [
[
'eventname' => '\core\event\coursecompleted',
'callback' => 'local_coursecompletion_observer\external_table_update',
]]

2) in local/coursecompletion/classes/observer.php

class local_coursecompletion_observer

public static function external_table_update(\core\event\coursecompleted $eventdata) {
$courseid=$eventdata->course;
$userid=$eventdata->userid;
$date=$eventdata->timecompleted;
// external db update
$sync_ext_db=local_coursecompletion_sync_external_table($userid, $courseid, $date);
// event triggering external db updates to log system
$event= \local_coursecompletion\event\external_db_updated::create(array(
'userid' => $userid,
'courseid' => $courseid
));
$event->trigger();
}

}
In reply to Nicola Vallinoto

Re: Ri: Re: Local plugin doesn't fire callback function

by Andreas Grabs -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Translators
Hi Nicola,

there might be a typo in your event.php.

the callback line
'callback' => 'local_coursecompletion_observer\external_table_update',
should be
'callback' => 'local_coursecompletion_observer::external_table_update',

Best regards
Andreas
In reply to Andreas Grabs

Ri: Re: Ri: Re: Local plugin doesn't fire callback function

by Nicola Vallinoto -
Hi Andreas,
thank you for your review.
Yes I used the right typo (sorry for mistake in copying it):
'callback' => 'local_coursecompletion_observer::external_table_update',
and note that I used observers (not observer) array as follows:

$observers = [
[
'eventname' => '\core\event\coursecompleted',
'callback' => 'local_coursecompletion_observer::external_table_update',
]]


Nicola
In reply to Nicola Vallinoto

Re: Ri: Re: Local plugin doesn't fire callback function

by Renaat Debleu -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers

Hello Nicola,

can you share your source code so we do not have typo issues?

Code like include("/plugin_folder/coursecompletion/lib.php"); will normally not work in Moodle.

 

 

 

Average of ratings: Useful (1)
In reply to Renaat Debleu

Ri: Re: Ri: Re: Local plugin doesn't fire callback function

by Nicola Vallinoto -
I removed the include ("/plugin_folder/coursecompletion/lib.php") and moved the logic of the external db udpdate inside the observer.php file but the result is the same. I include to this reply the two files as I have now with the last try.
\local\coursecompletion\db\events.php
and
\local\coursecompletion\classes\observer.php
hope can help to have the situation.
I tried also to reach the callback function using the namespace but with no results.
In some case I found people using locallib.php to include the callback function..
What I think is that the autoloading class loading is not working as expected, in some page I found that the class name should correspond to the file name. In the observer.php file the name of the class is local_coursecompletion_observer different form the file name.
Should be some typo too.. 



In reply to Nicola Vallinoto

Re: Ri: Re: Ri: Re: Local plugin doesn't fire callback function

by Mark Sharp -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers

Two things in your events.php file

  1. you don't include the namespace in the callback
  2. there's a typo in the callback function name "external_table_udpate" instead of "external_table_update"

So the callback should be "\local_coursecompleted\local_coursecompleted_observer::external_table_update"

Average of ratings: Useful (1)
In reply to Mark Sharp

Ri: Re: Ri: Re: Ri: Re: Local plugin doesn't fire callback function

by Nicola Vallinoto -
Hi Mark,
thank for your suggestion.
After that change the problem persisted. At the end I found the trick.
The file observer.php in local\coursecompletion\classes
should be changed in
local_coursecompletion_observer.php
with the same name of the class. It is the correct way for autoloading class.
After that I succeded to fire the course completion core event.
Now I'm searching to get data from $event but when I try to visualize with json_encode it will return an error depending on some null values.
The same operation is working well with another event I fired 
\core\event\user_updated
I used the following instructions in the callback function declared in observer php file
public static function user_update(\core\event\user_updated $event) {
   $event_data=$event->get_data();
   var_dump(json_encode($event_data));
}
The same code is returning error with course completed event.
I need to find a function normalizing the object.
In reply to Nicola Vallinoto

Re: Ri: Re: Ri: Re: Local plugin doesn't fire callback function

by Renaat Debleu -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers

In your observer file, you also have an error:

$sql = 'UPDATE prova_geco_moodle SET campo3 = '09-GEN-22' WHERE campo1 = 3'

should be something like

$sql = "UPDATE prova_geco_moodle SET campo3 = '09-GEN-22' WHERE campo1 = 3";

Try to use the codechecker (https://moodle.org/plugins/local_codechecker) plugin or run moodle-plugin-ci in Github Actions so these errors will be detected by a machine.

Average of ratings: Useful (1)
In reply to Renaat Debleu

Ri: Re: Ri: Re: Ri: Re: Local plugin doesn't fire callback function

by Nicola Vallinoto -
thank you Renaat for suggesting the codechecker plugin. I've installed it and searching any code review to apply to the code. Very very useful tool.
I succeded to update a table inside moodle in a callback function. If I try to update an external db (with the code you have checked) I receive no errors but the table is not updated. Don't know why. The same code is working in another php file inside moodle that is doing only the update sql operation (without coming from event firing) .
In reply to Renaat Debleu

Ri: Re: Ri: Re: Ri: Re: Local plugin doesn't fire callback function

by Nicola Vallinoto -
Hi Renaat, I succeded to update the external db. What is missing now is the logging of the local plugin in Log and Live Log reports and the visualization with json_encode of the event data with null values. After that I will close this thread publishing a comment with the final solution can help other Moodlers at their first experience to write a plugin. I appreciate a lot all useful helps in this thread.
In reply to Nicola Vallinoto

Re: Ri: Re: Ri: Re: Local plugin doesn't fire callback function

by Renaat Debleu -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers
Another typo:
public static function external_table_update(\core\event\course_completed &eventdata) {
should be
public static function external_table_update(\core\event\course_completed $eventdata) {
Average of ratings: Useful (1)
In reply to Renaat Debleu

Ri: Re: Ri: Re: Ri: Re: Local plugin doesn't fire callback function

by Nicola Vallinoto -
Hi Renaat,
I succeded to update a local table of moodle but not yet an external db table. I will search why.
In the meantime I would like to manage a log of the local event in a way I can see from admin report Log or Live logs.
For this aim I create an event triggered in the callback function (of course completed event).
$event=\local_coursecompletion\event\external_db_udpated::create(array(
'userid => $userid,
'courseid => $courseid
));
$event->trigger();

the code is not giving any error but in the two logs is not visible any item regarding it.
I defined it in the 
\local_coursecompletion\classes\event folder.
with init(), get_name() and get_description() functions

In reply to Renaat Debleu

Ri: Re: Ri: Re: Ri: Re: Local plugin doesn't fire callback function

by Nicola Vallinoto -
Hi,

I wrote two different callback function. One for user_updated event and the second one for course_completed event. I'm trying to visualize the $event object after the two event are fired in order to use the value to update an external db.
In the first case user_update event I obtain all the values while in the second case course_completed event I receive an error. Here I copy the code of the two callback functions and the results of var_dump.

- file location:
local\coursecompletion\classes\local_coursecompletion_observer.php
- file code: 
namespace local_coursecompletion:
class local_coursecompletion_observer {
public static function external_table_update(\core\event\course_completed $event) {
$eventdata = $event->get_data();
var_dump(json_encode($eventdata));
}
public static function user_update(\core\event\user_updated $event) {
$eventdata = $event->get_data();
var_dump(json_encode($eventdata));
        }
}

1) calling: external_table_update -> Unexpected token 's', "string(371"... is not valid JSON
2) calling: user_update ->
string(327) "{"eventname":"\\core\\event\\user_updated","component":"core","action":"updated","target":"user","objecttable":"user","objectid":"8278","crud":"u","edulevel":0,"contextid":28310,"contextlevel":30,"contextinstanceid":"8278","userid":"8278","courseid":0,"relateduserid":"8278","anonymous":0,"other":null,"timecreated":1674134122}"


They are fired from local\course_completion\db\events.php 

Any suggestion ? thanks.

In reply to Nicola Vallinoto

Re: Ri: Re: Ri: Re: Ri: Re: Local plugin doesn't fire callback function

by Renaat Debleu -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers

replace var_dump(json_encode($eventdata)); by print_object($eventdata); to get rid of the json error.

Average of ratings: Useful (1)
In reply to Renaat Debleu

Ri: Re: Ri: Re: Ri: Re: Ri: Re: Local plugin doesn't fire callback function

by Nicola Vallinoto -
The same try with user_updated event had a successful answer as follows:
Array
(
    [eventname] => \core\event\user_updated
    [component] => core
    [action] => updated
    [target] => user
    [objecttable] => user
    [objectid] => 8278
    [crud] => u
    [edulevel] => 0
    [contextid] => 28310
    [contextlevel] => 30
    [contextinstanceid] => 8278
    [userid] => 8278
    [courseid] => 0
    [relateduserid] => 8278
    [anonymous] => 0
    [other] => 
    [timecreated] => 1674204689
)

(Edited by Howard Miller at request of poster - original submission Friday, 20 January 2023, 8:52 AM)

In reply to Renaat Debleu

Ri: Re: Ri: Re: Ri: Re: Ri: Re: Local plugin doesn't fire callback function

by Nicola Vallinoto -
I inspected the network tab of the browsers and found more about the error:
{"error":"Coding error detected, it must be fixed by a programmer: Invalid json in request: Syntax error","errorcode":"codingerror","stacktrace":"* line 63 of \/lib\/ajax\/service.php: coding_exception thrown\n","debuginfo":"\nError code: codingerror","reproductionlink":"https:\/\/www.sitoweb.it\/"}


I have a read in the code of service.php and the error could be generated by either we are not allowing GET parameters or we didn't use GET because we did not pass a cache key or the URL was too long.


In reply to Nicola Vallinoto

Ri: Local plugin doesn't fire callback function

by Nicola Vallinoto -
This post can be closed.

I found the correct way to fire a callback function.

Here the code I used to do it.

local/coursecompletion/db/events.php

<?php
defined('MOODLE_INTERNAL') || die();
    $observers = [
      [
        'eventname' => '\core\event\course_completed',
        'callback'  => '\local_coursecompletion\local_coursecompletion_observer::external_table_update'
      ],

local/coursecompletion/classes/local_course_completion.php

<?php 
namespace local_coursecompletion;
defined('MOODLE_INTERNAL') || die();
class local_coursecompletion_observer {
 public static function external_table_update(\core\event\course_completed $event) {
// function code
}