MDL-67788 core_h5p: add tracking to player

This commit is contained in:
Ferran Recio 2020-03-03 16:41:10 +01:00
parent df0e58adb1
commit 8685c313e0
4 changed files with 116 additions and 7 deletions

View File

@ -27,6 +27,7 @@ namespace core_h5p;
defined('MOODLE_INTERNAL') || die();
use core_h5p\local\library\autoloader;
use core_xapi\local\statement\item_activity;
/**
* H5P player class, for displaying any local H5P content.
@ -67,6 +68,11 @@ class player {
*/
private $content;
/**
* @var string optional component name to send xAPI statements.
*/
private $component;
/**
* @var string Type of embed object, div or iframe.
*/
@ -98,8 +104,9 @@ class player {
* @param string $url Local URL of the H5P file to display.
* @param stdClass $config Configuration for H5P buttons.
* @param bool $preventredirect Set to true in scripts that can not redirect (CLI, RSS feeds, etc.), throws exceptions
* @param string $component optional moodle component to sent xAPI tracking
*/
public function __construct(string $url, \stdClass $config, bool $preventredirect = true) {
public function __construct(string $url, \stdClass $config, bool $preventredirect = true, string $component = '') {
if (empty($url)) {
throw new \moodle_exception('h5pinvalidurl', 'core_h5p');
}
@ -110,6 +117,8 @@ class player {
$this->messages = new \stdClass();
$this->component = $component;
// Create \core_h5p\core instance.
$this->core = $this->factory->get_core();
@ -129,14 +138,17 @@ class player {
* @param string $url Local URL of the H5P file to display.
* @param stdClass $config Configuration for H5P buttons.
* @param bool $preventredirect Set to true in scripts that can not redirect (CLI, RSS feeds, etc.), throws exceptions
* @param string $component optional moodle component to sent xAPI tracking
*
* @return string The embedable code to display a H5P file.
*/
public static function display(string $url, \stdClass $config, bool $preventredirect = true): string {
public static function display(string $url, \stdClass $config, bool $preventredirect = true,
string $component = ''): string {
global $OUTPUT;
$params = [
'url' => $url,
'preventredirect' => $preventredirect,
'component' => $component,
];
$optparams = ['frame', 'export', 'embed', 'copyright'];
@ -193,6 +205,7 @@ class player {
$contenturl = \moodle_url::make_pluginfile_url($systemcontext->id, \core_h5p\file_storage::COMPONENT,
\core_h5p\file_storage::CONTENT_FILEAREA, $this->h5pid, null, null);
$exporturl = $this->get_export_settings($displayoptions[ core::DISPLAY_OPTION_DOWNLOAD ]);
$xapiobject = item_activity::create_from_id($this->context->id);
$contentsettings = [
'library' => core::libraryToString($this->content['library']),
'fullScreen' => $this->content['library']['fullscreen'],
@ -202,7 +215,7 @@ class player {
'resizeCode' => self::get_resize_code(),
'title' => $this->content['slug'],
'displayOptions' => $displayoptions,
'url' => self::get_embed_url($this->url->out())->out(),
'url' => $xapiobject->get_data()->id,
'contentUrl' => $contenturl->out(),
'metadata' => $this->content['metadata'],
'contentUserData' => [0 => ['state' => '{}']]
@ -698,7 +711,7 @@ class player {
* @return array The settings.
*/
private function get_core_settings(): array {
global $CFG;
global $CFG, $USER;
$basepath = $CFG->wwwroot . '/';
$systemcontext = \context_system::instance();
@ -717,7 +730,7 @@ class player {
'saveFreq' => false,
'siteUrl' => $CFG->wwwroot,
'l10n' => array('H5P' => $this->core->getLocalization()),
'user' => [],
'user' => ['name' => $USER->username, 'mail' => $USER->email],
'hubIsEnabled' => false,
'reportingIsEnabled' => false,
'crossorigin' => null,
@ -725,6 +738,7 @@ class player {
'pluginCacheBuster' => $this->get_cache_buster(),
'libraryUrl' => autoloader::get_h5p_core_library_url('js'),
'moodleLibraryPaths' => $this->core->get_dependency_roots($this->h5pid),
'moodleComponent' => $this->component,
);
return $settings;

View File

@ -36,9 +36,11 @@ $config->copyright = optional_param('copyright', 0, PARAM_INT);
$preventredirect = optional_param('preventredirect', true, PARAM_BOOL);
$component = optional_param('component', '', PARAM_COMPONENT);
$PAGE->set_url(new \moodle_url('/h5p/embed.php', array('url' => $url)));
try {
$h5pplayer = new \core_h5p\player($url, $config, $preventredirect);
$h5pplayer = new \core_h5p\player($url, $config, $preventredirect, $component);
$messages = $h5pplayer->get_messages();
} catch (\Exception $e) {
@ -92,4 +94,4 @@ if (empty($messages->error) && empty($messages->exception)) {
echo $OUTPUT->render_from_template('core_h5p/h5perror', $messages);
}
echo $OUTPUT->footer();
echo $OUTPUT->footer();

View File

@ -71,6 +71,27 @@ H5PEmbedCommunicator = (function() {
// Parent origin can be anything.
window.parent.postMessage(data, '*');
};
/**
* Send a xAPI statement to LMS.
*
* @param {string} component
* @param {Object} statements
*/
self.post = function(component, statements) {
require(['core/ajax'], function(ajax) {
var data = {
component: component,
requestjson: JSON.stringify(statements)
};
ajax.call([
{
methodname: 'core_xapi_statement_post',
args: data
}
]);
});
};
}
return (window.postMessage && window.addEventListener ? new Communicator() : undefined);
@ -150,6 +171,38 @@ document.onreadystatechange = function() {
}, 0);
});
// Get emitted xAPI data.
H5P.externalDispatcher.on('xAPI', function(event) {
var moodlecomponent = H5P.getMoodleComponent();
if (moodlecomponent == undefined) {
return;
}
// Skip malformed events.
var hasStatement = event && event.data && event.data.statement;
if (!hasStatement) {
return;
}
var statement = event.data.statement;
var validVerb = statement.verb && statement.verb.id;
if (!validVerb) {
return;
}
var isCompleted = statement.verb.id === 'http://adlnet.gov/expapi/verbs/answered'
|| statement.verb.id === 'http://adlnet.gov/expapi/verbs/completed';
var isChild = statement.context && statement.context.contextActivities &&
statement.context.contextActivities.parent &&
statement.context.contextActivities.parent[0] &&
statement.context.contextActivities.parent[0].id;
if (isCompleted && !isChild) {
var statements = H5P.getXAPIStatements(this.contentId, statement);
H5PEmbedCommunicator.post(moodlecomponent, statements);
}
});
// Trigger initial resize for instance.
H5P.trigger(instance, 'resize');
};

View File

@ -7,3 +7,43 @@ H5P.getLibraryPath = function (library) {
}
return H5P._getLibraryPath(library);
};
H5P.findInstanceFromId = function (contentId) {
if (!contentId) {
return H5P.instances[0];
}
if (H5P.instances !== undefined) {
for (var i = 0; i < H5P.instances.length; i++) {
if (H5P.instances[i].contentId === contentId) {
return H5P.instances[i];
}
}
}
return undefined;
};
H5P.getXAPIStatements = function (contentId, statement) {
var statements = [];
var instance = H5P.findInstanceFromId(contentId);
if (!instance){
return statements;
}
if (instance.getXAPIData == undefined) {
var xAPIData = {
statement: statement
};
} else {
var xAPIData = instance.getXAPIData();
}
if (xAPIData.statement != undefined) {
statements.push(xAPIData.statement);
}
if (xAPIData.children != undefined) {
statements = statements.concat(xAPIData.children.map(a => a.statement));
}
return statements;
};
H5P.getMoodleComponent = function () {
if (H5PIntegration.moodleComponent) {
return H5PIntegration.moodleComponent;
}
return undefined;
};