Skip to content

Commit

Permalink
LTI provider support
Browse files Browse the repository at this point in the history
  • Loading branch information
sfraysse committed Sep 9, 2019
1 parent 2def19e commit b9dca16
Show file tree
Hide file tree
Showing 17 changed files with 266 additions and 42 deletions.
17 changes: 12 additions & 5 deletions classes/external.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,17 @@ public static function get_actors_returns() {
* @return external_function_parameters
*/
protected static function get_parameters(string $service) {
$structure = [
'type' => new external_value(PARAM_ALPHA, 'Moodle internal type of the '. $service, VALUE_OPTIONAL),
'id' => new external_value(PARAM_INT, 'Moodle internal ID of the ' . $service, VALUE_OPTIONAL),
'uuid' => new external_value(PARAM_ALPHANUMEXT, 'Generated UUID of the ' . $service, VALUE_OPTIONAL),
];
if ($service == 'actors') {
$structure['email'] = new external_value(PARAM_EMAIL, 'Email of the ' . $service, VALUE_OPTIONAL);
}
return new external_function_parameters([
'items' => new external_multiple_structure(
new external_single_structure([
'type' => new external_value(PARAM_ALPHA, 'Moodle internal type of the '. $service, VALUE_OPTIONAL),
'id' => new external_value(PARAM_INT, 'Moodle internal ID of the ' . $service, VALUE_OPTIONAL),
'uuid' => new external_value(PARAM_ALPHANUMEXT, 'Generated UUID of the ' . $service, VALUE_OPTIONAL),
])
new external_single_structure($structure)
),
'full' => new external_value(
PARAM_BOOL, 'Return the xAPI full definition (default is false)', VALUE_DEFAULT, false
Expand All @@ -131,6 +135,8 @@ protected static function get(array $items, bool $full, string $service) {
$item['xapi'] = $controller->$service->get_existing_by_uuid($item['uuid'], $full);
} else if (isset($item['type']) && isset($item['id'])) {
$item['xapi'] = $controller->$service->get_existing($item['type'], $item['id'], $full);
} else if (isset($item['email'])) {
$item['xapi'] = $controller->$service->get_by_email($item['email']);
} else {
throw new \moodle_exception('invalid_entry_identification', 'logstore_trax');
}
Expand All @@ -152,6 +158,7 @@ protected static function get_returns(string $service) {
'type' => new external_value(PARAM_ALPHA, 'Moodle internal type of the '. $service, VALUE_OPTIONAL),
'id' => new external_value(PARAM_INT, 'Moodle internal ID of the ' . $service, VALUE_OPTIONAL),
'uuid' => new external_value(PARAM_ALPHANUMEXT, 'Generated UUID of the ' . $service, VALUE_OPTIONAL),
'email' => new external_value(PARAM_EMAIL, 'Email of the ' . $service, VALUE_OPTIONAL),
'xapi' => new external_value(PARAM_RAW, 'The xAPI JSON string of the '. $service)
)
)
Expand Down
46 changes: 46 additions & 0 deletions classes/src/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ class config {
*/
const ASYNC = 1;

/**
* Actors identification: anonymous mode.
*/
const ID_ANONYMOUS = 0;

/**
* Actors identification: account with username.
*/
const ID_ACCOUNT_USERNAME = 1;

/**
* Actors identification: mbox.
*/
const ID_MBOX = 2;


/**
* Get the sync modes.
Expand All @@ -67,6 +82,37 @@ public static function sync() {
return get_config('logstore_trax', 'sync_mode') == self::SYNC;
}

/**
* Get the identification modes.
*
* @return array
*/
public static function actors_identification_modes() {
return [
self::ID_ANONYMOUS => get_string('anonymous', 'logstore_trax'),
self::ID_ACCOUNT_USERNAME => get_string('account_username', 'logstore_trax'),
self::ID_MBOX => get_string('mbox', 'logstore_trax'),
];
}

/**
* Is identification anonymous?
*
* @return bool
*/
public static function anonymous() {
return get_config('logstore_trax', 'actors_identification') == self::ID_ANONYMOUS;
}

/**
* Is identification based on mbox?
*
* @return bool
*/
public static function mbox() {
return get_config('logstore_trax', 'actors_identification') == self::ID_MBOX;
}

/**
* Get the loggable core events.
*
Expand Down
8 changes: 8 additions & 0 deletions classes/src/proxy/profile.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use logstore_trax\src\services\actors;
use logstore_trax\src\services\verbs;
use logstore_trax\src\services\activities;
use logstore_trax\src\statements\consumer_statement;

defined('MOODLE_INTERNAL') || die();

Expand All @@ -39,6 +40,8 @@
*/
abstract class profile {

use consumer_statement;

/**
* Actors service.
*
Expand Down Expand Up @@ -172,6 +175,11 @@ private function _transform(&$statement) {
$this->activities->get('system', 0, false),
$this->activities->get('course', $this->course->id, false),
];

// LTI client.
if ($consumer = $this->consumer($this->userid)) {
$statement->context->contextActivities->grouping[] = $consumer;
}

// Add VLE profile.
$statement->context->contextActivities->category[] = [
Expand Down
55 changes: 47 additions & 8 deletions classes/src/services/actors.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

defined('MOODLE_INTERNAL') || die();

use \logstore_trax\src\config;

/**
* Actors service.
*
Expand Down Expand Up @@ -71,31 +73,45 @@ public function __construct() {
* @return array
*/
public function get(string $type, int $mid = 0, bool $full = false, $entry = null) {
global $DB;
$config = get_config('logstore_trax');
$named = !$config->anonymization || ($full && !$config->xis_anonymization);
$named = !config::anonymous() || ($full && !$config->xis_anonymization);

// Base.
$res = [
'objectType' => $this->types->$type->object_type,
'account' => [
'homePage' => $this->platform_iri(),
],
];
if (!$named) {

// Anonymized.
if (!isset($entry)) {
$entry = $this->get_or_create_db_entry($mid, $type);
$record = $DB->get_record($type, ['id' => $mid], '*', MUST_EXIST);
$entry = $this->get_or_create_db_entry($mid, $type, ['email' => $record->email]);
}
$res['account']['name'] = $entry->uuid;
$res['account'] = [
'name' => $entry->uuid,
'homePage' => $this->platform_iri(),
];

} else {

// Not anonymized.
global $DB;
$record = $DB->get_record($type, ['id' => $mid], '*', MUST_EXIST);
$res['account']['name'] = $record->username;
$res['name'] = $record->firstname . ' ' . $record->lastname;

if (config::mbox()) {

// Mbox.
$res['mbox'] = 'mailto:' . $record->email;

} else {

// Account with username.
$res['account'] = [
'name' => $record->username,
'homePage' => $this->platform_iri(),
];
}
}
return $res;
}
Expand Down Expand Up @@ -125,5 +141,28 @@ public function get_existing_by_uuid(string $uuid, bool $full = false) {
return $this->get($entry->type, $entry->mid, $full, $entry);
}

/**
* Get actors matching with a given email.
*
* @param string $email Actor email
* @return array
*/
public function get_by_email(string $email) {
global $DB;
$entries = $DB->get_records('logstore_trax_actors', ['email' => $email]);

return array_values(array_map(function ($entry) {

return [
'objectType' => 'Agent',
'account' => [
'name' => $entry->uuid,
'homePage' => $this->platform_iri(),
],
];

}, $entries));
}


}
7 changes: 4 additions & 3 deletions classes/src/services/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,18 @@ abstract public function get(string $type, int $mid = 0, bool $full = false);
*
* @param int $mid Moodle ID of the item
* @param string $type Type of item
* @param array $data More data
* @return stdClass
*/
public function get_or_create_db_entry(int $mid, string $type) {
public function get_or_create_db_entry(int $mid, string $type, array $data = []) {
global $DB;
$entry = $this->get_db_entry($mid, $type);
if (!$entry) {
$entry = (object)[
$entry = (object)array_merge([
'mid' => $mid,
'type' => $type,
'uuid' => utils::uuid(),
];
], $data);
$entry->id = $DB->insert_record($this->table, $entry);
}
return $entry;
Expand Down
15 changes: 14 additions & 1 deletion classes/src/statements/base_statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
use logstore_trax\src\services\actors;
use logstore_trax\src\services\verbs;
use logstore_trax\src\services\activities;
use logstore_trax\src\utils;

/**
* Abstract class to implement an xAPI statement.
Expand All @@ -40,6 +39,8 @@
*/
abstract class base_statement {

use consumer_statement;

/**
* Actors service.
*
Expand Down Expand Up @@ -170,9 +171,21 @@ protected function base_context($activitytype, $withsystem, $vocabtype, $plugin

// System grouping.
if ($withsystem) {

// Moodle instance.
$res['contextActivities']['grouping'] = [
$this->activities->get('system', 0, false)
];

}

// LTI client.
if ($consumer = $this->consumer($this->event->userid)) {

if (!isset($res['contextActivities']['grouping'])) {
$res['contextActivities']['grouping'] = [];
}
$res['contextActivities']['grouping'][] = $consumer;
}

return $res;
Expand Down
67 changes: 67 additions & 0 deletions classes/src/statements/consumer_statement.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Trait to implement an external service consumer (LTI).
*
* @package logstore_trax
* @copyright 2019 Sébastien Fraysse {@link http://fraysse.eu}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

namespace logstore_trax\src\statements;

defined('MOODLE_INTERNAL') || die();

/**
* Trait to implement an external service consumer (LTI).
*
* @package logstore_trax
* @copyright 2019 Sébastien Fraysse {@link http://fraysse.eu}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
trait consumer_statement {

/**
* Get the consumer.
*
* @param int $userid
* @return array|false
*/
protected function consumer(int $userid) {
global $DB;
if ($user = $DB->get_record('user', ['id' => $userid])) {
if ($user->auth == 'lti') {
if ($enrol = $DB->get_record('enrol_lti_users', ['userid' => $user->id])) {

return [
'objectType' => 'Activity',
'id' => $enrol->serviceurl,
'definition' => [
'type' => 'http://vocab.xapi.fr/activities/consumer',
'extensions' => [
'http://vocab.xapi.fr/extensions/standard' => 'lti',
],
],
];
}
}
}

return false;
}

}
1 change: 1 addition & 0 deletions db/install.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="mid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="email" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="type" TYPE="char" LENGTH="50" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="uuid" TYPE="char" LENGTH="36" NOTNULL="true" SEQUENCE="false"/>
</FIELDS>
Expand Down
29 changes: 29 additions & 0 deletions db/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,5 +194,34 @@ function xmldb_logstore_trax_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2018050810, 'logstore', 'trax');
}

// Add email column in actors table.
if ($oldversion < 2018050812) {

// Add field.
$table = new xmldb_table('logstore_trax_actors');
$field = new xmldb_field('email', XMLDB_TYPE_CHAR, '100', null, false, false, null, 'mid');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

// Add index (unique).
$index = new xmldb_index('email', XMLDB_INDEX_NOTUNIQUE, array('email'));
if (!$dbman->index_exists($table, $index)) {
$dbman->add_index($table, $index);
}

// Update actors records.
$records = $DB->get_records('logstore_trax_actors');
foreach ($records as $record) {
if ($user = $DB->get_record('user', ['id' => $record->mid])) {
$record->email = $user->email;
$DB->update_record('logstore_trax_actors', $record);
}
}

// Savepoint.
upgrade_plugin_savepoint(true, 2018050812, 'logstore', 'trax');
}

return true;
}
Loading

0 comments on commit b9dca16

Please sign in to comment.