mirror of
https://github.com/moodle/moodle.git
synced 2025-08-05 08:56:36 +02:00
Merge branch 'MDL-60416_master' of git://github.com/markn86/moodle
This commit is contained in:
commit
98645b5be4
29 changed files with 3260 additions and 230 deletions
|
@ -87,6 +87,23 @@ abstract class restore_subplugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The after_restore dispatcher for any restore_subplugin class.
|
||||||
|
*
|
||||||
|
* This method will dispatch execution to the corresponding
|
||||||
|
* after_restore_xxx() method when available, with xxx
|
||||||
|
* being the connection point of the instance, so subplugin
|
||||||
|
* classes with multiple connection points will support
|
||||||
|
* multiple after_restore methods, one for each connection point.
|
||||||
|
*/
|
||||||
|
public function launch_after_restore_methods() {
|
||||||
|
// Check if the after_restore method exists and launch it.
|
||||||
|
$afterestore = 'after_restore_' . basename($this->connectionpoint->get_path());
|
||||||
|
if (method_exists($this, $afterestore)) {
|
||||||
|
$this->$afterestore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Protected API starts here
|
// Protected API starts here
|
||||||
|
|
||||||
// restore_step/structure_step/task wrappers
|
// restore_step/structure_step/task wrappers
|
||||||
|
|
|
@ -1804,7 +1804,7 @@ class core_plugin_manager {
|
||||||
),
|
),
|
||||||
|
|
||||||
'ltiservice' => array(
|
'ltiservice' => array(
|
||||||
'memberships', 'profile', 'toolproxy', 'toolsettings'
|
'gradebookservices', 'memberships', 'profile', 'toolproxy', 'toolsettings'
|
||||||
),
|
),
|
||||||
|
|
||||||
'mlbackend' => array(
|
'mlbackend' => array(
|
||||||
|
|
|
@ -28,6 +28,7 @@ namespace mod_lti\local\ltiservice;
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
global $CFG;
|
||||||
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,7 +42,16 @@ require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||||
*/
|
*/
|
||||||
abstract class resource_base {
|
abstract class resource_base {
|
||||||
|
|
||||||
/** @var object Service associated with this resource. */
|
/** HTTP Post method */
|
||||||
|
const HTTP_POST = 'POST';
|
||||||
|
/** HTTP Get method */
|
||||||
|
const HTTP_GET = 'GET';
|
||||||
|
/** HTTP Put method */
|
||||||
|
const HTTP_PUT = 'PUT';
|
||||||
|
/** HTTP Delete method */
|
||||||
|
const HTTP_DELETE = 'DELETE';
|
||||||
|
|
||||||
|
/** @var service_base Service associated with this resource. */
|
||||||
private $service;
|
private $service;
|
||||||
/** @var string Type for this resource. */
|
/** @var string Type for this resource. */
|
||||||
protected $type;
|
protected $type;
|
||||||
|
@ -62,7 +72,7 @@ abstract class resource_base {
|
||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Class constructor.
|
||||||
*
|
*
|
||||||
* @param mod_lti\local\ltiservice\service_base $service Service instance
|
* @param service_base $service Service instance
|
||||||
*/
|
*/
|
||||||
public function __construct($service) {
|
public function __construct($service) {
|
||||||
|
|
||||||
|
@ -125,7 +135,7 @@ abstract class resource_base {
|
||||||
/**
|
/**
|
||||||
* Get the resource's service.
|
* Get the resource's service.
|
||||||
*
|
*
|
||||||
* @return mod_lti\local\ltiservice\service_base
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function get_service() {
|
public function get_service() {
|
||||||
|
|
||||||
|
@ -190,7 +200,7 @@ abstract class resource_base {
|
||||||
/**
|
/**
|
||||||
* Execute the request for this resource.
|
* Execute the request for this resource.
|
||||||
*
|
*
|
||||||
* @param mod_lti\local\ltiservice\response $response Response object for this request.
|
* @param response $response Response object for this request.
|
||||||
*/
|
*/
|
||||||
public abstract function execute($response);
|
public abstract function execute($response);
|
||||||
|
|
||||||
|
@ -224,7 +234,7 @@ abstract class resource_base {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$ok) {
|
if (!$ok) {
|
||||||
debugging('Requested service not included in tool proxy: ' . $this->get_id());
|
debugging('Requested service not included in tool proxy: ' . $this->get_id(), DEBUG_DEVELOPER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,6 +243,45 @@ abstract class resource_base {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to make sure the request is valid.
|
||||||
|
*
|
||||||
|
* @param int $typeid The typeid we want to use
|
||||||
|
* @param int $contextid The course we are at
|
||||||
|
* @param string $permissionrequested The permission to be checked
|
||||||
|
* @param string $body Body of HTTP request message
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function check_type($typeid, $contextid, $permissionrequested, $body = null) {
|
||||||
|
$ok = false;
|
||||||
|
if ($this->get_service()->check_type($typeid, $contextid, $body)) {
|
||||||
|
$neededpermissions = $this->get_permissions($typeid);
|
||||||
|
foreach ($neededpermissions as $permission) {
|
||||||
|
if ($permission == $permissionrequested) {
|
||||||
|
$ok = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$ok) {
|
||||||
|
debugging('Requested service ' . $permissionrequested . ' not included in tool type: ' . $typeid,
|
||||||
|
DEBUG_DEVELOPER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $ok;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get permissions from the config of the tool for that resource
|
||||||
|
*
|
||||||
|
* @param int $ltitype Type of LTI
|
||||||
|
* @return array with the permissions related to this resource by the $ltitype or empty if none.
|
||||||
|
*/
|
||||||
|
public function get_permissions($ltitype) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a value for custom parameter substitution variables.
|
* Parse a value for custom parameter substitution variables.
|
||||||
*
|
*
|
||||||
|
|
|
@ -54,6 +54,8 @@ class response {
|
||||||
private $body;
|
private $body;
|
||||||
/** @var array HTTP response codes. */
|
/** @var array HTTP response codes. */
|
||||||
private $responsecodes;
|
private $responsecodes;
|
||||||
|
/** @var array HTTP additional headers. */
|
||||||
|
private $additionalheaders;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Class constructor.
|
||||||
|
@ -83,6 +85,7 @@ class response {
|
||||||
500 => 'Internal Server Error',
|
500 => 'Internal Server Error',
|
||||||
501 => 'Not Implemented'
|
501 => 'Not Implemented'
|
||||||
);
|
);
|
||||||
|
$this->additionalheaders = array();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,11 +205,23 @@ class response {
|
||||||
$this->body = $body;
|
$this->body = $body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an additional header.
|
||||||
|
*
|
||||||
|
* @param string $header The new header
|
||||||
|
*/
|
||||||
|
public function add_additional_header($header) {
|
||||||
|
array_push($this->additionalheaders, $header);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send the response.
|
* Send the response.
|
||||||
*/
|
*/
|
||||||
public function send() {
|
public function send() {
|
||||||
header("HTTP/1.0 {$this->code} {$this->get_reason()}");
|
header("HTTP/1.0 {$this->code} {$this->get_reason()}");
|
||||||
|
foreach ($this->additionalheaders as $header) {
|
||||||
|
header($header);
|
||||||
|
}
|
||||||
if (($this->code >= 200) && ($this->code < 300)) {
|
if (($this->code >= 200) && ($this->code < 300)) {
|
||||||
if (!empty($this->contenttype)) {
|
if (!empty($this->contenttype)) {
|
||||||
header("Content-Type: {$this->contenttype};charset=UTF-8");
|
header("Content-Type: {$this->contenttype};charset=UTF-8");
|
||||||
|
|
|
@ -28,11 +28,13 @@ namespace mod_lti\local\ltiservice;
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
global $CFG;
|
||||||
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||||
require_once($CFG->dirroot . '/mod/lti/OAuthBody.php');
|
require_once($CFG->dirroot . '/mod/lti/OAuthBody.php');
|
||||||
|
|
||||||
// TODO: Switch to core oauthlib once implemented - MDL-30149.
|
// TODO: Switch to core oauthlib once implemented - MDL-30149.
|
||||||
use moodle\mod\lti as lti;
|
use moodle\mod\lti as lti;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -133,10 +135,80 @@ abstract class service_base {
|
||||||
/**
|
/**
|
||||||
* Get the resources for this service.
|
* Get the resources for this service.
|
||||||
*
|
*
|
||||||
* @return array
|
* @return resource_base[]
|
||||||
*/
|
*/
|
||||||
abstract public function get_resources();
|
abstract public function get_resources();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the configuration options for this service.
|
||||||
|
*
|
||||||
|
* @param \MoodleQuickForm $mform Moodle quickform object definition
|
||||||
|
*/
|
||||||
|
public function get_configuration_options(&$mform) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array with the names of the parameters that the service will be saving in the configuration
|
||||||
|
*
|
||||||
|
* @return array Names list of the parameters that the service will be saving in the configuration
|
||||||
|
*/
|
||||||
|
public function get_configuration_parameter_names() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation will check for the existence of at least one mod_lti entry for that tool and context.
|
||||||
|
*
|
||||||
|
* It may be overridden if other inferences can be done.
|
||||||
|
*
|
||||||
|
* Ideally a Site Tool should be explicitly engaged with a course, the check on the presence of a link is a proxy
|
||||||
|
* to infer a Site Tool engagement until an explicit Site Tool - Course relationship exists.
|
||||||
|
*
|
||||||
|
* @param int $typeid The tool lti type id.
|
||||||
|
* @param int $courseid The course id.
|
||||||
|
* @return bool returns True if tool is used in context, false otherwise.
|
||||||
|
*/
|
||||||
|
public function is_used_in_context($typeid, $courseid) {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
$ok = $DB->record_exists('lti', array('course' => $courseid, 'typeid' => $typeid));
|
||||||
|
return $ok || $DB->record_exists('lti_types', array('course' => $courseid, 'id' => $typeid));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if there is a site tool or a course tool for this site.
|
||||||
|
*
|
||||||
|
* @param int $typeid The tool lti type id.
|
||||||
|
* @param int $courseid The course id.
|
||||||
|
* @return bool returns True if tool is allowed in context, false otherwise.
|
||||||
|
*/
|
||||||
|
public function is_allowed_in_context($typeid, $courseid) {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
// Check if it is a Course tool for this course or a Site tool.
|
||||||
|
$type = $DB->get_record('lti_types', array('id' => $typeid));
|
||||||
|
|
||||||
|
return $type && ($type->course == $courseid || $type->course == SITEID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of key/values to add to the launch parameters.
|
||||||
|
*
|
||||||
|
* @param string $messagetype 'basic-lti-launch-request' or 'ContentItemSelectionRequest'.
|
||||||
|
* @param string $courseid The course id.
|
||||||
|
* @param string $userid The user id.
|
||||||
|
* @param string $typeid The tool lti type id.
|
||||||
|
* @param string $modlti The id of the lti activity.
|
||||||
|
*
|
||||||
|
* The type is passed to check the configuration and not return parameters for services not used.
|
||||||
|
*
|
||||||
|
* @return array Key/value pairs to add as launch parameters.
|
||||||
|
*/
|
||||||
|
public function get_launch_parameters($messagetype, $courseid, $userid, $typeid, $modlti = null) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the path for service requests.
|
* Get the path for service requests.
|
||||||
*
|
*
|
||||||
|
@ -202,9 +274,35 @@ abstract class service_base {
|
||||||
if ($ok) {
|
if ($ok) {
|
||||||
$this->toolproxy = $toolproxy;
|
$this->toolproxy = $toolproxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ok;
|
return $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that the request has been properly signed.
|
||||||
|
*
|
||||||
|
* @param int $typeid The tool id
|
||||||
|
* @param int $courseid The course we are at
|
||||||
|
* @param string $body Request body (null if none)
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function check_type($typeid, $courseid, $body = null) {
|
||||||
|
$ok = false;
|
||||||
|
$tool = null;
|
||||||
|
$consumerkey = lti\get_oauth_key_from_headers();
|
||||||
|
if (empty($typeid)) {
|
||||||
|
return $ok;
|
||||||
|
} else if ($this->is_allowed_in_context($typeid, $courseid)) {
|
||||||
|
$tool = lti_get_type_type_config($typeid);
|
||||||
|
if ($tool !== false) {
|
||||||
|
if (!$this->is_unsigned() && ($tool->lti_resourcekey == $consumerkey)) {
|
||||||
|
$ok = $this->check_signature($tool->lti_resourcekey, $tool->lti_password, $body);
|
||||||
|
} else {
|
||||||
|
$ok = $this->is_unsigned();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -49,10 +49,24 @@
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die;
|
defined('MOODLE_INTERNAL') || die;
|
||||||
|
|
||||||
|
global $CFG;
|
||||||
require_once($CFG->libdir.'/formslib.php');
|
require_once($CFG->libdir.'/formslib.php');
|
||||||
require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
||||||
|
|
||||||
class mod_lti_edit_types_form extends moodleform{
|
/**
|
||||||
|
* LTI Edit Form
|
||||||
|
*
|
||||||
|
* @package mod_lti
|
||||||
|
* @copyright 2009 Marc Alier, Jordi Piguillem, Nikolas Galanis
|
||||||
|
* marc.alier@upc.edu
|
||||||
|
* @copyright 2009 Universitat Politecnica de Catalunya http://www.upc.edu
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class mod_lti_edit_types_form extends moodleform {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define this form.
|
||||||
|
*/
|
||||||
public function definition() {
|
public function definition() {
|
||||||
global $CFG;
|
global $CFG;
|
||||||
|
|
||||||
|
@ -147,6 +161,16 @@ class mod_lti_edit_types_form extends moodleform{
|
||||||
$mform->disabledIf('lti_contentitem', null);
|
$mform->disabledIf('lti_contentitem', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$mform->addElement('text', 'lti_toolurl_ContentItemSelectionRequest',
|
||||||
|
get_string('toolurl_contentitemselectionrequest', 'lti'), array('size' => '64'));
|
||||||
|
$mform->setType('lti_toolurl_ContentItemSelectionRequest', PARAM_URL);
|
||||||
|
$mform->setAdvanced('lti_toolurl_ContentItemSelectionRequest');
|
||||||
|
$mform->addHelpButton('lti_toolurl_ContentItemSelectionRequest', 'toolurl_contentitemselectionrequest', 'lti');
|
||||||
|
$mform->disabledIf('lti_toolurl_ContentItemSelectionRequest', 'lti_contentitem', 'notchecked');
|
||||||
|
if ($istool) {
|
||||||
|
$mform->disabledIf('lti_toolurl__ContentItemSelectionRequest', null);
|
||||||
|
}
|
||||||
|
|
||||||
$mform->addElement('hidden', 'oldicon');
|
$mform->addElement('hidden', 'oldicon');
|
||||||
$mform->setType('oldicon', PARAM_URL);
|
$mform->setType('oldicon', PARAM_URL);
|
||||||
|
|
||||||
|
@ -160,6 +184,11 @@ class mod_lti_edit_types_form extends moodleform{
|
||||||
$mform->setAdvanced('lti_secureicon');
|
$mform->setAdvanced('lti_secureicon');
|
||||||
$mform->addHelpButton('lti_secureicon', 'secure_icon_url', 'lti');
|
$mform->addHelpButton('lti_secureicon', 'secure_icon_url', 'lti');
|
||||||
|
|
||||||
|
if (!$istool) {
|
||||||
|
// Display the lti advantage services.
|
||||||
|
$this->get_lti_advantage_services($mform);
|
||||||
|
}
|
||||||
|
|
||||||
if (!$istool) {
|
if (!$istool) {
|
||||||
// Add privacy preferences fieldset where users choose whether to send their data.
|
// Add privacy preferences fieldset where users choose whether to send their data.
|
||||||
$mform->addElement('header', 'privacy', get_string('privacy', 'lti'));
|
$mform->addElement('header', 'privacy', get_string('privacy', 'lti'));
|
||||||
|
@ -253,4 +282,19 @@ class mod_lti_edit_types_form extends moodleform{
|
||||||
}
|
}
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the lti advantage extra configuration adding it to the mform
|
||||||
|
*
|
||||||
|
* @param MoodleQuickForm $mform
|
||||||
|
*/
|
||||||
|
public function get_lti_advantage_services(&$mform) {
|
||||||
|
// For each service add the label and get the array of configuration.
|
||||||
|
$services = lti_get_services();
|
||||||
|
$mform->addElement('header', 'services', get_string('services', 'lti'));
|
||||||
|
foreach ($services as $service) {
|
||||||
|
/** @var \mod_lti\local\ltiservice\service_base $service */
|
||||||
|
$service->get_configuration_options($mform);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -510,9 +510,10 @@ A base URL of *quiz.tool.com* would match the following:
|
||||||
If two different tool configurations are for the same domain, the most specific match will be used.
|
If two different tool configurations are for the same domain, the most specific match will be used.
|
||||||
|
|
||||||
You can also insert a cartridge URL if you have one and the details for the tool will be automatically filled.';
|
You can also insert a cartridge URL if you have one and the details for the tool will be automatically filled.';
|
||||||
|
$string['toolurl_contentitemselectionrequest'] = 'Content Selection URL';
|
||||||
|
$string['toolurl_contentitemselectionrequest_help'] = 'The Content Selection URL will be used to launch the content selection page from the tool provider. If it is empty, the Tool URL will be used';
|
||||||
$string['typename'] = 'Tool name';
|
$string['typename'] = 'Tool name';
|
||||||
$string['typename_help'] = 'The tool name is used to identify the tool provider within Moodle. The name entered will be visible
|
$string['typename_help'] = 'The tool name is used to identify the tool provider within Moodle. The name entered will be visible to teachers when adding external tools within courses.';
|
||||||
to teachers when adding external tools within courses.';
|
|
||||||
$string['types'] = 'Types';
|
$string['types'] = 'Types';
|
||||||
$string['unabletocreatetooltype'] = 'Unable to create tool';
|
$string['unabletocreatetooltype'] = 'Unable to create tool';
|
||||||
$string['unabletofindtooltype'] = 'Unable to find tool for {$a->id}';
|
$string['unabletofindtooltype'] = 'Unable to find tool for {$a->id}';
|
||||||
|
|
|
@ -53,6 +53,7 @@ defined('MOODLE_INTERNAL') || die;
|
||||||
// TODO: Switch to core oauthlib once implemented - MDL-30149.
|
// TODO: Switch to core oauthlib once implemented - MDL-30149.
|
||||||
use moodle\mod\lti as lti;
|
use moodle\mod\lti as lti;
|
||||||
|
|
||||||
|
global $CFG;
|
||||||
require_once($CFG->dirroot.'/mod/lti/OAuth.php');
|
require_once($CFG->dirroot.'/mod/lti/OAuth.php');
|
||||||
require_once($CFG->libdir.'/weblib.php');
|
require_once($CFG->libdir.'/weblib.php');
|
||||||
require_once($CFG->dirroot . '/course/modlib.php');
|
require_once($CFG->dirroot . '/course/modlib.php');
|
||||||
|
@ -96,7 +97,7 @@ define('LTI_VERSION_2', 'LTI-2p0');
|
||||||
* @since Moodle 3.0
|
* @since Moodle 3.0
|
||||||
*/
|
*/
|
||||||
function lti_get_launch_data($instance) {
|
function lti_get_launch_data($instance) {
|
||||||
global $PAGE, $CFG;
|
global $PAGE, $CFG, $USER;
|
||||||
|
|
||||||
if (empty($instance->typeid)) {
|
if (empty($instance->typeid)) {
|
||||||
$tool = lti_get_tool_by_url_match($instance->toolurl, $instance->course);
|
$tool = lti_get_tool_by_url_match($instance->toolurl, $instance->course);
|
||||||
|
@ -230,6 +231,18 @@ function lti_get_launch_data($instance) {
|
||||||
|
|
||||||
$requestparams['launch_presentation_return_url'] = $returnurl;
|
$requestparams['launch_presentation_return_url'] = $returnurl;
|
||||||
|
|
||||||
|
// Add the parameters configured by the LTI advantage services.
|
||||||
|
if ($typeid && !$islti2) {
|
||||||
|
$services = lti_get_services();
|
||||||
|
foreach ($services as $service) {
|
||||||
|
$ltiadvantageparameters = $service->get_launch_parameters('basic-lti-launch-request',
|
||||||
|
$course->id, $USER->id , $typeid, $instance->id);
|
||||||
|
foreach ($ltiadvantageparameters as $ltiadvantagekey => $ltiadvantagevalue) {
|
||||||
|
$requestparams[$ltiadvantagekey] = $ltiadvantagevalue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Allow request params to be updated by sub-plugins.
|
// Allow request params to be updated by sub-plugins.
|
||||||
$plugins = core_component::get_plugin_list('ltisource');
|
$plugins = core_component::get_plugin_list('ltisource');
|
||||||
foreach (array_keys($plugins) as $plugin) {
|
foreach (array_keys($plugins) as $plugin) {
|
||||||
|
@ -284,7 +297,7 @@ function lti_launch_tool($instance) {
|
||||||
/**
|
/**
|
||||||
* Prepares an LTI registration request message
|
* Prepares an LTI registration request message
|
||||||
*
|
*
|
||||||
* $param object $instance Tool Proxy instance object
|
* @param object $toolproxy Tool Proxy instance object
|
||||||
*/
|
*/
|
||||||
function lti_register($toolproxy) {
|
function lti_register($toolproxy) {
|
||||||
$endpoint = $toolproxy->regurl;
|
$endpoint = $toolproxy->regurl;
|
||||||
|
@ -617,6 +630,8 @@ function lti_build_custom_parameters($toolproxy, $tool, $instance, $params, $cus
|
||||||
function lti_build_content_item_selection_request($id, $course, moodle_url $returnurl, $title = '', $text = '', $mediatypes = [],
|
function lti_build_content_item_selection_request($id, $course, moodle_url $returnurl, $title = '', $text = '', $mediatypes = [],
|
||||||
$presentationtargets = [], $autocreate = false, $multiple = false,
|
$presentationtargets = [], $autocreate = false, $multiple = false,
|
||||||
$unsigned = false, $canconfirm = false, $copyadvice = false) {
|
$unsigned = false, $canconfirm = false, $copyadvice = false) {
|
||||||
|
global $USER;
|
||||||
|
|
||||||
$tool = lti_get_type($id);
|
$tool = lti_get_type($id);
|
||||||
// Validate parameters.
|
// Validate parameters.
|
||||||
if (!$tool) {
|
if (!$tool) {
|
||||||
|
@ -693,6 +708,18 @@ function lti_build_content_item_selection_request($id, $course, moodle_url $retu
|
||||||
$requestparams = array_merge($requestparams, $lti2params);
|
$requestparams = array_merge($requestparams, $lti2params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the parameters configured by the LTI advantage services.
|
||||||
|
if ($id && !$islti2) {
|
||||||
|
$services = lti_get_services();
|
||||||
|
foreach ($services as $service) {
|
||||||
|
$ltiadvantageparameters = $service->get_launch_parameters('ContentItemSelectionRequest',
|
||||||
|
$course->id, $USER->id , $id);
|
||||||
|
foreach ($ltiadvantageparameters as $ltiadvantagekey => $ltiadvantagevalue) {
|
||||||
|
$requestparams[$ltiadvantagekey] = $ltiadvantagevalue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get standard request parameters and merge to the request parameters.
|
// Get standard request parameters and merge to the request parameters.
|
||||||
$orgid = !empty($typeconfig['organizationid']) ? $typeconfig['organizationid'] : '';
|
$orgid = !empty($typeconfig['organizationid']) ? $typeconfig['organizationid'] : '';
|
||||||
$standardparams = lti_build_standard_request(null, $orgid, $islti2, 'ContentItemSelectionRequest');
|
$standardparams = lti_build_standard_request(null, $orgid, $islti2, 'ContentItemSelectionRequest');
|
||||||
|
@ -856,9 +883,6 @@ function lti_tool_configuration_from_content_item($typeid, $messagetype, $ltiver
|
||||||
if (empty($items)) {
|
if (empty($items)) {
|
||||||
throw new moodle_exception('errorinvaliddata', 'mod_lti', '', $contentitemsjson);
|
throw new moodle_exception('errorinvaliddata', 'mod_lti', '', $contentitemsjson);
|
||||||
}
|
}
|
||||||
if ($items->{'@context'} !== 'http://purl.imsglobal.org/ctx/lti/v1/ContentItem') {
|
|
||||||
throw new moodle_exception('errorinvalidmediatype', 'mod_lti', '', $items->{'@context'});
|
|
||||||
}
|
|
||||||
if (!isset($items->{'@graph'}) || !is_array($items->{'@graph'}) || (count($items->{'@graph'}) > 1)) {
|
if (!isset($items->{'@graph'}) || !is_array($items->{'@graph'}) || (count($items->{'@graph'}) > 1)) {
|
||||||
throw new moodle_exception('errorinvalidresponseformat', 'mod_lti');
|
throw new moodle_exception('errorinvalidresponseformat', 'mod_lti');
|
||||||
}
|
}
|
||||||
|
@ -922,7 +946,7 @@ function lti_tool_configuration_from_content_item($typeid, $messagetype, $ltiver
|
||||||
}
|
}
|
||||||
|
|
||||||
function lti_get_tool_table($tools, $id) {
|
function lti_get_tool_table($tools, $id) {
|
||||||
global $CFG, $OUTPUT, $USER;
|
global $OUTPUT;
|
||||||
$html = '';
|
$html = '';
|
||||||
|
|
||||||
$typename = get_string('typename', 'lti');
|
$typename = get_string('typename', 'lti');
|
||||||
|
@ -1124,9 +1148,9 @@ EOD;
|
||||||
/**
|
/**
|
||||||
* Extracts the enabled capabilities into an array, including those implicitly declared in a parameter
|
* Extracts the enabled capabilities into an array, including those implicitly declared in a parameter
|
||||||
*
|
*
|
||||||
* @param object $tool Tool instance object
|
* @param object $tool Tool instance object
|
||||||
*
|
*
|
||||||
* @return Array of enabled capabilities
|
* @return array List of enabled capabilities
|
||||||
*/
|
*/
|
||||||
function lti_get_enabled_capabilities($tool) {
|
function lti_get_enabled_capabilities($tool) {
|
||||||
if (!isset($tool)) {
|
if (!isset($tool)) {
|
||||||
|
@ -1224,10 +1248,11 @@ function lti_get_custom_parameters($toolproxy, $tool, $params, $parameters) {
|
||||||
* @param string $value Custom parameter value
|
* @param string $value Custom parameter value
|
||||||
* @param boolean $islti2 True if an LTI 2 tool is being launched
|
* @param boolean $islti2 True if an LTI 2 tool is being launched
|
||||||
*
|
*
|
||||||
* @return Parsed value of custom parameter
|
* @return string Parsed value of custom parameter
|
||||||
*/
|
*/
|
||||||
function lti_parse_custom_parameter($toolproxy, $tool, $params, $value, $islti2) {
|
function lti_parse_custom_parameter($toolproxy, $tool, $params, $value, $islti2) {
|
||||||
global $USER, $COURSE;
|
// This is required as {${$valarr[0]}->{$valarr[1]}}" may be using the USER var.
|
||||||
|
global $USER;
|
||||||
|
|
||||||
if ($value) {
|
if ($value) {
|
||||||
if (substr($value, 0, 1) == '\\') {
|
if (substr($value, 0, 1) == '\\') {
|
||||||
|
@ -1403,8 +1428,6 @@ function lti_get_tools_by_url($url, $state, $courseid = null) {
|
||||||
function lti_get_tools_by_domain($domain, $state = null, $courseid = null) {
|
function lti_get_tools_by_domain($domain, $state = null, $courseid = null) {
|
||||||
global $DB, $SITE;
|
global $DB, $SITE;
|
||||||
|
|
||||||
$filters = array('tooldomain' => $domain);
|
|
||||||
|
|
||||||
$statefilter = '';
|
$statefilter = '';
|
||||||
$coursefilter = '';
|
$coursefilter = '';
|
||||||
|
|
||||||
|
@ -1433,6 +1456,9 @@ function lti_get_tools_by_domain($domain, $state = null, $courseid = null) {
|
||||||
/**
|
/**
|
||||||
* Returns all basicLTI tools configured by the administrator
|
* Returns all basicLTI tools configured by the administrator
|
||||||
*
|
*
|
||||||
|
* @param int $course
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
*/
|
*/
|
||||||
function lti_filter_get_types($course) {
|
function lti_filter_get_types($course) {
|
||||||
global $DB;
|
global $DB;
|
||||||
|
@ -1698,7 +1724,7 @@ function lti_delete_type($id) {
|
||||||
function lti_set_state_for_type($id, $state) {
|
function lti_set_state_for_type($id, $state) {
|
||||||
global $DB;
|
global $DB;
|
||||||
|
|
||||||
$DB->update_record('lti_types', array('id' => $id, 'state' => $state));
|
$DB->update_record('lti_types', (object)array('id' => $id, 'state' => $state));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1709,7 +1735,6 @@ function lti_set_state_for_type($id, $state) {
|
||||||
* @return array Basic LTI configuration details
|
* @return array Basic LTI configuration details
|
||||||
*/
|
*/
|
||||||
function lti_get_config($ltiobject) {
|
function lti_get_config($ltiobject) {
|
||||||
$typeconfig = array();
|
|
||||||
$typeconfig = (array)$ltiobject;
|
$typeconfig = (array)$ltiobject;
|
||||||
$additionalconfig = lti_get_type_config($ltiobject->typeid);
|
$additionalconfig = lti_get_type_config($ltiobject->typeid);
|
||||||
$typeconfig = array_merge($typeconfig, $additionalconfig);
|
$typeconfig = array_merge($typeconfig, $additionalconfig);
|
||||||
|
@ -1722,7 +1747,7 @@ function lti_get_config($ltiobject) {
|
||||||
*
|
*
|
||||||
* @param int $id
|
* @param int $id
|
||||||
*
|
*
|
||||||
* @return Instance configuration
|
* @return object configuration
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function lti_get_type_config_from_instance($id) {
|
function lti_get_type_config_from_instance($id) {
|
||||||
|
@ -1760,7 +1785,7 @@ function lti_get_type_config_from_instance($id) {
|
||||||
*
|
*
|
||||||
* @param int $id
|
* @param int $id
|
||||||
*
|
*
|
||||||
* @return Configuration details
|
* @return stdClass Configuration details
|
||||||
*/
|
*/
|
||||||
function lti_get_type_type_config($id) {
|
function lti_get_type_type_config($id) {
|
||||||
global $DB;
|
global $DB;
|
||||||
|
@ -1847,6 +1872,10 @@ function lti_get_type_type_config($id) {
|
||||||
$type->lti_contentitem = $config['contentitem'];
|
$type->lti_contentitem = $config['contentitem'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($config['toolurl_ContentItemSelectionRequest'])) {
|
||||||
|
$type->lti_toolurl_ContentItemSelectionRequest = $config['toolurl_ContentItemSelectionRequest'];
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($config['debuglaunch'])) {
|
if (isset($config['debuglaunch'])) {
|
||||||
$type->lti_debuglaunch = $config['debuglaunch'];
|
$type->lti_debuglaunch = $config['debuglaunch'];
|
||||||
}
|
}
|
||||||
|
@ -1855,6 +1884,19 @@ function lti_get_type_type_config($id) {
|
||||||
$type->lti_module_class_type = $config['module_class_type'];
|
$type->lti_module_class_type = $config['module_class_type'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the parameters from the LTI services.
|
||||||
|
$services = lti_get_services();
|
||||||
|
$ltiserviceprefixlength = 11;
|
||||||
|
foreach ($services as $service) {
|
||||||
|
$configurationparameters = $service->get_configuration_parameter_names();
|
||||||
|
foreach ($configurationparameters as $ltiserviceparameter) {
|
||||||
|
$shortltiserviceparameter = substr($ltiserviceparameter, $ltiserviceprefixlength);
|
||||||
|
if (isset($config[$shortltiserviceparameter])) {
|
||||||
|
$type->$ltiserviceparameter = $config[$shortltiserviceparameter];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $type;
|
return $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1886,6 +1928,14 @@ function lti_prepare_type_for_save($type, $config) {
|
||||||
$type->contentitem = !empty($config->lti_contentitem) ? $config->lti_contentitem : 0;
|
$type->contentitem = !empty($config->lti_contentitem) ? $config->lti_contentitem : 0;
|
||||||
$config->lti_contentitem = $type->contentitem;
|
$config->lti_contentitem = $type->contentitem;
|
||||||
}
|
}
|
||||||
|
if (isset($config->lti_toolurl_ContentItemSelectionRequest)) {
|
||||||
|
if (!empty($config->lti_toolurl_ContentItemSelectionRequest)) {
|
||||||
|
$type->toolurl_ContentItemSelectionRequest = $config->lti_toolurl_ContentItemSelectionRequest;
|
||||||
|
} else {
|
||||||
|
$type->toolurl_ContentItemSelectionRequest = '';
|
||||||
|
}
|
||||||
|
$config->lti_toolurl_ContentItemSelectionRequest = $type->toolurl_ContentItemSelectionRequest;
|
||||||
|
}
|
||||||
|
|
||||||
$type->timemodified = time();
|
$type->timemodified = time();
|
||||||
|
|
||||||
|
@ -1901,7 +1951,6 @@ function lti_update_type($type, $config) {
|
||||||
|
|
||||||
lti_prepare_type_for_save($type, $config);
|
lti_prepare_type_for_save($type, $config);
|
||||||
|
|
||||||
$clearcache = false;
|
|
||||||
if (lti_request_is_using_ssl() && !empty($type->secureicon)) {
|
if (lti_request_is_using_ssl() && !empty($type->secureicon)) {
|
||||||
$clearcache = !isset($config->oldicon) || ($config->oldicon !== $type->secureicon);
|
$clearcache = !isset($config->oldicon) || ($config->oldicon !== $type->secureicon);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1918,6 +1967,13 @@ function lti_update_type($type, $config) {
|
||||||
$record->value = $value;
|
$record->value = $value;
|
||||||
lti_update_config($record);
|
lti_update_config($record);
|
||||||
}
|
}
|
||||||
|
if (substr($key, 0, 11) == 'ltiservice_' && !is_null($value)) {
|
||||||
|
$record = new \StdClass();
|
||||||
|
$record->typeid = $type->id;
|
||||||
|
$record->name = substr($key, 11);
|
||||||
|
$record->value = $value;
|
||||||
|
lti_update_config($record);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
require_once($CFG->libdir.'/modinfolib.php');
|
require_once($CFG->libdir.'/modinfolib.php');
|
||||||
if ($clearcache) {
|
if ($clearcache) {
|
||||||
|
@ -1964,10 +2020,17 @@ function lti_add_type($type, $config) {
|
||||||
|
|
||||||
if ($id) {
|
if ($id) {
|
||||||
foreach ($config as $key => $value) {
|
foreach ($config as $key => $value) {
|
||||||
if (substr($key, 0, 4) == 'lti_' && !is_null($value)) {
|
if (!is_null($value)) {
|
||||||
|
$fieldparts = preg_split("/(lti|ltiservice)_/i", $key);
|
||||||
|
// If array has only one element, it did not start with the pattern.
|
||||||
|
if (count($fieldparts) < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$fieldname = $fieldparts[1];
|
||||||
|
|
||||||
$record = new \StdClass();
|
$record = new \StdClass();
|
||||||
$record->typeid = $id;
|
$record->typeid = $id;
|
||||||
$record->name = substr($key, 4);
|
$record->name = $fieldname;
|
||||||
$record->value = $value;
|
$record->value = $value;
|
||||||
|
|
||||||
lti_add_config($record);
|
lti_add_config($record);
|
||||||
|
@ -2033,7 +2096,7 @@ function lti_get_tool_proxies_from_registration_url($regurl) {
|
||||||
*
|
*
|
||||||
* @param int $id
|
* @param int $id
|
||||||
*
|
*
|
||||||
* @return Tool Proxy details
|
* @return mixed Tool Proxy details
|
||||||
*/
|
*/
|
||||||
function lti_get_tool_proxy($id) {
|
function lti_get_tool_proxy($id) {
|
||||||
global $DB;
|
global $DB;
|
||||||
|
@ -2052,7 +2115,6 @@ function lti_get_tool_proxies($orphanedonly) {
|
||||||
global $DB;
|
global $DB;
|
||||||
|
|
||||||
if ($orphanedonly) {
|
if ($orphanedonly) {
|
||||||
$tools = $DB->get_records('lti_types');
|
|
||||||
$usedproxyids = array_values($DB->get_fieldset_select('lti_types', 'toolproxyid', 'toolproxyid IS NOT NULL'));
|
$usedproxyids = array_values($DB->get_fieldset_select('lti_types', 'toolproxyid', 'toolproxyid IS NOT NULL'));
|
||||||
$proxies = $DB->get_records('lti_tool_proxies', null, 'state DESC, timemodified DESC');
|
$proxies = $DB->get_records('lti_tool_proxies', null, 'state DESC, timemodified DESC');
|
||||||
foreach ($proxies as $key => $value) {
|
foreach ($proxies as $key => $value) {
|
||||||
|
@ -2071,7 +2133,7 @@ function lti_get_tool_proxies($orphanedonly) {
|
||||||
*
|
*
|
||||||
* @param int $id
|
* @param int $id
|
||||||
*
|
*
|
||||||
* @return Tool Proxy details
|
* @return mixed Tool Proxy details
|
||||||
*/
|
*/
|
||||||
function lti_get_tool_proxy_config($id) {
|
function lti_get_tool_proxy_config($id) {
|
||||||
$toolproxy = lti_get_tool_proxy($id);
|
$toolproxy = lti_get_tool_proxy($id);
|
||||||
|
@ -2190,12 +2252,11 @@ function lti_add_config($config) {
|
||||||
*
|
*
|
||||||
* @param object $config Tool configuration
|
* @param object $config Tool configuration
|
||||||
*
|
*
|
||||||
* @return Record id number
|
* @return mixed Record id number
|
||||||
*/
|
*/
|
||||||
function lti_update_config($config) {
|
function lti_update_config($config) {
|
||||||
global $DB;
|
global $DB;
|
||||||
|
|
||||||
$return = true;
|
|
||||||
$old = $DB->get_record('lti_types_config', array('typeid' => $config->typeid, 'name' => $config->name));
|
$old = $DB->get_record('lti_types_config', array('typeid' => $config->typeid, 'name' => $config->name));
|
||||||
|
|
||||||
if ($old) {
|
if ($old) {
|
||||||
|
@ -2243,7 +2304,7 @@ function lti_set_tool_settings($settings, $toolproxyid, $courseid = null, $insta
|
||||||
$record = $DB->get_record('lti_tool_settings', array('toolproxyid' => $toolproxyid,
|
$record = $DB->get_record('lti_tool_settings', array('toolproxyid' => $toolproxyid,
|
||||||
'course' => $courseid, 'coursemoduleid' => $instanceid));
|
'course' => $courseid, 'coursemoduleid' => $instanceid));
|
||||||
if ($record !== false) {
|
if ($record !== false) {
|
||||||
$DB->update_record('lti_tool_settings', array('id' => $record->id, 'settings' => $json, 'timemodified' => time()));
|
$DB->update_record('lti_tool_settings', (object)array('id' => $record->id, 'settings' => $json, 'timemodified' => time()));
|
||||||
} else {
|
} else {
|
||||||
$record = new \stdClass();
|
$record = new \stdClass();
|
||||||
$record->toolproxyid = $toolproxyid;
|
$record->toolproxyid = $toolproxyid;
|
||||||
|
@ -2259,11 +2320,12 @@ function lti_set_tool_settings($settings, $toolproxyid, $courseid = null, $insta
|
||||||
/**
|
/**
|
||||||
* Signs the petition to launch the external tool using OAuth
|
* Signs the petition to launch the external tool using OAuth
|
||||||
*
|
*
|
||||||
* @param $oldparms Parameters to be passed for signing
|
* @param array $oldparms Parameters to be passed for signing
|
||||||
* @param $endpoint url of the external tool
|
* @param string $endpoint url of the external tool
|
||||||
* @param $method Method for sending the parameters (e.g. POST)
|
* @param string $method Method for sending the parameters (e.g. POST)
|
||||||
* @param $oauth_consumoer_key Key
|
* @param string $oauthconsumerkey
|
||||||
* @param $oauth_consumoer_secret Secret
|
* @param string $oauthconsumersecret
|
||||||
|
* @return array|null
|
||||||
*/
|
*/
|
||||||
function lti_sign_parameters($oldparms, $endpoint, $method, $oauthconsumerkey, $oauthconsumersecret) {
|
function lti_sign_parameters($oldparms, $endpoint, $method, $oauthconsumerkey, $oauthconsumersecret) {
|
||||||
|
|
||||||
|
@ -2285,9 +2347,10 @@ function lti_sign_parameters($oldparms, $endpoint, $method, $oauthconsumerkey, $
|
||||||
/**
|
/**
|
||||||
* Posts the launch petition HTML
|
* Posts the launch petition HTML
|
||||||
*
|
*
|
||||||
* @param $newparms Signed parameters
|
* @param array $newparms Signed parameters
|
||||||
* @param $endpoint URL of the external tool
|
* @param string $endpoint URL of the external tool
|
||||||
* @param $debug Debug (true/false)
|
* @param bool $debug Debug (true/false)
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
function lti_post_launch_html($newparms, $endpoint, $debug=false) {
|
function lti_post_launch_html($newparms, $endpoint, $debug=false) {
|
||||||
$r = "<form action=\"" . $endpoint .
|
$r = "<form action=\"" . $endpoint .
|
||||||
|
@ -2607,7 +2670,7 @@ function lti_get_services() {
|
||||||
*
|
*
|
||||||
* @param string $servicename Name of service
|
* @param string $servicename Name of service
|
||||||
*
|
*
|
||||||
* @return mod_lti\local\ltiservice\service_base Service
|
* @return bool|\mod_lti\local\ltiservice\service_base Service
|
||||||
*/
|
*/
|
||||||
function lti_get_service_by_name($servicename) {
|
function lti_get_service_by_name($servicename) {
|
||||||
|
|
||||||
|
@ -2624,7 +2687,7 @@ function lti_get_service_by_name($servicename) {
|
||||||
/**
|
/**
|
||||||
* Finds a service by id
|
* Finds a service by id
|
||||||
*
|
*
|
||||||
* @param array $services Array of services
|
* @param \mod_lti\local\ltiservice\service_base[] $services Array of services
|
||||||
* @param string $resourceid ID of resource
|
* @param string $resourceid ID of resource
|
||||||
*
|
*
|
||||||
* @return mod_lti\local\ltiservice\service_base Service
|
* @return mod_lti\local\ltiservice\service_base Service
|
||||||
|
@ -2742,15 +2805,14 @@ function get_tool_proxy_edit_url(stdClass $proxy) {
|
||||||
*
|
*
|
||||||
* @param stdClass $type The tool type
|
* @param stdClass $type The tool type
|
||||||
*
|
*
|
||||||
* @return string|void The url to the course of the tool type, void if it is a site wide type
|
* @return string The url to the course of the tool type, void if it is a site wide type
|
||||||
*/
|
*/
|
||||||
function get_tool_type_course_url(stdClass $type) {
|
function get_tool_type_course_url(stdClass $type) {
|
||||||
if ($type->course == 1) {
|
if ($type->course != 1) {
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
$url = new moodle_url('/course/view.php', array('id' => $type->course));
|
$url = new moodle_url('/course/view.php', array('id' => $type->course));
|
||||||
return $url->out();
|
return $url->out();
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2758,7 +2820,7 @@ function get_tool_type_course_url(stdClass $type) {
|
||||||
*
|
*
|
||||||
* @param stdClass $type The tool type
|
* @param stdClass $type The tool type
|
||||||
*
|
*
|
||||||
* @return string The urls of the tool type
|
* @return array The urls of the tool type
|
||||||
*/
|
*/
|
||||||
function get_tool_type_urls(stdClass $type) {
|
function get_tool_type_urls(stdClass $type) {
|
||||||
$courseurl = get_tool_type_course_url($type);
|
$courseurl = get_tool_type_course_url($type);
|
||||||
|
@ -2780,7 +2842,7 @@ function get_tool_type_urls(stdClass $type) {
|
||||||
*
|
*
|
||||||
* @param stdClass $proxy The tool proxy
|
* @param stdClass $proxy The tool proxy
|
||||||
*
|
*
|
||||||
* @return string The urls of the tool proxy
|
* @return array The urls of the tool proxy
|
||||||
*/
|
*/
|
||||||
function get_tool_proxy_urls(stdClass $proxy) {
|
function get_tool_proxy_urls(stdClass $proxy) {
|
||||||
global $OUTPUT;
|
global $OUTPUT;
|
||||||
|
@ -2802,7 +2864,6 @@ function get_tool_proxy_urls(stdClass $proxy) {
|
||||||
* pending, configured, rejected, unknown
|
* pending, configured, rejected, unknown
|
||||||
*/
|
*/
|
||||||
function get_tool_type_state_info(stdClass $type) {
|
function get_tool_type_state_info(stdClass $type) {
|
||||||
$state = '';
|
|
||||||
$isconfigured = false;
|
$isconfigured = false;
|
||||||
$ispending = false;
|
$ispending = false;
|
||||||
$isrejected = false;
|
$isrejected = false;
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file contains the class for restore of this gradebookservices plugin
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @author Dirk Singels, Diego del Blanco
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
global $CFG;
|
||||||
|
|
||||||
|
require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the information to backup gradebookservices lineitems
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @author Dirk Singels, Diego del Blanco, Claude Vervoort
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class backup_ltiservice_gradebookservices_subplugin extends backup_subplugin {
|
||||||
|
|
||||||
|
/** TypeId contained in DB but is invalid */
|
||||||
|
const NONVALIDTYPEID = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the subplugin information to attach to submission element
|
||||||
|
* @return backup_subplugin_element
|
||||||
|
*/
|
||||||
|
protected function define_lti_subplugin_structure() {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
// Create XML elements.
|
||||||
|
$subplugin = $this->get_subplugin_element();
|
||||||
|
$subpluginwrapper = new backup_nested_element($this->get_recommended_name());
|
||||||
|
// The gbs entries related with this element.
|
||||||
|
$lineitems = new backup_nested_element('lineitems');
|
||||||
|
$lineitem = new backup_nested_element('lineitem', array('id'), array(
|
||||||
|
'gradeitemid',
|
||||||
|
'courseid',
|
||||||
|
'toolproxyid',
|
||||||
|
'typeid',
|
||||||
|
'baseurl',
|
||||||
|
'ltilinkid',
|
||||||
|
'tag',
|
||||||
|
'vendorcode',
|
||||||
|
'guid'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Build the tree.
|
||||||
|
$subplugin->add_child($subpluginwrapper);
|
||||||
|
$subpluginwrapper->add_child($lineitems);
|
||||||
|
$lineitems->add_child($lineitem);
|
||||||
|
|
||||||
|
// We need to know the actual activity tool or toolproxy.
|
||||||
|
// If and activity is assigned to a type that doesn't exists we don't want to backup any related lineitems.``
|
||||||
|
// Default to invalid condition.
|
||||||
|
$typeid = 0;
|
||||||
|
$toolproxyid = '0';
|
||||||
|
|
||||||
|
/* cache parent property to account for missing PHPDoc type specification */
|
||||||
|
/** @var backup_activity_task $activitytask */
|
||||||
|
$activitytask = $this->task;
|
||||||
|
$activityid = $activitytask->get_activityid();
|
||||||
|
$activitycourseid = $activitytask->get_courseid();
|
||||||
|
$lti = $DB->get_record('lti', ['id' => $activityid], 'typeid, toolurl, securetoolurl');
|
||||||
|
$ltitype = $DB->get_record('lti_types', ['id' => $lti->typeid], 'toolproxyid, baseurl');
|
||||||
|
if ($ltitype) {
|
||||||
|
$typeid = $lti->typeid;
|
||||||
|
$toolproxyid = $ltitype->toolproxyid;
|
||||||
|
} else if ($lti->typeid == self::NONVALIDTYPEID) { // This activity comes from an old backup.
|
||||||
|
// 1. Let's check if the activity is coupled. If so, find the values in the GBS element.
|
||||||
|
$gbsrecord = $DB->get_record('ltiservice_gradebookservices',
|
||||||
|
['ltilinkid' => $activityid], 'typeid,toolproxyid,baseurl');
|
||||||
|
if ($gbsrecord) {
|
||||||
|
$typeid = $gbsrecord->typeid;
|
||||||
|
$toolproxyid = $gbsrecord->toolproxyid;
|
||||||
|
} else { // 2. If it is uncoupled... we will need to guess the right activity typeid
|
||||||
|
// Guess the typeid for the activity.
|
||||||
|
$tool = lti_get_tool_by_url_match($lti->toolurl, $activitycourseid);
|
||||||
|
if (!$tool) {
|
||||||
|
$tool = lti_get_tool_by_url_match($lti->securetoolurl, $activitycourseid);
|
||||||
|
}
|
||||||
|
if ($tool) {
|
||||||
|
$alttypeid = $tool->id;
|
||||||
|
// If we have a valid typeid then get types again.
|
||||||
|
if ($alttypeid != self::NONVALIDTYPEID) {
|
||||||
|
$ltitype = $DB->get_record('lti_types', ['id' => $alttypeid], 'toolproxyid, baseurl');
|
||||||
|
$toolproxyid = $ltitype->toolproxyid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define sources.
|
||||||
|
if ($toolproxyid != null) {
|
||||||
|
$lineitemssql = "SELECT l.*, t.vendorcode as vendorcode, t.guid as guid
|
||||||
|
FROM {ltiservice_gradebookservices} l
|
||||||
|
INNER JOIN {lti_tool_proxies} t ON (t.id = l.toolproxyid)
|
||||||
|
WHERE l.courseid = ?
|
||||||
|
AND l.toolproxyid = ?
|
||||||
|
AND l.typeid is null";
|
||||||
|
$lineitemsparams = ['courseid' => backup::VAR_COURSEID, backup_helper::is_sqlparam($toolproxyid)];
|
||||||
|
} else {
|
||||||
|
$lineitemssql = "SELECT l.*, null as vendorcode, null as guid
|
||||||
|
FROM {ltiservice_gradebookservices} l
|
||||||
|
WHERE l.courseid = ?
|
||||||
|
AND l.typeid = ?
|
||||||
|
AND l.toolproxyid is null";
|
||||||
|
$lineitemsparams = ['courseid' => backup::VAR_COURSEID, backup_helper::is_sqlparam($typeid)];
|
||||||
|
}
|
||||||
|
|
||||||
|
$lineitem->set_source_sql($lineitemssql, $lineitemsparams);
|
||||||
|
|
||||||
|
return $subplugin;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file contains the class for restore of this gradebookservices plugin
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @author Dirk Singels, Diego del Blanco, Claude Vervoort
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
global $CFG;
|
||||||
|
require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore subplugin class.
|
||||||
|
*
|
||||||
|
* Provides the necessary information
|
||||||
|
* needed to restore the lineitems related with the lti activity (coupled),
|
||||||
|
* and all the uncoupled ones from the course.
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @author Dirk Singels, Diego del Blanco, Claude Vervoort
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class restore_ltiservice_gradebookservices_subplugin extends restore_subplugin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the subplugin structure to attach to the XML element.
|
||||||
|
*
|
||||||
|
* @return restore_path_element[] array of elements to be processed on restore.
|
||||||
|
*/
|
||||||
|
protected function define_lti_subplugin_structure() {
|
||||||
|
|
||||||
|
$paths = array();
|
||||||
|
$elename = $this->get_namefor('lineitem');
|
||||||
|
$elepath = $this->get_pathfor('/lineitems/lineitem');
|
||||||
|
$paths[] = new restore_path_element($elename, $elepath);
|
||||||
|
return $paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes one lineitem
|
||||||
|
*
|
||||||
|
* @param mixed $data
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function process_ltiservice_gradebookservices_lineitem($data) {
|
||||||
|
global $DB;
|
||||||
|
$data = (object)$data;
|
||||||
|
// The coupled lineitems are restored as any other grade item
|
||||||
|
// so we will only create the entry in the ltiservice_gradebookservices table.
|
||||||
|
// We will try to find a valid toolproxy in the system.
|
||||||
|
// If it has been found before... we use it.
|
||||||
|
/* cache parent property to account for missing PHPDoc type specification */
|
||||||
|
/** @var backup_activity_task $activitytask */
|
||||||
|
$activitytask = $this->task;
|
||||||
|
$courseid = $activitytask->get_courseid();
|
||||||
|
if ($data->typeid != null) {
|
||||||
|
if ($ltitypeid = $this->get_mappingid('ltitype', $data->typeid)) {
|
||||||
|
$newtypeid = $ltitypeid;
|
||||||
|
} else { // If not, then we will call our own function to find it.
|
||||||
|
$newtypeid = $this->find_typeid($data, $courseid);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$newtypeid = null;
|
||||||
|
}
|
||||||
|
if ($data->toolproxyid != null) {
|
||||||
|
$ltitoolproxy = $this->get_mappingid('ltitoolproxy', $data->toolproxyid);
|
||||||
|
if ($ltitoolproxy && $ltitoolproxy != 0) {
|
||||||
|
$newtoolproxyid = $ltitoolproxy;
|
||||||
|
} else { // If not, then we will call our own function to find it.
|
||||||
|
$newtoolproxyid = $this->find_proxy_id($data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$newtoolproxyid = null;
|
||||||
|
}
|
||||||
|
if ($data->ltilinkid != null) {
|
||||||
|
$ltilinkid = $this->get_new_parentid('lti');
|
||||||
|
} else {
|
||||||
|
$ltilinkid = null;
|
||||||
|
}
|
||||||
|
// If this has not been restored before.
|
||||||
|
if ($this->get_mappingid('gbsgradeitemrestored', $data->id, 0) == 0) {
|
||||||
|
$newgbsid = $DB->insert_record('ltiservice_gradebookservices', (object) array(
|
||||||
|
'gradeitemid' => 0,
|
||||||
|
'courseid' => $courseid,
|
||||||
|
'toolproxyid' => $newtoolproxyid,
|
||||||
|
'ltilinkid' => $ltilinkid,
|
||||||
|
'typeid' => $newtypeid,
|
||||||
|
'baseurl' => $data->baseurl,
|
||||||
|
'tag' => $data->tag
|
||||||
|
));
|
||||||
|
$this->set_mapping('gbsgradeitemoldid', $newgbsid, $data->gradeitemid);
|
||||||
|
$this->set_mapping('gbsgradeitemrestored', $data->id, $data->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the toolproxy is not in the mapping (or it is 0)
|
||||||
|
* we try to find the toolproxyid.
|
||||||
|
* If none is found, then we set it to 0.
|
||||||
|
*
|
||||||
|
* @param mixed $data
|
||||||
|
* @return integer $newtoolproxyid
|
||||||
|
*/
|
||||||
|
private function find_proxy_id($data) {
|
||||||
|
global $DB;
|
||||||
|
$newtoolproxyid = 0;
|
||||||
|
$oldtoolproxyguid = $data->guid;
|
||||||
|
$oldtoolproxyvendor = $data->vendorcode;
|
||||||
|
|
||||||
|
$dbtoolproxyjsonparams = array('guid' => $oldtoolproxyguid, 'vendorcode' => $oldtoolproxyvendor);
|
||||||
|
$dbtoolproxy = $DB->get_field('lti_tool_proxies', 'id', $dbtoolproxyjsonparams, IGNORE_MISSING);
|
||||||
|
if ($dbtoolproxy) {
|
||||||
|
$newtoolproxyid = $dbtoolproxy;
|
||||||
|
}
|
||||||
|
return $newtoolproxyid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the typeid is not in the mapping or it is 0, (it should be most of the times)
|
||||||
|
* we will try to find the better typeid that matches with the lineitem.
|
||||||
|
* If none is found, then we set it to 0.
|
||||||
|
*
|
||||||
|
* @param stdClass $data
|
||||||
|
* @param int $courseid
|
||||||
|
* @return int The item type id
|
||||||
|
*/
|
||||||
|
private function find_typeid($data, $courseid) {
|
||||||
|
global $DB;
|
||||||
|
$newtypeid = 0;
|
||||||
|
$oldtypeid = $data->typeid;
|
||||||
|
|
||||||
|
// 1. Find a type with the same id in the same course.
|
||||||
|
$dbtypeidparameter = array('id' => $oldtypeid, 'course' => $courseid, 'baseurl' => $data->baseurl);
|
||||||
|
$dbtype = $DB->get_field_select('lti_types', 'id', "id=:id
|
||||||
|
AND course=:course AND ".$DB->sql_compare_text('baseurl')."=:baseurl",
|
||||||
|
$dbtypeidparameter);
|
||||||
|
if ($dbtype) {
|
||||||
|
$newtypeid = $dbtype;
|
||||||
|
} else {
|
||||||
|
// 2. Find a site type for all the courses (course == 1), but with the same id.
|
||||||
|
$dbtypeidparameter = array('id' => $oldtypeid, 'baseurl' => $data->baseurl);
|
||||||
|
$dbtype = $DB->get_field_select('lti_types', 'id', "id=:id
|
||||||
|
AND course=1 AND ".$DB->sql_compare_text('baseurl')."=:baseurl",
|
||||||
|
$dbtypeidparameter);
|
||||||
|
if ($dbtype) {
|
||||||
|
$newtypeid = $dbtype;
|
||||||
|
} else {
|
||||||
|
// 3. Find a type with the same baseurl in the actual site.
|
||||||
|
$dbtypeidparameter = array('course' => $courseid, 'baseurl' => $data->baseurl);
|
||||||
|
$dbtype = $DB->get_field_select('lti_types', 'id', "course=:course
|
||||||
|
AND ".$DB->sql_compare_text('baseurl')."=:baseurl",
|
||||||
|
$dbtypeidparameter);
|
||||||
|
if ($dbtype) {
|
||||||
|
$newtypeid = $dbtype;
|
||||||
|
} else {
|
||||||
|
// 4. Find a site type for all the courses (course == 1) with the same baseurl.
|
||||||
|
$dbtypeidparameter = array('course' => 1, 'baseurl' => $data->baseurl);
|
||||||
|
$dbtype = $DB->get_field_select('lti_types', 'id', "course=1
|
||||||
|
AND ".$DB->sql_compare_text('baseurl')."=:baseurl",
|
||||||
|
$dbtypeidparameter);
|
||||||
|
if ($dbtype) {
|
||||||
|
$newtypeid = $dbtype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $newtypeid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We call the after_restore_lti to update the grade_items id's that we didn't know in the moment of creating
|
||||||
|
* the gradebookservices rows.
|
||||||
|
*/
|
||||||
|
protected function after_restore_lti() {
|
||||||
|
global $DB;
|
||||||
|
$activitytask = $this->task;
|
||||||
|
$courseid = $activitytask->get_courseid();
|
||||||
|
$gbstoupdate = $DB->get_records('ltiservice_gradebookservices', array('gradeitemid' => 0, 'courseid' => $courseid));
|
||||||
|
foreach ($gbstoupdate as $gbs) {
|
||||||
|
$oldgradeitemid = $this->get_mappingid('gbsgradeitemoldid', $gbs->id, 0);
|
||||||
|
$newgradeitemid = $this->get_mappingid('grade_item', $oldgradeitemid, 0);
|
||||||
|
if ($newgradeitemid > 0) {
|
||||||
|
$gbs->gradeitemid = $newgradeitemid;
|
||||||
|
$DB->update_record('ltiservice_gradebookservices', $gbs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,365 @@
|
||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file contains a class definition for the LineItem resource
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @author Dirk Singels, Diego del Blanco, Claude Vervoort
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ltiservice_gradebookservices\local\resources;
|
||||||
|
|
||||||
|
use ltiservice_gradebookservices\local\service\gradebookservices;
|
||||||
|
use mod_lti\local\ltiservice\resource_base;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A resource implementing LineItem.
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class lineitem extends resource_base {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor.
|
||||||
|
*
|
||||||
|
* @param gradebookservices $service Service instance
|
||||||
|
*/
|
||||||
|
public function __construct($service) {
|
||||||
|
|
||||||
|
parent::__construct($service);
|
||||||
|
$this->id = 'LineItem.item';
|
||||||
|
$this->template = '/{context_id}/lineitems/{item_id}/lineitem';
|
||||||
|
$this->variables[] = 'LineItem.url';
|
||||||
|
$this->formats[] = 'application/vnd.ims.lis.v2.lineitem+json';
|
||||||
|
$this->methods[] = self::HTTP_GET;
|
||||||
|
$this->methods[] = self::HTTP_PUT;
|
||||||
|
$this->methods[] = self::HTTP_DELETE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the request for this resource.
|
||||||
|
*
|
||||||
|
* @param \mod_lti\local\ltiservice\response $response Response object for this request.
|
||||||
|
*/
|
||||||
|
public function execute($response) {
|
||||||
|
global $CFG, $DB;
|
||||||
|
|
||||||
|
$params = $this->parse_template();
|
||||||
|
$contextid = $params['context_id'];
|
||||||
|
$itemid = $params['item_id'];
|
||||||
|
if ($response->get_request_method() === 'GET') {
|
||||||
|
$contenttype = $response->get_accept();
|
||||||
|
} else {
|
||||||
|
$contenttype = $response->get_content_type();
|
||||||
|
}
|
||||||
|
// We will receive typeid when working with LTI 1.x, if not then we are in LTI 2.
|
||||||
|
$typeid = optional_param('type_id', null, PARAM_ALPHANUM);
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
if (!$this->check_tool_proxy(null, $response->get_request_data())) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("Invalid tool proxy specified.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch ($response->get_request_method()) {
|
||||||
|
case self::HTTP_GET:
|
||||||
|
if (!$this->check_type($typeid, $contextid, 'LineItem.item:get', $response->get_request_data())) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("This resource does not support GET requests.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case self::HTTP_PUT:
|
||||||
|
if (!$this->check_type($typeid, $contextid, 'LineItem.item:put', $response->get_request_data())) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("This resource does not support PUT requests.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case self::HTTP_DELETE:
|
||||||
|
if (!$this->check_type($typeid, $contextid, 'LineItem.item:delete', $response->get_request_data())) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("This resource does not support DELETE requests.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: // Should not be possible.
|
||||||
|
$response->set_code(405);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($contextid) || (!empty($contenttype) && !in_array($contenttype, $this->formats))) {
|
||||||
|
$response->set_code(400);
|
||||||
|
$response->set_reason("Invalid request made.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$DB->record_exists('course', array('id' => $contextid))) {
|
||||||
|
$response->set_code(404);
|
||||||
|
$response->set_reason("Not Found: Course $contextid doesn't exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$DB->record_exists('grade_items', array('id' => $itemid))) {
|
||||||
|
$response->set_code(404);
|
||||||
|
$response->set_reason("Not Found: Grade item $itemid doesn't exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$item = $this->get_service()->get_lineitem($contextid, $itemid, $typeid);
|
||||||
|
if ($item === false) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("Line item does not exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
require_once($CFG->libdir.'/gradelib.php');
|
||||||
|
switch ($response->get_request_method()) {
|
||||||
|
case 'GET':
|
||||||
|
$this->get_request($response, $item, $typeid);
|
||||||
|
break;
|
||||||
|
case 'PUT':
|
||||||
|
$json = $this->process_put_request($response->get_request_data(), $item, $typeid);
|
||||||
|
$response->set_body($json);
|
||||||
|
$response->set_code(200);
|
||||||
|
break;
|
||||||
|
case 'DELETE':
|
||||||
|
$this->process_delete_request($item);
|
||||||
|
$response->set_code(204);
|
||||||
|
break;
|
||||||
|
default: // Should not be possible.
|
||||||
|
$response->set_code(405);
|
||||||
|
$response->set_reason("Invalid request method specified.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a GET request.
|
||||||
|
*
|
||||||
|
* @param \mod_lti\local\ltiservice\response $response Response object for this request.
|
||||||
|
* @param object $item Grade item instance.
|
||||||
|
* @param string $typeid Tool Type Id
|
||||||
|
*/
|
||||||
|
private function get_request($response, $item, $typeid) {
|
||||||
|
|
||||||
|
$response->set_content_type($this->formats[0]);
|
||||||
|
$lineitem = gradebookservices::item_for_json($item, substr(parent::get_endpoint(),
|
||||||
|
0, strrpos(parent::get_endpoint(), "/", -10)), $typeid);
|
||||||
|
$response->set_body(json_encode($lineitem));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a PUT request.
|
||||||
|
*
|
||||||
|
* @param string $body PUT body
|
||||||
|
* @param \ltiservice_gradebookservices\local\resources\lineitem $olditem Grade item instance
|
||||||
|
* @param string $typeid Tool Type Id
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
private function process_put_request($body, $olditem, $typeid) {
|
||||||
|
global $DB;
|
||||||
|
$json = json_decode($body);
|
||||||
|
if (empty($json) ||
|
||||||
|
!isset($json->scoreMaximum) ||
|
||||||
|
!isset($json->label)) {
|
||||||
|
throw new \Exception(null, 400);
|
||||||
|
}
|
||||||
|
$item = \grade_item::fetch(array('id' => $olditem->id, 'courseid' => $olditem->courseid));
|
||||||
|
$gbs = gradebookservices::find_ltiservice_gradebookservice_for_lineitem($olditem->id);
|
||||||
|
$updategradeitem = false;
|
||||||
|
$rescalegrades = false;
|
||||||
|
$oldgrademax = grade_floatval($item->grademax);
|
||||||
|
$upgradegradebookservices = false;
|
||||||
|
if ($item->itemname !== $json->label) {
|
||||||
|
$updategradeitem = true;
|
||||||
|
}
|
||||||
|
$item->itemname = $json->label;
|
||||||
|
if (!is_numeric($json->scoreMaximum)) {
|
||||||
|
throw new \Exception(null, 400);
|
||||||
|
} else {
|
||||||
|
if (grade_floats_different($oldgrademax,
|
||||||
|
grade_floatval($json->scoreMaximum))) {
|
||||||
|
$updategradeitem = true;
|
||||||
|
$rescalegrades = true;
|
||||||
|
}
|
||||||
|
$item->grademax = grade_floatval($json->scoreMaximum);
|
||||||
|
}
|
||||||
|
$resourceid = (isset($json->resourceId)) ? $json->resourceId : '';
|
||||||
|
if ($item->idnumber !== $resourceid) {
|
||||||
|
$updategradeitem = true;
|
||||||
|
}
|
||||||
|
$item->idnumber = $resourceid;
|
||||||
|
if ($gbs) {
|
||||||
|
$tag = (isset($json->tag)) ? $json->tag : null;
|
||||||
|
if ($gbs->tag !== $tag) {
|
||||||
|
$upgradegradebookservices = true;
|
||||||
|
}
|
||||||
|
$gbs->tag = $tag;
|
||||||
|
}
|
||||||
|
$ltilinkid = null;
|
||||||
|
if (isset($json->ltiLinkId)) {
|
||||||
|
if (is_numeric($json->ltiLinkId)) {
|
||||||
|
$ltilinkid = $json->ltiLinkId;
|
||||||
|
if ($gbs) {
|
||||||
|
if (intval($gbs->ltilinkid) !== intval($json->ltiLinkId)) {
|
||||||
|
$gbs->ltilinkid = $json->ltiLinkId;
|
||||||
|
$upgradegradebookservices = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (intval($item->iteminstance) !== intval($json->ltiLinkId)) {
|
||||||
|
$item->iteminstance = intval($json->ltiLinkId);
|
||||||
|
$updategradeitem = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new \Exception(null, 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($ltilinkid != null) {
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
if (!gradebookservices::check_lti_id($ltilinkid, $item->courseid,
|
||||||
|
$this->get_service()->get_tool_proxy()->id)) {
|
||||||
|
throw new \Exception(null, 403);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!gradebookservices::check_lti_1x_id($ltilinkid, $item->courseid,
|
||||||
|
$typeid)) {
|
||||||
|
throw new \Exception(null, 403);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($updategradeitem) {
|
||||||
|
if (!$item->update('mod/ltiservice_gradebookservices')) {
|
||||||
|
throw new \Exception(null, 500);
|
||||||
|
}
|
||||||
|
if ($rescalegrades) {
|
||||||
|
$item->rescale_grades_keep_percentage(0, $oldgrademax, 0, $item->grademax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$lineitem = new lineitem($this->get_service());
|
||||||
|
$endpoint = $lineitem->get_endpoint();
|
||||||
|
|
||||||
|
if ($upgradegradebookservices) {
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
$toolproxyid = $this->get_service()->get_tool_proxy()->id;
|
||||||
|
$baseurl = null;
|
||||||
|
} else {
|
||||||
|
$toolproxyid = null;
|
||||||
|
$baseurl = lti_get_type_type_config($typeid)->lti_toolurl;
|
||||||
|
}
|
||||||
|
$DB->update_record('ltiservice_gradebookservices', (object)array(
|
||||||
|
'id' => $gbs->id,
|
||||||
|
'gradeitemid' => $gbs->gradeitemid,
|
||||||
|
'courseid' => $gbs->courseid,
|
||||||
|
'toolproxyid' => $toolproxyid,
|
||||||
|
'typeid' => $typeid,
|
||||||
|
'baseurl' => $baseurl,
|
||||||
|
'ltilinkid' => $ltilinkid,
|
||||||
|
'tag' => $gbs->tag
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
$id = "{$endpoint}";
|
||||||
|
$json->id = $id;
|
||||||
|
} else {
|
||||||
|
$id = "{$endpoint}?type_id={$typeid}";
|
||||||
|
$json->id = $id;
|
||||||
|
}
|
||||||
|
return json_encode($json, JSON_UNESCAPED_SLASHES);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a DELETE request.
|
||||||
|
*
|
||||||
|
* @param \ltiservice_gradebookservices\local\resources\lineitem $item Grade item instance
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
private function process_delete_request($item) {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
$gradeitem = \grade_item::fetch(array('id' => $item->id));
|
||||||
|
if (($gbs = gradebookservices::find_ltiservice_gradebookservice_for_lineitem($item->id)) == false) {
|
||||||
|
throw new \Exception(null, 403);
|
||||||
|
}
|
||||||
|
if (!$gradeitem->delete('mod/ltiservice_gradebookservices')) {
|
||||||
|
throw new \Exception(null, 500);
|
||||||
|
} else {
|
||||||
|
$sqlparams = array();
|
||||||
|
$sqlparams['id'] = $gbs->id;
|
||||||
|
if (!$DB->delete_records('ltiservice_gradebookservices', $sqlparams)) {
|
||||||
|
throw new \Exception(null, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get permissions from the config of the tool for that resource
|
||||||
|
*
|
||||||
|
* @param int $typeid
|
||||||
|
*
|
||||||
|
* @return array with the permissions related to this resource by the $lti_type or null if none.
|
||||||
|
*/
|
||||||
|
public function get_permissions($typeid) {
|
||||||
|
$tool = lti_get_type_type_config($typeid);
|
||||||
|
if ($tool->ltiservice_gradesynchronization == '1') {
|
||||||
|
return array('LineItem.item:get');
|
||||||
|
} else if ($tool->ltiservice_gradesynchronization == '2') {
|
||||||
|
return array('LineItem.item:get', 'LineItem.item:put', 'LineItem.item:delete');
|
||||||
|
} else {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a value for custom parameter substitution variables.
|
||||||
|
*
|
||||||
|
* @param string $value String to be parsed
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function parse_value($value) {
|
||||||
|
global $COURSE, $CFG;
|
||||||
|
if (strpos($value, '$LineItem.url') !== false) {
|
||||||
|
$resolved = '';
|
||||||
|
require_once($CFG->libdir . '/gradelib.php');
|
||||||
|
|
||||||
|
$this->params['context_id'] = $COURSE->id;
|
||||||
|
$id = optional_param('id', 0, PARAM_INT); // Course Module ID.
|
||||||
|
if (!empty($id)) {
|
||||||
|
$cm = get_coursemodule_from_id('lti', $id, 0, false, MUST_EXIST);
|
||||||
|
$id = $cm->instance;
|
||||||
|
$item = grade_get_grades($COURSE->id, 'mod', 'lti', $id);
|
||||||
|
if ($item && $item->items) {
|
||||||
|
$this->params['item_id'] = $item->items[0]->id;
|
||||||
|
$resolved = parent::get_endpoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$value = str_replace('$LineItem.url', $resolved, $value);
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,352 @@
|
||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file contains a class definition for the LineItem container resource
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @author Dirk Singels, Diego del Blanco, Claude Vervoort
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ltiservice_gradebookservices\local\resources;
|
||||||
|
|
||||||
|
use ltiservice_gradebookservices\local\service\gradebookservices;
|
||||||
|
use mod_lti\local\ltiservice\resource_base;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A resource implementing LineItem container.
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class lineitems extends resource_base {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor.
|
||||||
|
*
|
||||||
|
* @param \ltiservice_gradebookservices\local\service\gradebookservices $service Service instance
|
||||||
|
*/
|
||||||
|
public function __construct($service) {
|
||||||
|
|
||||||
|
parent::__construct($service);
|
||||||
|
$this->id = 'LineItem.collection';
|
||||||
|
$this->template = '/{context_id}/lineitems';
|
||||||
|
$this->variables[] = 'LineItems.url';
|
||||||
|
$this->formats[] = 'application/vnd.ims.lis.v2.lineitemcontainer+json';
|
||||||
|
$this->formats[] = 'application/vnd.ims.lis.v2.lineitem+json';
|
||||||
|
$this->methods[] = self::HTTP_GET;
|
||||||
|
$this->methods[] = self::HTTP_POST;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the request for this resource.
|
||||||
|
*
|
||||||
|
* @param \mod_lti\local\ltiservice\response $response Response object for this request.
|
||||||
|
*/
|
||||||
|
public function execute($response) {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
$params = $this->parse_template();
|
||||||
|
$contextid = $params['context_id'];
|
||||||
|
$isget = $response->get_request_method() === self::HTTP_GET;
|
||||||
|
if ($isget) {
|
||||||
|
$contenttype = $response->get_accept();
|
||||||
|
} else {
|
||||||
|
$contenttype = $response->get_content_type();
|
||||||
|
}
|
||||||
|
$container = empty($contenttype) || ($contenttype === $this->formats[0]);
|
||||||
|
// We will receive typeid when working with LTI 1.x, if not the we are in LTI 2.
|
||||||
|
$typeid = optional_param('type_id', null, PARAM_ALPHANUM);
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
if (!$this->check_tool_proxy(null, $response->get_request_data())) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("Invalid tool proxy specified.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch ($response->get_request_method()) {
|
||||||
|
case self::HTTP_GET:
|
||||||
|
if (!$this->check_type($typeid, $contextid, 'LineItem.collection:get', $response->get_request_data())) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("This resource does not support GET requests.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case self::HTTP_POST:
|
||||||
|
if (!$this->check_type($typeid, $contextid, 'LineItem.collection:post', $response->get_request_data())) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("This resource does not support POST requests.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: // Should not be possible.
|
||||||
|
$response->set_code(405);
|
||||||
|
$response->set_reason("Invalid request method specified.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($contextid) || !($container ^ ($response->get_request_method() === self::HTTP_POST)) ||
|
||||||
|
(!empty($contenttype) && !in_array($contenttype, $this->formats))) {
|
||||||
|
$response->set_code(400);
|
||||||
|
$response->set_reason("Invalid request made.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$DB->record_exists('course', array('id' => $contextid))) {
|
||||||
|
$response->set_code(404);
|
||||||
|
$response->set_reason("Not Found: Course $contextid doesn't exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch ($response->get_request_method()) {
|
||||||
|
case self::HTTP_GET:
|
||||||
|
$resourceid = optional_param('resource_id', null, PARAM_TEXT);
|
||||||
|
$ltilinkid = optional_param('lti_link_id', null, PARAM_TEXT);
|
||||||
|
$tag = optional_param('tag', null, PARAM_TEXT);
|
||||||
|
$limitnum = optional_param('limit', 0, PARAM_INT);
|
||||||
|
$limitfrom = optional_param('from', 0, PARAM_INT);
|
||||||
|
$itemsandcount = $this->get_service()->get_lineitems($contextid, $resourceid, $ltilinkid, $tag, $limitfrom,
|
||||||
|
$limitnum, $typeid);
|
||||||
|
$items = $itemsandcount[1];
|
||||||
|
$totalcount = $itemsandcount[0];
|
||||||
|
$json = $this->get_json_for_get_request($items, $resourceid, $ltilinkid, $tag, $limitfrom,
|
||||||
|
$limitnum, $totalcount, $typeid, $response);
|
||||||
|
$response->set_content_type($this->formats[0]);
|
||||||
|
break;
|
||||||
|
case self::HTTP_POST:
|
||||||
|
try {
|
||||||
|
$json = $this->get_json_for_post_request($response->get_request_data(), $contextid, $typeid);
|
||||||
|
$response->set_code(201);
|
||||||
|
$response->set_content_type($this->formats[1]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$response->set_code($e->getCode());
|
||||||
|
$response->set_reason($e->getMessage());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: // Should not be possible.
|
||||||
|
$response->set_code(405);
|
||||||
|
$response->set_reason("Invalid request method specified.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$response->set_body($json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the JSON for a GET request.
|
||||||
|
*
|
||||||
|
* @param array $items Array of lineitems
|
||||||
|
* @param string $resourceid Resource identifier used for filtering, may be null
|
||||||
|
* @param string $ltilinkid Resource Link identifier used for filtering, may be null
|
||||||
|
* @param string $tag Tag identifier used for filtering, may be null
|
||||||
|
* @param int $limitfrom Offset of the first line item to return
|
||||||
|
* @param int $limitnum Maximum number of line items to return, ignored if zero or less
|
||||||
|
* @param int $totalcount Number of total lineitems before filtering for paging
|
||||||
|
* @param int $typeid Maximum number of line items to return, ignored if zero or less
|
||||||
|
* @param \mod_lti\local\ltiservice\response $response
|
||||||
|
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function get_json_for_get_request($items, $resourceid, $ltilinkid,
|
||||||
|
$tag, $limitfrom, $limitnum, $totalcount, $typeid, $response) {
|
||||||
|
|
||||||
|
$firstpage = null;
|
||||||
|
$nextpage = null;
|
||||||
|
$prevpage = null;
|
||||||
|
$lastpage = null;
|
||||||
|
if (isset($limitnum) && $limitnum > 0) {
|
||||||
|
if ($limitfrom >= $totalcount || $limitfrom < 0) {
|
||||||
|
$outofrange = true;
|
||||||
|
} else {
|
||||||
|
$outofrange = false;
|
||||||
|
}
|
||||||
|
$limitprev = $limitfrom - $limitnum >= 0 ? $limitfrom - $limitnum : 0;
|
||||||
|
$limitcurrent = $limitfrom;
|
||||||
|
$limitlast = $totalcount - $limitnum + 1 >= 0 ? $totalcount - $limitnum + 1 : 0;
|
||||||
|
$limitfrom += $limitnum;
|
||||||
|
|
||||||
|
$baseurl = new \moodle_url($this->get_endpoint());
|
||||||
|
if (isset($resourceid)) {
|
||||||
|
$baseurl->param('resource_id', $resourceid);
|
||||||
|
}
|
||||||
|
if (isset($ltilinkid)) {
|
||||||
|
$baseurl->param('lti_link_id', $ltilinkid);
|
||||||
|
}
|
||||||
|
if (isset($tag)) {
|
||||||
|
$baseurl->param('tag', $tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
$baseurl->param('limit', $limitnum);
|
||||||
|
if (($limitfrom <= $totalcount - 1) && (!$outofrange)) {
|
||||||
|
$nextpage = new \moodle_url($baseurl, ['from' => $limitfrom]);
|
||||||
|
}
|
||||||
|
$firstpage = new \moodle_url($baseurl, ['from' => 0]);
|
||||||
|
$canonicalpage = new \moodle_url($baseurl, ['from' => $limitcurrent]);
|
||||||
|
$lastpage = new \moodle_url($baseurl, ['from' > $limitlast]);
|
||||||
|
if (($limitcurrent > 0) && (!$outofrange)) {
|
||||||
|
$prevpage = new \moodle_url($baseurl, ['from' => $limitprev]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$baseurl->params(['type_id' => $typeid, 'limit' => $limitnum]);
|
||||||
|
if (($limitfrom <= $totalcount - 1) && (!$outofrange)) {
|
||||||
|
$nextpage = new \moodle_url($baseurl, ['from' => $limitfrom]);
|
||||||
|
}
|
||||||
|
$firstpage = new \moodle_url($baseurl, ['from' => 0]);
|
||||||
|
$canonicalpage = new \moodle_url($baseurl, ['from' => $limitcurrent]);
|
||||||
|
$lastpage = new \moodle_url($baseurl, ['from' => $limitlast]);
|
||||||
|
if (($limitcurrent > 0) && (!$outofrange)) {
|
||||||
|
$prevpage = new \moodle_url($baseurl, ['from' => $limitprev]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$jsonitems=[];
|
||||||
|
$endpoint = parent::get_endpoint();
|
||||||
|
foreach ($items as $item) {
|
||||||
|
array_push($jsonitems, gradebookservices::item_for_json($item, $endpoint, $typeid));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($canonicalpage) && ($canonicalpage)) {
|
||||||
|
$links = 'Link: <' . $firstpage->out() . '>; rel=“first”';
|
||||||
|
if (!is_null($prevpage)) {
|
||||||
|
$links .= ', <' . $prevpage->out() . '>; rel=“prev”';
|
||||||
|
}
|
||||||
|
$links .= ', <' . $canonicalpage->out(). '>; rel=“canonical”';
|
||||||
|
if (!is_null($nextpage)) {
|
||||||
|
$links .= ', <' . $nextpage->out() . '>; rel=“next”';
|
||||||
|
}
|
||||||
|
$links .= ', <' . $lastpage->out() . '>; rel=“last”';
|
||||||
|
$response->add_additional_header($links);
|
||||||
|
}
|
||||||
|
return json_encode($jsonitems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the JSON for a POST request.
|
||||||
|
*
|
||||||
|
* @param string $body POST body
|
||||||
|
* @param string $contextid Course ID
|
||||||
|
* @param string $typeid
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
private function get_json_for_post_request($body, $contextid, $typeid) {
|
||||||
|
global $CFG, $DB;
|
||||||
|
|
||||||
|
$json = json_decode($body);
|
||||||
|
if (empty($json) ||
|
||||||
|
!isset($json->scoreMaximum) ||
|
||||||
|
!isset($json->label)) {
|
||||||
|
throw new \Exception(null, 400);
|
||||||
|
}
|
||||||
|
if (is_numeric($json->scoreMaximum)) {
|
||||||
|
$max = $json->scoreMaximum;
|
||||||
|
} else {
|
||||||
|
throw new \Exception(null, 400);
|
||||||
|
}
|
||||||
|
require_once($CFG->libdir.'/gradelib.php');
|
||||||
|
$resourceid = (isset($json->resourceId)) ? $json->resourceId : '';
|
||||||
|
$ltilinkid = (isset($json->ltiLinkId)) ? $json->ltiLinkId : null;
|
||||||
|
if ($ltilinkid != null) {
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
if (!gradebookservices::check_lti_id($ltilinkid, $contextid, $this->get_service()->get_tool_proxy()->id)) {
|
||||||
|
throw new \Exception(null, 403);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!gradebookservices::check_lti_1x_id($ltilinkid, $contextid, $typeid)) {
|
||||||
|
throw new \Exception(null, 403);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$tag = (isset($json->tag)) ? $json->tag : '';
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
$toolproxyid = $this->get_service()->get_tool_proxy()->id;
|
||||||
|
$baseurl = null;
|
||||||
|
} else {
|
||||||
|
$toolproxyid = null;
|
||||||
|
$baseurl = lti_get_type_type_config($typeid)->lti_toolurl;
|
||||||
|
}
|
||||||
|
$params = array();
|
||||||
|
$params['itemname'] = $json->label;
|
||||||
|
$params['gradetype'] = GRADE_TYPE_VALUE;
|
||||||
|
$params['grademax'] = $max;
|
||||||
|
$params['grademin'] = 0;
|
||||||
|
$item = new \grade_item(array('id' => 0, 'courseid' => $contextid));
|
||||||
|
\grade_item::set_properties($item, $params);
|
||||||
|
$item->itemtype = 'manual';
|
||||||
|
$item->idnumber = $resourceid;
|
||||||
|
$item->grademax = $max;
|
||||||
|
$id = $item->insert('mod/ltiservice_gradebookservices');
|
||||||
|
$DB->insert_record('ltiservice_gradebookservices', (object)array(
|
||||||
|
'gradeitemid' => $id,
|
||||||
|
'courseid' => $contextid,
|
||||||
|
'toolproxyid' => $toolproxyid,
|
||||||
|
'typeid' => $typeid,
|
||||||
|
'baseurl' => $baseurl,
|
||||||
|
'ltilinkid' => $ltilinkid,
|
||||||
|
'tag' => $tag
|
||||||
|
));
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
$json->id = parent::get_endpoint() . "/{$id}/lineitem";
|
||||||
|
} else {
|
||||||
|
$json->id = parent::get_endpoint() . "/{$id}/lineitem?type_id={$typeid}";
|
||||||
|
}
|
||||||
|
return json_encode($json, JSON_UNESCAPED_SLASHES);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get permissions from the config of the tool for that resource
|
||||||
|
*
|
||||||
|
* @param string $typeid
|
||||||
|
*
|
||||||
|
* @return array with the permissions related to this resource by the lti type or null if none.
|
||||||
|
*/
|
||||||
|
public function get_permissions($typeid) {
|
||||||
|
$tool = lti_get_type_type_config($typeid);
|
||||||
|
if ($tool->ltiservice_gradesynchronization == '1') {
|
||||||
|
return array('LineItem.collection:get');
|
||||||
|
} else if ($tool->ltiservice_gradesynchronization == '2') {
|
||||||
|
return array('LineItem.collection:get', 'LineItem.collection:post');
|
||||||
|
} else {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a value for custom parameter substitution variables.
|
||||||
|
*
|
||||||
|
* @param string $value String to be parsed
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function parse_value($value) {
|
||||||
|
global $COURSE;
|
||||||
|
|
||||||
|
if (strpos($value, '$LineItems.url') !== false) {
|
||||||
|
$this->params['context_id'] = $COURSE->id;
|
||||||
|
$value = str_replace('$LineItems.url', parent::get_endpoint(), $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,304 @@
|
||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file contains a class definition for the LISResults container resource
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @author Dirk Singels, Diego del Blanco, Claude Vervoort
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ltiservice_gradebookservices\local\resources;
|
||||||
|
|
||||||
|
use ltiservice_gradebookservices\local\service\gradebookservices;
|
||||||
|
use mod_lti\local\ltiservice\resource_base;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A resource implementing LISResults container.
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class results extends resource_base {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor.
|
||||||
|
*
|
||||||
|
* @param \ltiservice_gradebookservices\local\service\gradebookservices $service Service instance
|
||||||
|
*/
|
||||||
|
public function __construct($service) {
|
||||||
|
|
||||||
|
parent::__construct($service);
|
||||||
|
$this->id = 'Result.collection';
|
||||||
|
$this->template = '/{context_id}/lineitems/{item_id}/lineitem/results';
|
||||||
|
$this->variables[] = 'Results.url';
|
||||||
|
$this->formats[] = 'application/vnd.ims.lis.v2.resultcontainer+json';
|
||||||
|
$this->methods[] = 'GET';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the request for this resource.
|
||||||
|
*
|
||||||
|
* @param \mod_lti\local\ltiservice\response $response Response object for this request.
|
||||||
|
*/
|
||||||
|
public function execute($response) {
|
||||||
|
global $CFG, $DB;
|
||||||
|
|
||||||
|
$params = $this->parse_template();
|
||||||
|
$contextid = $params['context_id'];
|
||||||
|
$itemid = $params['item_id'];
|
||||||
|
|
||||||
|
$isget = $response->get_request_method() === 'GET';
|
||||||
|
if ($isget) {
|
||||||
|
$contenttype = $response->get_accept();
|
||||||
|
} else {
|
||||||
|
$contenttype = $response->get_content_type();
|
||||||
|
}
|
||||||
|
// We will receive typeid when working with LTI 1.x, if not the we are in LTI 2.
|
||||||
|
$typeid = optional_param('type_id', null, PARAM_ALPHANUM);
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
if (!$this->check_tool_proxy(null, $response->get_request_data())) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("Invalid tool proxy specified.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!$this->check_type($typeid, $contextid, 'Result.collection:get', $response->get_request_data())) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("This resource does not support GET requests.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($contextid) || (!empty($contenttype) && !in_array($contenttype, $this->formats))) {
|
||||||
|
$response->set_code(400);
|
||||||
|
$response->set_reason("Invalid request made.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$DB->record_exists('course', array('id' => $contextid))) {
|
||||||
|
$response->set_code(404);
|
||||||
|
$response->set_reason("Not Found: Course $contextid doesn't exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$DB->record_exists('grade_items', array('id' => $itemid))) {
|
||||||
|
$response->set_code(404);
|
||||||
|
$response->set_reason("Not Found: Grade item $itemid doesn't exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$item = $this->get_service()->get_lineitem($contextid, $itemid, $typeid);
|
||||||
|
if ($item === false) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("Line item does not exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$gbs = gradebookservices::find_ltiservice_gradebookservice_for_lineitem($itemid);
|
||||||
|
$ltilinkid = null;
|
||||||
|
if (isset($item->iteminstance)) {
|
||||||
|
$ltilinkid = $item->iteminstance;
|
||||||
|
} else if ($gbs && isset($gbs->ltilinkid)) {
|
||||||
|
$ltilinkid = $gbs->ltilinkid;
|
||||||
|
}
|
||||||
|
if ($ltilinkid != null) {
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
if (isset($item->iteminstance) && (!gradebookservices::check_lti_id($ltilinkid, $item->courseid,
|
||||||
|
$this->get_service()->get_tool_proxy()->id))) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("Invalid LTI id supplied.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isset($item->iteminstance) && (!gradebookservices::check_lti_1x_id($ltilinkid, $item->courseid,
|
||||||
|
$typeid))) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("Invalid LTI id supplied.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require_once($CFG->libdir.'/gradelib.php');
|
||||||
|
switch ($response->get_request_method()) {
|
||||||
|
case 'GET':
|
||||||
|
$useridfilter = optional_param('user_id', 0, PARAM_INT);
|
||||||
|
$limitnum = optional_param('limit', 0, PARAM_INT);
|
||||||
|
$limitfrom = optional_param('from', 0, PARAM_INT);
|
||||||
|
$typeid = optional_param('type_id', null, PARAM_TEXT);
|
||||||
|
$json = $this->get_json_for_get_request($item->id, $limitfrom, $limitnum,
|
||||||
|
$useridfilter, $typeid, $response);
|
||||||
|
$response->set_content_type($this->formats[0]);
|
||||||
|
$response->set_body($json);
|
||||||
|
break;
|
||||||
|
default: // Should not be possible.
|
||||||
|
$response->set_code(405);
|
||||||
|
$response->set_reason("Invalid request method specified.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$response->set_body($json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the JSON for a GET request.
|
||||||
|
*
|
||||||
|
* @param int $itemid Grade item instance ID
|
||||||
|
* @param int $limitfrom Offset for the first result to include in this paged set
|
||||||
|
* @param int $limitnum Maximum number of results to include in the response, ignored if zero
|
||||||
|
* @param int $useridfilter The user id to filter the results.
|
||||||
|
* @param int $typeid Lti tool typeid (or null)
|
||||||
|
* @param \mod_lti\local\ltiservice\response $response The response element needed to add a header.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function get_json_for_get_request($itemid, $limitfrom, $limitnum, $useridfilter, $typeid, $response) {
|
||||||
|
|
||||||
|
if ($useridfilter > 0) {
|
||||||
|
$grades = \grade_grade::fetch_all(array('itemid' => $itemid, 'userid' => $useridfilter));
|
||||||
|
} else {
|
||||||
|
$grades = \grade_grade::fetch_all(array('itemid' => $itemid));
|
||||||
|
}
|
||||||
|
|
||||||
|
$firstpage = null;
|
||||||
|
$nextpage = null;
|
||||||
|
$prevpage = null;
|
||||||
|
$lastpage = null;
|
||||||
|
if ($grades && isset($limitnum) && $limitnum > 0) {
|
||||||
|
// Since we only display grades that have been modified, we need to filter first in order to support
|
||||||
|
// paging.
|
||||||
|
$resultgrades = array_filter($grades, function ($grade) {
|
||||||
|
return !empty($grade->timemodified);
|
||||||
|
});
|
||||||
|
// We save the total count to calculate the last page.
|
||||||
|
$totalcount = count($resultgrades);
|
||||||
|
// We slice to the requested item offset to insure proper item is always first, and we always return
|
||||||
|
// first pageset of any remaining items.
|
||||||
|
$grades = array_slice($resultgrades, $limitfrom);
|
||||||
|
if (count($grades) > 0) {
|
||||||
|
$pagedgrades = array_chunk($grades, $limitnum);
|
||||||
|
$pageset = 0;
|
||||||
|
$grades = $pagedgrades[$pageset];
|
||||||
|
}
|
||||||
|
if ($limitfrom >= $totalcount || $limitfrom < 0) {
|
||||||
|
$outofrange = true;
|
||||||
|
} else {
|
||||||
|
$outofrange = false;
|
||||||
|
}
|
||||||
|
$limitprev = $limitfrom - $limitnum >= 0 ? $limitfrom - $limitnum : 0;
|
||||||
|
$limitcurrent = $limitfrom;
|
||||||
|
$limitlast = $totalcount - $limitnum + 1 >= 0 ? $totalcount - $limitnum + 1 : 0;
|
||||||
|
$limitfrom += $limitnum;
|
||||||
|
|
||||||
|
$baseurl = new \moodle_url($this->get_endpoint());
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
$baseurl->param('limit', $limitnum);
|
||||||
|
|
||||||
|
if (($limitfrom <= $totalcount - 1) && (!$outofrange)) {
|
||||||
|
$nextpage = new \moodle_url($baseurl, ['from' => $limitfrom]);
|
||||||
|
}
|
||||||
|
$firstpage = new \moodle_url($baseurl, ['from' => 0]);
|
||||||
|
$canonicalpage = new \moodle_url($baseurl, ['from' => $limitcurrent]);
|
||||||
|
$lastpage = new \moodle_url($baseurl, ['from' => $limitlast]);
|
||||||
|
if (($limitcurrent > 0) && (!$outofrange)) {
|
||||||
|
$prevpage = new \moodle_url($baseurl, ['from' => $limitprev]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$baseurl->params(['type_id' => $typeid, 'limit' => $limitnum]);
|
||||||
|
|
||||||
|
if (($limitfrom <= $totalcount - 1) && (!$outofrange)) {
|
||||||
|
$nextpage = new \moodle_url($baseurl, ['from' => $limitfrom]);
|
||||||
|
}
|
||||||
|
$firstpage = new \moodle_url($baseurl, ['from' => 0]);
|
||||||
|
$canonicalpage = new \moodle_url($baseurl, ['from' => $limitcurrent]);
|
||||||
|
if (($limitcurrent > 0) && (!$outofrange)) {
|
||||||
|
$prevpage = new \moodle_url($baseurl, ['from' => $limitprev]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$jsonresults = [];
|
||||||
|
$lineitem = new lineitem($this->get_service());
|
||||||
|
$endpoint = $lineitem->get_endpoint();
|
||||||
|
if ($grades) {
|
||||||
|
foreach ($grades as $grade) {
|
||||||
|
if (!empty($grade->timemodified)) {
|
||||||
|
array_push($jsonresults, gradebookservices::result_for_json($grade, $endpoint, $typeid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($canonicalpage) && ($canonicalpage)) {
|
||||||
|
$links = 'Link: <' . $firstpage->out() . '>; rel=“first”';
|
||||||
|
if (!is_null($prevpage)) {
|
||||||
|
$links .= ', <' . $prevpage->out() . '>; rel=“prev”';
|
||||||
|
}
|
||||||
|
$links .= ', <' . $canonicalpage->out() . '>; rel=“canonical”';
|
||||||
|
if (!is_null($nextpage)) {
|
||||||
|
$links .= ', <' . $nextpage->out() . '>; rel=“next”';
|
||||||
|
}
|
||||||
|
$links .= ', <' . $lastpage->out() . '>; rel=“last”';
|
||||||
|
$response->add_additional_header($links);
|
||||||
|
}
|
||||||
|
return json_encode($jsonresults);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get permissions from the config of the tool for that resource
|
||||||
|
*
|
||||||
|
* @param int $typeid
|
||||||
|
*
|
||||||
|
* @return array with the permissions related to this resource by the $lti_type or null if none.
|
||||||
|
*/
|
||||||
|
public function get_permissions($typeid) {
|
||||||
|
$tool = lti_get_type_type_config($typeid);
|
||||||
|
if ($tool->ltiservice_gradesynchronization == '1') {
|
||||||
|
return array('Result.collection:get');
|
||||||
|
} else if ($tool->ltiservice_gradesynchronization == '2') {
|
||||||
|
return array('Result.collection:get');
|
||||||
|
} else {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a value for custom parameter substitution variables.
|
||||||
|
*
|
||||||
|
* @param string $value String to be parsed
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function parse_value($value) {
|
||||||
|
global $COURSE, $CFG;
|
||||||
|
if (strpos($value, '$Results.url') !== false) {
|
||||||
|
require_once($CFG->libdir . '/gradelib.php');
|
||||||
|
|
||||||
|
$resolved = '';
|
||||||
|
$this->params['context_id'] = $COURSE->id;
|
||||||
|
$id = optional_param('id', 0, PARAM_INT); // Course Module ID.
|
||||||
|
if (!empty($id)) {
|
||||||
|
$cm = get_coursemodule_from_id('lti', $id, 0, false, MUST_EXIST);
|
||||||
|
$id = $cm->instance;
|
||||||
|
$item = grade_get_grades($COURSE->id, 'mod', 'lti', $id);
|
||||||
|
if ($item && $item->items) {
|
||||||
|
$this->params['item_id'] = $item->items[0]->id;
|
||||||
|
$resolved = parent::get_endpoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$value = str_replace('$Results.url', $resolved, $value);
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,270 @@
|
||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file contains a class definition for the LISResult container resource
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @author Dirk Singels, Diego del Blanco, Claude Vervoort
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ltiservice_gradebookservices\local\resources;
|
||||||
|
|
||||||
|
use ltiservice_gradebookservices\local\service\gradebookservices;
|
||||||
|
use mod_lti\local\ltiservice\resource_base;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A resource implementing LISResult container.
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class scores extends resource_base {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor.
|
||||||
|
*
|
||||||
|
* @param \ltiservice_gradebookservices\local\service\gradebookservices $service Service instance
|
||||||
|
*/
|
||||||
|
public function __construct($service) {
|
||||||
|
|
||||||
|
parent::__construct($service);
|
||||||
|
$this->id = 'Score.collection';
|
||||||
|
$this->template = '/{context_id}/lineitems/{item_id}/lineitem/scores';
|
||||||
|
$this->variables[] = 'Scores.url';
|
||||||
|
$this->formats[] = 'application/vnd.ims.lis.v1.scorecontainer+json';
|
||||||
|
$this->formats[] = 'application/vnd.ims.lis.v1.score+json';
|
||||||
|
$this->methods[] = 'POST';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the request for this resource.
|
||||||
|
*
|
||||||
|
* @param \mod_lti\local\ltiservice\response $response Response object for this request.
|
||||||
|
*/
|
||||||
|
public function execute($response) {
|
||||||
|
global $CFG, $DB;
|
||||||
|
|
||||||
|
$params = $this->parse_template();
|
||||||
|
$contextid = $params['context_id'];
|
||||||
|
$itemid = $params['item_id'];
|
||||||
|
|
||||||
|
// GET is disabled by the moment, but we have the code ready
|
||||||
|
// for a future implementation.
|
||||||
|
|
||||||
|
$isget = $response->get_request_method() === 'GET';
|
||||||
|
if ($isget) {
|
||||||
|
$contenttype = $response->get_accept();
|
||||||
|
} else {
|
||||||
|
$contenttype = $response->get_content_type();
|
||||||
|
}
|
||||||
|
$container = empty($contenttype) || ($contenttype === $this->formats[0]);
|
||||||
|
// We will receive typeid when working with LTI 1.x, if not the we are in LTI 2.
|
||||||
|
$typeid = optional_param('type_id', null, PARAM_ALPHANUM);
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
if (!$this->check_tool_proxy(null, $response->get_request_data())) {
|
||||||
|
$response->set_code(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch ($response->get_request_method()) {
|
||||||
|
case 'GET':
|
||||||
|
$response->set_code(405);
|
||||||
|
$response->set_reason("GET requests are not allowed.");
|
||||||
|
return;
|
||||||
|
case 'POST':
|
||||||
|
if (!$this->check_type($typeid, $contextid, 'Score.collection:post', $response->get_request_data())) {
|
||||||
|
$response->set_code(401);
|
||||||
|
$response->set_reason("This resource does not support POST requests.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: // Should not be possible.
|
||||||
|
$response->set_code(405);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($contextid) || !($container ^ ($response->get_request_method() === 'POST')) ||
|
||||||
|
(!empty($contenttype) && !in_array($contenttype, $this->formats))) {
|
||||||
|
$response->set_code(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$DB->record_exists('course', array('id' => $contextid))) {
|
||||||
|
$response->set_code(404);
|
||||||
|
$response->set_reason("Not Found: Course $contextid doesn't exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$DB->record_exists('grade_items', array('id' => $itemid))) {
|
||||||
|
$response->set_code(404);
|
||||||
|
$response->set_reason("Not Found: Grade item $itemid doesn't exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$item = $this->get_service()->get_lineitem($contextid, $itemid, $typeid);
|
||||||
|
if ($item === false) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("Line item does not exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$gbs = gradebookservices::find_ltiservice_gradebookservice_for_lineitem($itemid);
|
||||||
|
$ltilinkid = null;
|
||||||
|
if (isset($item->iteminstance)) {
|
||||||
|
$ltilinkid = $item->iteminstance;
|
||||||
|
} else if ($gbs && isset($gbs->ltilinkid)) {
|
||||||
|
$ltilinkid = $gbs->ltilinkid;
|
||||||
|
}
|
||||||
|
if ($ltilinkid != null) {
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
if (isset($item->iteminstance) && (!gradebookservices::check_lti_id($ltilinkid, $item->courseid,
|
||||||
|
$this->get_service()->get_tool_proxy()->id))) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("Invalid LTI id supplied.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isset($item->iteminstance) && (!gradebookservices::check_lti_1x_id($ltilinkid, $item->courseid,
|
||||||
|
$typeid))) {
|
||||||
|
$response->set_code(403);
|
||||||
|
$response->set_reason("Invalid LTI id supplied.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$json = '[]';
|
||||||
|
require_once($CFG->libdir.'/gradelib.php');
|
||||||
|
switch ($response->get_request_method()) {
|
||||||
|
case 'GET':
|
||||||
|
$response->set_code(405);
|
||||||
|
$response->set_reason("GET requests are not allowed.");
|
||||||
|
break;
|
||||||
|
case 'POST':
|
||||||
|
try {
|
||||||
|
$json = $this->get_json_for_post_request($response, $response->get_request_data(), $item, $contextid, $typeid);
|
||||||
|
$response->set_content_type($this->formats[1]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$response->set_code($e->getCode());
|
||||||
|
$response->set_reason($e->getMessage());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: // Should not be possible.
|
||||||
|
$response->set_code(405);
|
||||||
|
$response->set_reason("Invalid request method specified.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$response->set_body($json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the JSON for a POST request.
|
||||||
|
*
|
||||||
|
* @param \mod_lti\local\ltiservice\response $response Response object for this request.
|
||||||
|
* @param string $body POST body
|
||||||
|
* @param object $item Grade item instance
|
||||||
|
* @param string $contextid
|
||||||
|
* @param string $typeid
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
private function get_json_for_post_request($response, $body, $item, $contextid, $typeid) {
|
||||||
|
$score = json_decode($body);
|
||||||
|
if (empty($score) ||
|
||||||
|
!isset($score->userId) ||
|
||||||
|
!isset($score->timestamp) ||
|
||||||
|
!isset($score->gradingProgress) ||
|
||||||
|
!isset($score->activityProgress) ||
|
||||||
|
!isset($score->timestamp) ||
|
||||||
|
isset($score->timestamp) && !gradebookservices::validate_iso8601_date($score->timestamp) ||
|
||||||
|
(isset($score->scoreGiven) && !is_numeric($score->scoreGiven)) ||
|
||||||
|
(isset($score->scoreMaximum) && !is_numeric($score->scoreMaximum)) ||
|
||||||
|
(!gradebookservices::is_user_gradable_in_course($contextid, $score->userId))
|
||||||
|
) {
|
||||||
|
throw new \Exception('Incorrect score received' . $score, 400);
|
||||||
|
}
|
||||||
|
$score->timemodified = intval($score->timestamp);
|
||||||
|
|
||||||
|
if (!isset($score->scoreMaximum)) {
|
||||||
|
$score->scoreMaximum = 1;
|
||||||
|
}
|
||||||
|
$response->set_code(200);
|
||||||
|
$grade = \grade_grade::fetch(array('itemid' => $item->id, 'userid' => $score->userId));
|
||||||
|
if ($grade && !empty($grade->timemodified)) {
|
||||||
|
if ($grade->timemodified >= strtotime($score->timestamp)) {
|
||||||
|
$exmsg = "Refusing score with an earlier timestamp for item " . $item->id . " and user " . $score->userId;
|
||||||
|
throw new \Exception($exmsg, 409);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isset($score->scoreGiven)) {
|
||||||
|
if ($score->gradingProgress != 'FullyGraded') {
|
||||||
|
$score->scoreGiven = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gradebookservices::save_score($item, $score, $score->userId, $typeid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get permissions from the config of the tool for that resource
|
||||||
|
*
|
||||||
|
* @param int $typeid
|
||||||
|
*
|
||||||
|
* @return array with the permissions related to this resource by the $lti_type or null if none.
|
||||||
|
*/
|
||||||
|
public function get_permissions($typeid) {
|
||||||
|
$tool = lti_get_type_type_config($typeid);
|
||||||
|
if ($tool->ltiservice_gradesynchronization == '1') {
|
||||||
|
return array('Score.collection:post');
|
||||||
|
} else if ($tool->ltiservice_gradesynchronization == '2') {
|
||||||
|
return array('Score.collection:post');
|
||||||
|
} else {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a value for custom parameter substitution variables.
|
||||||
|
*
|
||||||
|
* @param string $value String to be parsed
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function parse_value($value) {
|
||||||
|
global $COURSE, $CFG;
|
||||||
|
|
||||||
|
if (strpos($value, '$Scores.url') !== false) {
|
||||||
|
require_once($CFG->libdir . '/gradelib.php');
|
||||||
|
|
||||||
|
$resolved = '';
|
||||||
|
$this->params['context_id'] = $COURSE->id;
|
||||||
|
$id = optional_param('id', 0, PARAM_INT); // Course Module ID.
|
||||||
|
if (!empty($id)) {
|
||||||
|
$cm = get_coursemodule_from_id('lti', $id, 0, false, MUST_EXIST);
|
||||||
|
$id = $cm->instance;
|
||||||
|
$item = grade_get_grades($COURSE->id, 'mod', 'lti', $id);
|
||||||
|
if ($item && $item->items) {
|
||||||
|
$this->params['item_id'] = $item->items[0]->id;
|
||||||
|
$resolved = parent::get_endpoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$value = str_replace('$Scores.url', $resolved, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,625 @@
|
||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file contains a class definition for the LTI Gradebook Services
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @author Dirk Singels, Diego del Blanco, Claude Vervoort
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ltiservice_gradebookservices\local\service;
|
||||||
|
|
||||||
|
use ltiservice_gradebookservices\local\resources\lineitem;
|
||||||
|
use ltiservice_gradebookservices\local\resources\lineitems;
|
||||||
|
use ltiservice_gradebookservices\local\resources\results;
|
||||||
|
use ltiservice_gradebookservices\local\resources\scores;
|
||||||
|
use mod_lti\local\ltiservice\resource_base;
|
||||||
|
use mod_lti\local\ltiservice\service_base;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service implementing LTI Gradebook Services.
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class gradebookservices extends service_base {
|
||||||
|
|
||||||
|
/** Internal service name */
|
||||||
|
const SERVICE_NAME = 'ltiservice_gradebookservices';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
$this->id = 'gradebookservices';
|
||||||
|
$this->name = $this->get_string('servicename');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the resources for this service.
|
||||||
|
*
|
||||||
|
* @return resource_base[]
|
||||||
|
*/
|
||||||
|
public function get_resources() {
|
||||||
|
|
||||||
|
// The containers should be ordered in the array after their elements.
|
||||||
|
// Lineitems should be after lineitem.
|
||||||
|
if (empty($this->resources)) {
|
||||||
|
$this->resources = array();
|
||||||
|
$this->resources[] = new lineitem($this);
|
||||||
|
$this->resources[] = new lineitems($this);
|
||||||
|
$this->resources[] = new results($this);
|
||||||
|
$this->resources[] = new scores($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds form elements for gradebook sync add/edit page.
|
||||||
|
*
|
||||||
|
* @param \MoodleQuickForm $mform Moodle quickform object definition
|
||||||
|
*/
|
||||||
|
public function get_configuration_options(&$mform) {
|
||||||
|
|
||||||
|
$selectelementname = 'ltiservice_gradesynchronization';
|
||||||
|
$identifier = 'grade_synchronization';
|
||||||
|
$options = [
|
||||||
|
$this->get_string('nevergs'),
|
||||||
|
$this->get_string('partialgs'),
|
||||||
|
$this->get_string('alwaysgs')
|
||||||
|
];
|
||||||
|
|
||||||
|
$mform->addElement('select', $selectelementname, $this->get_string($identifier), $options);
|
||||||
|
$mform->setType($selectelementname, 'int');
|
||||||
|
$mform->setDefault($selectelementname, 0);
|
||||||
|
$mform->addHelpButton($selectelementname, $identifier, self::SERVICE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves string from lang file
|
||||||
|
*
|
||||||
|
* @param string $identifier
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function get_string($identifier) {
|
||||||
|
return get_string($identifier, self::SERVICE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array with the names of the parameters that the service will be saving in the configuration
|
||||||
|
*
|
||||||
|
* @return array with the names of the parameters that the service will be saving in the configuration
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function get_configuration_parameter_names() {
|
||||||
|
return array('ltiservice_gradesynchronization');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of key/values to add to the launch parameters.
|
||||||
|
*
|
||||||
|
* @param string $messagetype 'basic-lti-launch-request' or 'ContentItemSelectionRequest'.
|
||||||
|
* @param string $courseid the course id.
|
||||||
|
* @param object $user The user id.
|
||||||
|
* @param string $typeid The tool lti type id.
|
||||||
|
* @param string $modlti The id of the lti activity.
|
||||||
|
*
|
||||||
|
* The type is passed to check the configuration
|
||||||
|
* and not return parameters for services not used.
|
||||||
|
*
|
||||||
|
* @return array of key/value pairs to add as launch parameters.
|
||||||
|
*/
|
||||||
|
public function get_launch_parameters($messagetype, $courseid, $user, $typeid, $modlti = null) {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
$launchparameters = array();
|
||||||
|
$tool = lti_get_type_type_config($typeid);
|
||||||
|
// Only inject parameters if the service is enabled for this tool.
|
||||||
|
if (isset($tool->ltiservice_gradesynchronization)) {
|
||||||
|
if ($tool->ltiservice_gradesynchronization == '1' || $tool->ltiservice_gradesynchronization == '2') {
|
||||||
|
// Check for used in context is only needed because there is no explicit site tool - course relation.
|
||||||
|
if ($this->is_allowed_in_context($typeid, $courseid)) {
|
||||||
|
$endpoint = $this->get_service_path() . "/{$courseid}/lineitems";
|
||||||
|
if (is_null($modlti)) {
|
||||||
|
$id = null;
|
||||||
|
} else {
|
||||||
|
$conditions = array('courseid' => $courseid, 'itemtype' => 'mod',
|
||||||
|
'itemmodule' => 'lti', 'iteminstance' => $modlti);
|
||||||
|
|
||||||
|
$lineitems = $DB->get_records('grade_items', $conditions);
|
||||||
|
$conditionsgbs = array('courseid' => $courseid, 'ltilinkid' => $modlti);
|
||||||
|
$lineitemsgbs = $DB->get_records('ltiservice_gradebookservices', $conditionsgbs);
|
||||||
|
if (count($lineitems) + count($lineitemsgbs) == 1) {
|
||||||
|
if ($lineitems) {
|
||||||
|
$lineitem = reset($lineitems);
|
||||||
|
$id = $lineitem->id;
|
||||||
|
} else {
|
||||||
|
$lineitemsgb = reset($lineitemsgbs);
|
||||||
|
$id = $lineitemsgb->gradeitemid;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$id = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$launchparameters['custom_lineitems_url'] = $endpoint . "?type_id={$typeid}";
|
||||||
|
if (!is_null($id)) {
|
||||||
|
$launchparameters['custom_lineitem_url'] = $endpoint . "/{$id}/lineitem?type_id={$typeid}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $launchparameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the lineitem instances.
|
||||||
|
*
|
||||||
|
* @param string $courseid ID of course
|
||||||
|
* @param string $resourceid Resource identifier used for filtering, may be null
|
||||||
|
* @param string $ltilinkid Resource Link identifier used for filtering, may be null
|
||||||
|
* @param string $tag
|
||||||
|
* @param int $limitfrom Offset for the first line item to include in a paged set
|
||||||
|
* @param int $limitnum Maximum number of line items to include in the paged set
|
||||||
|
* @param string $typeid
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function get_lineitems($courseid, $resourceid, $ltilinkid, $tag, $limitfrom, $limitnum, $typeid) {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
// Select all lti potential linetiems in site.
|
||||||
|
$params = array('courseid' => $courseid);
|
||||||
|
|
||||||
|
$optionalfilters = "";
|
||||||
|
if (isset($resourceid)) {
|
||||||
|
$optionalfilters .= " AND (i.idnumber = :resourceid)";
|
||||||
|
$params['resourceid'] = $resourceid;
|
||||||
|
}
|
||||||
|
$sql = "SELECT i.*
|
||||||
|
FROM {grade_items} i
|
||||||
|
WHERE (i.courseid = :courseid)
|
||||||
|
{$optionalfilters}
|
||||||
|
ORDER BY i.id";
|
||||||
|
$lineitems = $DB->get_records_sql($sql, $params);
|
||||||
|
|
||||||
|
// For each one, check the gbs id, and check that toolproxy matches. If so, add the
|
||||||
|
// tag to the result and add it to a final results array.
|
||||||
|
$lineitemstoreturn = array();
|
||||||
|
$lineitemsandtotalcount = array();
|
||||||
|
if ($lineitems) {
|
||||||
|
foreach ($lineitems as $lineitem) {
|
||||||
|
$gbs = $this->find_ltiservice_gradebookservice_for_lineitem($lineitem->id);
|
||||||
|
if ($gbs && (!isset($tag) || (isset($tag) && $gbs->tag == $tag))
|
||||||
|
&& (!isset($ltilinkid) || (isset($ltilinkid) && $gbs->ltilinkid == $ltilinkid))) {
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
if ($this->get_tool_proxy()->id == $gbs->toolproxyid) {
|
||||||
|
array_push($lineitemstoreturn, $lineitem);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($typeid == $gbs->typeid) {
|
||||||
|
array_push($lineitemstoreturn, $lineitem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (($lineitem->itemtype == 'mod') && ($lineitem->itemmodule == 'lti') && (!isset($tag) &&
|
||||||
|
(!isset($ltilinkid) || (isset($ltilinkid) && $lineitem->iteminstance == $ltilinkid)))) {
|
||||||
|
// We will need to check if the activity related belongs to our tool proxy.
|
||||||
|
$ltiactivity = $DB->get_record('lti', array('id' => $lineitem->iteminstance));
|
||||||
|
if (($ltiactivity) && (isset($ltiactivity->typeid))) {
|
||||||
|
if ($ltiactivity->typeid != 0) {
|
||||||
|
$tool = $DB->get_record('lti_types', array('id' => $ltiactivity->typeid));
|
||||||
|
} else {
|
||||||
|
$tool = lti_get_tool_by_url_match($ltiactivity->toolurl, $courseid);
|
||||||
|
if (!$tool) {
|
||||||
|
$tool = lti_get_tool_by_url_match($ltiactivity->securetoolurl, $courseid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
if (($tool) && ($this->get_tool_proxy()->id == $tool->toolproxyid)) {
|
||||||
|
array_push($lineitemstoreturn, $lineitem);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (($tool) && ($tool->id == $typeid)) {
|
||||||
|
array_push($lineitemstoreturn, $lineitem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$lineitemsandtotalcount = array();
|
||||||
|
array_push($lineitemsandtotalcount, count($lineitemstoreturn));
|
||||||
|
// Return the right array based in the paging parameters limit and from.
|
||||||
|
if (($limitnum) && ($limitnum > 0)) {
|
||||||
|
$lineitemstoreturn = array_slice($lineitemstoreturn, $limitfrom, $limitnum);
|
||||||
|
}
|
||||||
|
array_push($lineitemsandtotalcount, $lineitemstoreturn);
|
||||||
|
}
|
||||||
|
return $lineitemsandtotalcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a lineitem instance.
|
||||||
|
*
|
||||||
|
* Returns the lineitem instance if found, otherwise false.
|
||||||
|
*
|
||||||
|
* @param string $courseid ID of course
|
||||||
|
* @param string $itemid ID of lineitem
|
||||||
|
* @param string $typeid
|
||||||
|
*
|
||||||
|
* @return \ltiservice_gradebookservices\local\resources\lineitem|bool
|
||||||
|
*/
|
||||||
|
public function get_lineitem($courseid, $itemid, $typeid) {
|
||||||
|
global $DB, $CFG;
|
||||||
|
|
||||||
|
require_once($CFG->libdir . '/gradelib.php');
|
||||||
|
$lineitem = \grade_item::fetch(array('id' => $itemid));
|
||||||
|
if ($lineitem) {
|
||||||
|
$gbs = $this->find_ltiservice_gradebookservice_for_lineitem($itemid);
|
||||||
|
if (!$gbs) {
|
||||||
|
// We will need to check if the activity related belongs to our tool proxy.
|
||||||
|
$ltiactivity = $DB->get_record('lti', array('id' => $lineitem->iteminstance));
|
||||||
|
if (($ltiactivity) && (isset($ltiactivity->typeid))) {
|
||||||
|
if ($ltiactivity->typeid != 0) {
|
||||||
|
$tool = $DB->get_record('lti_types', array('id' => $ltiactivity->typeid));
|
||||||
|
} else {
|
||||||
|
$tool = lti_get_tool_by_url_match($ltiactivity->toolurl, $courseid);
|
||||||
|
if (!$tool) {
|
||||||
|
$tool = lti_get_tool_by_url_match($ltiactivity->securetoolurl, $courseid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
if (!(($tool) && ($this->get_tool_proxy()->id == $tool->toolproxyid))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!(($tool) && ($tool->id == $typeid))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $lineitem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a grade item.
|
||||||
|
*
|
||||||
|
* @param object $gradeitem Grade Item record
|
||||||
|
* @param object $score Result object
|
||||||
|
* @param int $userid User ID
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public static function save_score($gradeitem, $score, $userid) {
|
||||||
|
global $DB, $CFG;
|
||||||
|
$source = 'mod' . self::SERVICE_NAME;
|
||||||
|
if ($DB->get_record('user', array('id' => $userid)) === false) {
|
||||||
|
throw new \Exception(null, 400);
|
||||||
|
}
|
||||||
|
require_once($CFG->libdir . '/gradelib.php');
|
||||||
|
$finalgrade = null;
|
||||||
|
$timemodified = null;
|
||||||
|
if (isset($score->scoreGiven) && $score->scoreGiven) {
|
||||||
|
$finalgrade = grade_floatval($score->scoreGiven);
|
||||||
|
$max = 1;
|
||||||
|
if (isset($score->scoreMaximum)) {
|
||||||
|
$max = $score->scoreMaximum;
|
||||||
|
}
|
||||||
|
if (!is_null($max) && grade_floats_different($max, $gradeitem->grademax) && grade_floats_different($max, 0.0)) {
|
||||||
|
// Rescale to match the grade item maximum.
|
||||||
|
$finalgrade = grade_floatval($finalgrade * $gradeitem->grademax / $max);
|
||||||
|
}
|
||||||
|
if (isset($score->timestamp)) {
|
||||||
|
$timemodified = strtotime($score->timestamp);
|
||||||
|
} else {
|
||||||
|
$timemodified = time();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$feedbackformat = FORMAT_MOODLE;
|
||||||
|
$feedback = null;
|
||||||
|
if (isset($score->comment) && !empty($score->comment)) {
|
||||||
|
$feedback = $score->comment;
|
||||||
|
$feedbackformat = FORMAT_PLAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$grade = \grade_grade::fetch(array('itemid' => $gradeitem->id, 'userid' => $userid))) {
|
||||||
|
$grade = new \grade_grade();
|
||||||
|
$grade->userid = $userid;
|
||||||
|
$grade->itemid = $gradeitem->id;
|
||||||
|
}
|
||||||
|
$grade->rawgrademax = $score->scoreMaximum;
|
||||||
|
$grade->timemodified = $timemodified;
|
||||||
|
$grade->feedbackformat = $feedbackformat;
|
||||||
|
$grade->feedback = $feedback;
|
||||||
|
if ($gradeitem->is_manual_item()) {
|
||||||
|
$grade->finalgrade = $finalgrade;
|
||||||
|
if (empty($grade->id)) {
|
||||||
|
$result = (bool)$grade->insert($source);
|
||||||
|
} else {
|
||||||
|
$result = $grade->update($source);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$grade->rawgrade = $finalgrade;
|
||||||
|
$status = \grade_update($source, $gradeitem->courseid,
|
||||||
|
$gradeitem->itemtype, $gradeitem->itemmodule,
|
||||||
|
$gradeitem->iteminstance, $gradeitem->itemnumber,
|
||||||
|
$grade);
|
||||||
|
|
||||||
|
$result = ($status == GRADE_UPDATE_OK);
|
||||||
|
}
|
||||||
|
if (!$result) {
|
||||||
|
debugging("failed to save score for item ".$gradeitem->id." and user ".$grade->userid);
|
||||||
|
throw new \Exception(null, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the json object representation of the grade item
|
||||||
|
*
|
||||||
|
* @param object $item Grade Item record
|
||||||
|
* @param string $endpoint Endpoint for lineitems container request
|
||||||
|
* @param string $typeid
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public static function item_for_json($item, $endpoint, $typeid) {
|
||||||
|
|
||||||
|
$lineitem = new \stdClass();
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
$typeidstring = "";
|
||||||
|
} else {
|
||||||
|
$typeidstring = "?type_id={$typeid}";
|
||||||
|
}
|
||||||
|
$lineitem->id = "{$endpoint}/{$item->id}/lineitem" . $typeidstring;
|
||||||
|
$lineitem->label = $item->itemname;
|
||||||
|
$lineitem->scoreMaximum = floatval($item->grademax);
|
||||||
|
$lineitem->resourceId = (!empty($item->idnumber)) ? $item->idnumber : '';
|
||||||
|
$gbs = self::find_ltiservice_gradebookservice_for_lineitem($item->id);
|
||||||
|
if ($gbs) {
|
||||||
|
$lineitem->tag = (!empty($gbs->tag)) ? $gbs->tag : '';
|
||||||
|
if (isset($gbs->ltilinkid)) {
|
||||||
|
$lineitem->ltiLinkId = strval($gbs->ltilinkid);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$lineitem->tag = '';
|
||||||
|
if (isset($item->iteminstance)) {
|
||||||
|
$lineitem->ltiLinkId = strval($item->iteminstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $lineitem;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the object matching the JSON representation of the result.
|
||||||
|
*
|
||||||
|
* @param object $grade Grade record
|
||||||
|
* @param string $endpoint Endpoint for lineitem
|
||||||
|
* @param int $typeid The id of the type to include in the result url.
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public static function result_for_json($grade, $endpoint, $typeid) {
|
||||||
|
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
$id = "{$endpoint}/results?user_id={$grade->userid}";
|
||||||
|
} else {
|
||||||
|
$id = "{$endpoint}/results?type_id={$typeid}&user_id={$grade->userid}";
|
||||||
|
}
|
||||||
|
$result = new \stdClass();
|
||||||
|
$result->id = $id;
|
||||||
|
$result->userId = $grade->userid;
|
||||||
|
if (!empty($grade->finalgrade)) {
|
||||||
|
$result->resultScore = floatval($grade->finalgrade);
|
||||||
|
$result->resultMaximum = floatval($grade->rawgrademax);
|
||||||
|
if (!empty($grade->feedback)) {
|
||||||
|
$result->comment = $grade->feedback;
|
||||||
|
}
|
||||||
|
if (is_null($typeid)) {
|
||||||
|
$result->scoreOf = $endpoint;
|
||||||
|
} else {
|
||||||
|
$result->scoreOf = "{$endpoint}?type_id={$typeid}";
|
||||||
|
}
|
||||||
|
$result->timestamp = date('c', $grade->timemodified);
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an LTI id is valid.
|
||||||
|
*
|
||||||
|
* @param string $linkid The lti id
|
||||||
|
* @param string $course The course
|
||||||
|
* @param string $toolproxy The tool proxy id
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function check_lti_id($linkid, $course, $toolproxy) {
|
||||||
|
global $DB;
|
||||||
|
// Check if lti type is zero or not (comes from a backup).
|
||||||
|
$sqlparams1 = array();
|
||||||
|
$sqlparams1['linkid'] = $linkid;
|
||||||
|
$sqlparams1['course'] = $course;
|
||||||
|
$ltiactivity = $DB->get_record('lti', array('id' => $linkid, 'course' => $course));
|
||||||
|
if ($ltiactivity->typeid == 0) {
|
||||||
|
$tool = lti_get_tool_by_url_match($ltiactivity->toolurl, $course);
|
||||||
|
if (!$tool) {
|
||||||
|
$tool = lti_get_tool_by_url_match($ltiactivity->securetoolurl, $course);
|
||||||
|
}
|
||||||
|
return (($tool) && ($toolproxy == $tool->toolproxyid));
|
||||||
|
} else {
|
||||||
|
$sqlparams2 = array();
|
||||||
|
$sqlparams2['linkid'] = $linkid;
|
||||||
|
$sqlparams2['course'] = $course;
|
||||||
|
$sqlparams2['toolproxy'] = $toolproxy;
|
||||||
|
$sql = 'SELECT lti.*
|
||||||
|
FROM {lti} lti
|
||||||
|
INNER JOIN {lti_types} typ ON lti.typeid = typ.id
|
||||||
|
WHERE lti.id = ?
|
||||||
|
AND lti.course = ?
|
||||||
|
AND typ.toolproxyid = ?';
|
||||||
|
return $DB->record_exists_sql($sql, $sqlparams2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an LTI id is valid when we are in a LTI 1.x case
|
||||||
|
*
|
||||||
|
* @param string $linkid The lti id
|
||||||
|
* @param string $course The course
|
||||||
|
* @param string $typeid The lti type id
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function check_lti_1x_id($linkid, $course, $typeid) {
|
||||||
|
global $DB;
|
||||||
|
// Check if lti type is zero or not (comes from a backup).
|
||||||
|
$sqlparams1 = array();
|
||||||
|
$sqlparams1['linkid'] = $linkid;
|
||||||
|
$sqlparams1['course'] = $course;
|
||||||
|
$ltiactivity = $DB->get_record('lti', array('id' => $linkid, 'course' => $course));
|
||||||
|
if ($ltiactivity) {
|
||||||
|
if ($ltiactivity->typeid == 0) {
|
||||||
|
$tool = lti_get_tool_by_url_match($ltiactivity->toolurl, $course);
|
||||||
|
if (!$tool) {
|
||||||
|
$tool = lti_get_tool_by_url_match($ltiactivity->securetoolurl, $course);
|
||||||
|
}
|
||||||
|
return (($tool) && ($typeid == $tool->id));
|
||||||
|
} else {
|
||||||
|
$sqlparams2 = array();
|
||||||
|
$sqlparams2['linkid'] = $linkid;
|
||||||
|
$sqlparams2['course'] = $course;
|
||||||
|
$sqlparams2['typeid'] = $typeid;
|
||||||
|
$sql = 'SELECT lti.*
|
||||||
|
FROM {lti} lti
|
||||||
|
INNER JOIN {lti_types} typ ON lti.typeid = typ.id
|
||||||
|
WHERE lti.id = ?
|
||||||
|
AND lti.course = ?
|
||||||
|
AND typ.id = ?';
|
||||||
|
return $DB->record_exists_sql($sql, $sqlparams2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes orphaned rows from the 'ltiservice_gradebookservices' table.
|
||||||
|
*
|
||||||
|
* Sometimes, if a gradebook entry is deleted and it was a lineitem
|
||||||
|
* the row in the table ltiservice_gradebookservices can become an orphan
|
||||||
|
* This method will clean these orphans. It will happens based on a task
|
||||||
|
* because it is not urgent and we don't want to slow the service
|
||||||
|
*/
|
||||||
|
public static function delete_orphans_ltiservice_gradebookservices_rows() {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
$sql = "DELETE
|
||||||
|
FROM {ltiservice_gradebookservices}
|
||||||
|
WHERE gradeitemid NOT IN (SELECT id
|
||||||
|
FROM {grade_items} gi
|
||||||
|
WHERE gi.itemtype = 'mod'
|
||||||
|
AND gi.itemmodule = 'lti')";
|
||||||
|
$DB->execute($sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a user can be graded in a course
|
||||||
|
*
|
||||||
|
* @param int $courseid The course
|
||||||
|
* @param int $userid The user
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function is_user_gradable_in_course($courseid, $userid) {
|
||||||
|
global $CFG;
|
||||||
|
|
||||||
|
$gradableuser = false;
|
||||||
|
$coursecontext = \context_course::instance($courseid);
|
||||||
|
if (is_enrolled($coursecontext, $userid, '', false)) {
|
||||||
|
$roles = get_user_roles($coursecontext, $userid);
|
||||||
|
$gradebookroles = explode(',', $CFG->gradebookroles);
|
||||||
|
foreach ($roles as $role) {
|
||||||
|
foreach ($gradebookroles as $gradebookrole) {
|
||||||
|
if ($role->roleid = $gradebookrole) {
|
||||||
|
$gradableuser = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $gradableuser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the right element in the ltiservice_gradebookservice table for a lineitem
|
||||||
|
*
|
||||||
|
* @param string $lineitemid The lineitem
|
||||||
|
* @return object|bool gradebookservice id or false if none
|
||||||
|
*/
|
||||||
|
public static function find_ltiservice_gradebookservice_for_lineitem($lineitemid) {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
if (!$lineitemid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$gradeitem = $DB->get_record('grade_items', array('id' => $lineitemid));
|
||||||
|
if ($gradeitem) {
|
||||||
|
$gbs = $DB->get_record('ltiservice_gradebookservices',
|
||||||
|
array('gradeitemid' => $gradeitem->id, 'courseid' => $gradeitem->courseid));
|
||||||
|
if ($gbs) {
|
||||||
|
return $gbs;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates specific ISO 8601 format of the timestamps.
|
||||||
|
*
|
||||||
|
* @param string $date The timestamp to check.
|
||||||
|
* @return boolean true or false if the date matches the format.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static function validate_iso8601_date($date) {
|
||||||
|
if (preg_match('/^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])' .
|
||||||
|
'(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))' .
|
||||||
|
'([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)' .
|
||||||
|
'?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/', $date) > 0) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scheduled task for gradebookservices.
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @author Dirk Singels, Diego del Blanco, Claude Vervoort
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
namespace ltiservice_gradebookservices\task;
|
||||||
|
|
||||||
|
use core\task\scheduled_task;
|
||||||
|
use ltiservice_gradebookservices\local\service\gradebookservices;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class containing the scheduled task for gradebookservices.
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @author Dirk Singels, Diego del Blanco, Claude Vervoort
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class cleanup_task extends scheduled_task {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a descriptive name for this task (shown to admins).
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get_name() {
|
||||||
|
return get_string('taskcleanup', 'ltiservice_gradebookservices');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run forum cron.
|
||||||
|
*/
|
||||||
|
public function execute() {
|
||||||
|
gradebookservices::delete_orphans_ltiservice_gradebookservices_rows();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
25
mod/lti/service/gradebookservices/db/install.xml
Normal file
25
mod/lti/service/gradebookservices/db/install.xml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<XMLDB PATH="mod/lti/service/gradebookservices/db" VERSION="20150915" COMMENT="XMLDB file for Moodle mod/lti/service/gradebookservices"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="../../../../../lib/xmldb/xmldb.xsd"
|
||||||
|
>
|
||||||
|
<TABLES>
|
||||||
|
<TABLE NAME="ltiservice_gradebookservices" COMMENT="This file records the grade items created by the LTI Gradebook Services service">
|
||||||
|
<FIELDS>
|
||||||
|
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||||
|
<FIELD NAME="gradeitemid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of the gradeItem related."/>
|
||||||
|
<FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of the course related."/>
|
||||||
|
<FIELD NAME="toolproxyid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="ID of the Tool Proxy instance."/>
|
||||||
|
<FIELD NAME="typeid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="ID of the LTI Type if not Proxy."/>
|
||||||
|
<FIELD NAME="baseurl" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Lineitem URL that will be returned to the Tool provider"/>
|
||||||
|
<FIELD NAME="ltilinkid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="ID of the LTI element related with this lineitem."/>
|
||||||
|
<FIELD NAME="tag" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Tag type specified for the line item"/>
|
||||||
|
</FIELDS>
|
||||||
|
<KEYS>
|
||||||
|
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||||
|
<KEY NAME="ltilinkid" TYPE="foreign" FIELDS="ltilinkid" REFTABLE="lti" REFFIELDS="id" COMMENT="The ID of the LTI element related with this lineitem."/>
|
||||||
|
<KEY NAME="itemnumbercourse" TYPE="foreign" FIELDS="gradeitemid,courseid" REFTABLE="grade_items" REFFIELDS="id,courseid" COMMENT="The row in gradeitems"/>
|
||||||
|
</KEYS>
|
||||||
|
</TABLE>
|
||||||
|
</TABLES>
|
||||||
|
</XMLDB>
|
39
mod/lti/service/gradebookservices/db/tasks.php
Normal file
39
mod/lti/service/gradebookservices/db/tasks.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file defines tasks performed by the plugin.
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @author Dirk Singels, Diego del Blanco, Claude Vervoort
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
// List of tasks.
|
||||||
|
$tasks = array(
|
||||||
|
array(
|
||||||
|
'classname' => 'ltiservice_gradebookservices\task\cleanup_task',
|
||||||
|
'blocking' => 0,
|
||||||
|
'minute' => 'R',
|
||||||
|
'hour' => 'R',
|
||||||
|
'day' => '*',
|
||||||
|
'dayofweek' => '*',
|
||||||
|
'month' => '*'
|
||||||
|
)
|
||||||
|
);
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strings for component 'ltiservice_gradebookservices', language 'en'
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @author Dirk Singels, Diego del Blanco, Claude Vervoort
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
$string['alwaysgs'] = 'Use this service for grade sync and column management ';
|
||||||
|
$string['grade_synchronization'] = 'IMS LTI Assignment and Grade Services: ';
|
||||||
|
$string['grade_synchronization_help'] = 'Use the IMS LTI Assignment and Grade Service to synchronize the grades instead Basic Outcomes.
|
||||||
|
|
||||||
|
* **Do not use this service** - This will use the basic outcomes features and configuration
|
||||||
|
* **Use this service for grade sync only** - The service will populate the grades in an already existing gradebook column, but it will not be able to create new columns
|
||||||
|
* **Use this service for grade sync and column management** - The service will be able to create and update gradebook columns and manage the grades. ';
|
||||||
|
$string['modulename'] = 'LTI Grades';
|
||||||
|
$string['nevergs'] = 'Do not use this service';
|
||||||
|
$string['partialgs'] = 'Use this service for grade sync only';
|
||||||
|
$string['pluginname'] = 'LTI Assignment and Grade Services';
|
||||||
|
$string['servicename'] = 'LTI Assignment and Grade Services';
|
||||||
|
$string['taskcleanup'] = 'LTI Assignment and Grade Services table cleanup';
|
103
mod/lti/service/gradebookservices/tests/task_cleanup_test.php
Normal file
103
mod/lti/service/gradebookservices/tests/task_cleanup_test.php
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests cleaning up the gradebook services task.
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @category test
|
||||||
|
* @copyright 2018 Mark Nelson <markn@moodle.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests cleaning up the gradebook services task.
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @category test
|
||||||
|
* @copyright 2018 Mark Nelson <markn@moodle.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class ltiservice_gradebookservices_cleanup_task_testcase extends advanced_testcase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test set up.
|
||||||
|
*
|
||||||
|
* This is executed before running any test in this file.
|
||||||
|
*/
|
||||||
|
public function setUp() {
|
||||||
|
$this->resetAfterTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the cleanup task.
|
||||||
|
*/
|
||||||
|
public function test_cleanup_task() {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
// Create a course.
|
||||||
|
$course = $this->getDataGenerator()->create_course();
|
||||||
|
|
||||||
|
// Create a few LTI items.
|
||||||
|
$lti = $this->getDataGenerator()->create_module('lti', ['course' => $course->id]);
|
||||||
|
$lti2 = $this->getDataGenerator()->create_module('lti', ['course' => $course->id]);
|
||||||
|
|
||||||
|
$conditions = [
|
||||||
|
'courseid' => $course->id,
|
||||||
|
'itemtype' => 'mod',
|
||||||
|
'itemmodule' => 'lti',
|
||||||
|
'iteminstance' => $lti->id
|
||||||
|
];
|
||||||
|
|
||||||
|
// Get the grade items.
|
||||||
|
$gradeitem = $DB->get_record('grade_items', $conditions);
|
||||||
|
|
||||||
|
$conditions['iteminstance'] = $lti2->id;
|
||||||
|
$gradeitem2 = $DB->get_record('grade_items', $conditions);
|
||||||
|
|
||||||
|
// Insert these into the 'ltiservice_gradebookservices' table.
|
||||||
|
$data = new stdClass();
|
||||||
|
$data->gradeitemid = $gradeitem->id;
|
||||||
|
$data->courseid = $course->id;
|
||||||
|
$DB->insert_record('ltiservice_gradebookservices', $data);
|
||||||
|
|
||||||
|
$data->gradeitemid = $gradeitem2->id;
|
||||||
|
$DB->insert_record('ltiservice_gradebookservices', $data);
|
||||||
|
|
||||||
|
$task = new \ltiservice_gradebookservices\task\cleanup_task();
|
||||||
|
$task->execute();
|
||||||
|
|
||||||
|
// Check they both still exist.
|
||||||
|
$this->assertEquals(2, $DB->count_records('ltiservice_gradebookservices'));
|
||||||
|
|
||||||
|
// Delete the first LTI activity.
|
||||||
|
course_delete_module($lti->cmid);
|
||||||
|
|
||||||
|
// Run the task again.
|
||||||
|
$task = new \ltiservice_gradebookservices\task\cleanup_task();
|
||||||
|
$task->execute();
|
||||||
|
|
||||||
|
// Check only the second grade item exists.
|
||||||
|
$gradebookserviceitems = $DB->get_records('ltiservice_gradebookservices');
|
||||||
|
$this->assertCount(1, $gradebookserviceitems);
|
||||||
|
|
||||||
|
$gradebookserviceitem = reset($gradebookserviceitems);
|
||||||
|
|
||||||
|
$this->assertEquals($gradeitem2->id, $gradebookserviceitem->gradeitemid);
|
||||||
|
}
|
||||||
|
}
|
30
mod/lti/service/gradebookservices/version.php
Normal file
30
mod/lti/service/gradebookservices/version.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version information for the ltiservice_gradebookservices service.
|
||||||
|
*
|
||||||
|
* @package ltiservice_gradebookservices
|
||||||
|
* @copyright 2017 Cengage Learning http://www.cengage.com
|
||||||
|
* @author Dirk Singels, Diego del Blanco, Claude Vervoort
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
$plugin->version = 2018011100;
|
||||||
|
$plugin->requires = 2018010100;
|
||||||
|
$plugin->component = 'ltiservice_gradebookservices';
|
|
@ -26,8 +26,9 @@
|
||||||
|
|
||||||
namespace ltiservice_memberships\local\resources;
|
namespace ltiservice_memberships\local\resources;
|
||||||
|
|
||||||
use \mod_lti\local\ltiservice\service_base;
|
use mod_lti\local\ltiservice\resource_base;
|
||||||
use ltiservice_memberships\local\service\memberships;
|
use ltiservice_memberships\local\service\memberships;
|
||||||
|
use core_availability\info_module;
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
@ -39,18 +40,18 @@ defined('MOODLE_INTERNAL') || die();
|
||||||
* @copyright 2015 Vital Source Technologies http://vitalsource.com
|
* @copyright 2015 Vital Source Technologies http://vitalsource.com
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
class contextmemberships extends \mod_lti\local\ltiservice\resource_base {
|
class contextmemberships extends resource_base {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Class constructor.
|
||||||
*
|
*
|
||||||
* @param ltiservice_memberships\local\service\memberships $service Service instance
|
* @param \ltiservice_memberships\local\service\memberships $service Service instance
|
||||||
*/
|
*/
|
||||||
public function __construct($service) {
|
public function __construct($service) {
|
||||||
|
|
||||||
parent::__construct($service);
|
parent::__construct($service);
|
||||||
$this->id = 'ToolProxyBindingMemberships';
|
$this->id = 'ToolProxyBindingMemberships';
|
||||||
$this->template = '/{context_type}/{context_id}/bindings/{vendor_code}/{product_code}/{tool_code}/memberships';
|
$this->template = '/{context_type}/{context_id}/bindings/{tool_code}/memberships';
|
||||||
$this->variables[] = 'ToolProxyBinding.memberships.url';
|
$this->variables[] = 'ToolProxyBinding.memberships.url';
|
||||||
$this->formats[] = 'application/vnd.ims.lis.v2.membershipcontainer+json';
|
$this->formats[] = 'application/vnd.ims.lis.v2.membershipcontainer+json';
|
||||||
$this->methods[] = 'GET';
|
$this->methods[] = 'GET';
|
||||||
|
@ -60,23 +61,24 @@ class contextmemberships extends \mod_lti\local\ltiservice\resource_base {
|
||||||
/**
|
/**
|
||||||
* Execute the request for this resource.
|
* Execute the request for this resource.
|
||||||
*
|
*
|
||||||
* @param mod_lti\local\ltiservice\response $response Response object for this request.
|
* @param \mod_lti\local\ltiservice\response $response Response object for this request.
|
||||||
*/
|
*/
|
||||||
public function execute($response) {
|
public function execute($response) {
|
||||||
global $CFG, $DB;
|
global $DB;
|
||||||
|
|
||||||
$params = $this->parse_template();
|
$params = $this->parse_template();
|
||||||
$role = optional_param('role', '', PARAM_TEXT);
|
$role = optional_param('role', '', PARAM_TEXT);
|
||||||
$limitnum = optional_param('limit', 0, PARAM_INT);
|
$limitnum = optional_param('limit', 0, PARAM_INT);
|
||||||
$limitfrom = optional_param('from', 0, PARAM_INT);
|
$limitfrom = optional_param('from', 0, PARAM_INT);
|
||||||
|
$linkid = optional_param('rlid', '', PARAM_TEXT);
|
||||||
|
$lti = null;
|
||||||
|
$modinfo = null;
|
||||||
|
|
||||||
if ($limitnum <= 0) {
|
if ($limitnum <= 0) {
|
||||||
$limitfrom = 0;
|
$limitfrom = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!$this->get_service()->check_tool_proxy($params['product_code'])) {
|
|
||||||
throw new \Exception(null, 401);
|
|
||||||
}
|
|
||||||
if (!($course = $DB->get_record('course', array('id' => $params['context_id']), 'id', IGNORE_MISSING))) {
|
if (!($course = $DB->get_record('course', array('id' => $params['context_id']), 'id', IGNORE_MISSING))) {
|
||||||
throw new \Exception(null, 404);
|
throw new \Exception(null, 404);
|
||||||
}
|
}
|
||||||
|
@ -84,14 +86,33 @@ class contextmemberships extends \mod_lti\local\ltiservice\resource_base {
|
||||||
throw new \Exception(null, 404);
|
throw new \Exception(null, 404);
|
||||||
}
|
}
|
||||||
if (!($tool = $DB->get_record('lti_types', array('id' => $params['tool_code']),
|
if (!($tool = $DB->get_record('lti_types', array('id' => $params['tool_code']),
|
||||||
'toolproxyid,enabledcapability,parameter', IGNORE_MISSING))) {
|
'id,toolproxyid,enabledcapability,parameter', IGNORE_MISSING))) {
|
||||||
throw new \Exception(null, 404);
|
throw new \Exception(null, 404);
|
||||||
}
|
}
|
||||||
$toolproxy = $DB->get_record('lti_tool_proxies', array('id' => $tool->toolproxyid), 'guid', IGNORE_MISSING);
|
if (!empty($linkid)) {
|
||||||
if (!$toolproxy || ($toolproxy->guid !== $this->get_service()->get_tool_proxy()->guid)) {
|
if (!($lti = $DB->get_record('lti', array('id' => $linkid), 'id,course,typeid,servicesalt', IGNORE_MISSING))) {
|
||||||
throw new \Exception(null, 400);
|
throw new \Exception(null, 404);
|
||||||
|
}
|
||||||
|
$modinfo = get_fast_modinfo($course);
|
||||||
|
$cm = get_coursemodule_from_instance('lti', $linkid, $lti->course, false, MUST_EXIST);
|
||||||
|
$cm = $modinfo->get_cm($cm->id);
|
||||||
|
$modinfo = new info_module($cm);
|
||||||
|
if ($modinfo->is_available_for_all()) {
|
||||||
|
$modinfo = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$json = memberships::get_users_json($this, $context, $course->id, $tool, $role, $limitfrom, $limitnum, null, null);
|
if ($tool->toolproxyid == 0) {
|
||||||
|
if (!$this->check_type($params['tool_code'], $params['context_id'],
|
||||||
|
'ToolProxyBinding.memberships.url:get', null)) {
|
||||||
|
throw new \Exception(null, 403);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$toolproxy = $DB->get_record('lti_tool_proxies', array('id' => $tool->toolproxyid), 'guid', IGNORE_MISSING);
|
||||||
|
if (!$this->check_tool_proxy($toolproxy->guid, $response->get_request_data())) {
|
||||||
|
throw new \Exception(null, 403);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$json = memberships::get_users_json($this, $context, $course->id, $tool, $role, $limitfrom, $limitnum, $lti, $modinfo);
|
||||||
|
|
||||||
$response->set_content_type($this->formats[0]);
|
$response->set_content_type($this->formats[0]);
|
||||||
$response->set_body($json);
|
$response->set_body($json);
|
||||||
|
@ -101,6 +122,21 @@ class contextmemberships extends \mod_lti\local\ltiservice\resource_base {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get permissions from the config of the tool for that resource
|
||||||
|
*
|
||||||
|
* @param int $typeid
|
||||||
|
* @return array with the permissions related to this resource by the $lti_type or null if none.
|
||||||
|
*/
|
||||||
|
public function get_permissions($typeid) {
|
||||||
|
$tool = lti_get_type_type_config($typeid);
|
||||||
|
if ($tool->ltiservice_memberships == '1') {
|
||||||
|
return array('ToolProxyBinding.memberships.url:get');
|
||||||
|
} else {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a value for custom parameter substitution variables.
|
* Parse a value for custom parameter substitution variables.
|
||||||
*
|
*
|
||||||
|
@ -111,25 +147,24 @@ class contextmemberships extends \mod_lti\local\ltiservice\resource_base {
|
||||||
public function parse_value($value) {
|
public function parse_value($value) {
|
||||||
global $COURSE, $DB;
|
global $COURSE, $DB;
|
||||||
|
|
||||||
if ($COURSE->id === SITEID) {
|
if (strpos($value, '$ToolProxyBinding.memberships.url') !== false) {
|
||||||
$this->params['context_type'] = 'Group';
|
if ($COURSE->id === SITEID) {
|
||||||
} else {
|
$this->params['context_type'] = 'Group';
|
||||||
$this->params['context_type'] = 'CourseSection';
|
} else {
|
||||||
}
|
$this->params['context_type'] = 'CourseSection';
|
||||||
$this->params['context_id'] = $COURSE->id;
|
|
||||||
$this->params['vendor_code'] = $this->get_service()->get_tool_proxy()->vendorcode;
|
|
||||||
$this->params['product_code'] = $this->get_service()->get_tool_proxy()->guid;
|
|
||||||
|
|
||||||
$id = optional_param('id', 0, PARAM_INT); // Course Module ID.
|
|
||||||
if (!empty($id)) {
|
|
||||||
$cm = get_coursemodule_from_id('lti', $id, 0, false, IGNORE_MISSING);
|
|
||||||
$lti = $DB->get_record('lti', array('id' => $cm->instance), 'typeid', IGNORE_MISSING);
|
|
||||||
if ($lti && !empty($lti->typeid)) {
|
|
||||||
$this->params['tool_code'] = $lti->typeid;
|
|
||||||
}
|
}
|
||||||
}
|
$this->params['context_id'] = $COURSE->id;
|
||||||
$value = str_replace('$ToolProxyBinding.memberships.url', parent::get_endpoint(), $value);
|
|
||||||
|
|
||||||
|
$id = optional_param('id', 0, PARAM_INT); // Course Module ID.
|
||||||
|
if (!empty($id)) {
|
||||||
|
$cm = get_coursemodule_from_id('lti', $id, 0, false, IGNORE_MISSING);
|
||||||
|
$lti = $DB->get_record('lti', array('id' => $cm->instance), 'typeid', IGNORE_MISSING);
|
||||||
|
if ($lti && !empty($lti->typeid)) {
|
||||||
|
$this->params['tool_code'] = $lti->typeid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$value = str_replace('$ToolProxyBinding.memberships.url', parent::get_endpoint(), $value);
|
||||||
|
}
|
||||||
return $value;
|
return $value;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,27 +26,29 @@
|
||||||
|
|
||||||
namespace ltiservice_memberships\local\resources;
|
namespace ltiservice_memberships\local\resources;
|
||||||
|
|
||||||
use \mod_lti\local\ltiservice\service_base;
|
use mod_lti\local\ltiservice\resource_base;
|
||||||
use ltiservice_memberships\local\service\memberships;
|
use ltiservice_memberships\local\service\memberships;
|
||||||
use core_availability\info;
|
|
||||||
use core_availability\info_module;
|
use core_availability\info_module;
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A resource implementing Link Memberships.
|
* A resource implementing Link Memberships.
|
||||||
|
* The link membership is no longer defined in the published
|
||||||
|
* version of the LTI specification. It is replaced by the
|
||||||
|
* rlid parameter in the context membership URL.
|
||||||
*
|
*
|
||||||
* @package ltiservice_memberships
|
* @package ltiservice_memberships
|
||||||
* @since Moodle 3.0
|
* @since Moodle 3.0
|
||||||
* @copyright 2015 Vital Source Technologies http://vitalsource.com
|
* @copyright 2015 Vital Source Technologies http://vitalsource.com
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
class linkmemberships extends \mod_lti\local\ltiservice\resource_base {
|
class linkmemberships extends resource_base {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Class constructor.
|
||||||
*
|
*
|
||||||
* @param ltiservice_memberships\local\service\memberships $service Service instance
|
* @param \ltiservice_memberships\local\service\memberships $service Service instance
|
||||||
*/
|
*/
|
||||||
public function __construct($service) {
|
public function __construct($service) {
|
||||||
|
|
||||||
|
@ -62,55 +64,77 @@ class linkmemberships extends \mod_lti\local\ltiservice\resource_base {
|
||||||
/**
|
/**
|
||||||
* Execute the request for this resource.
|
* Execute the request for this resource.
|
||||||
*
|
*
|
||||||
* @param mod_lti\local\ltiservice\response $response Response object for this request.
|
* @param \mod_lti\local\ltiservice\response $response Response object for this request.
|
||||||
*/
|
*/
|
||||||
public function execute($response) {
|
public function execute($response) {
|
||||||
global $CFG, $DB;
|
global $DB;
|
||||||
|
|
||||||
$params = $this->parse_template();
|
$params = $this->parse_template();
|
||||||
$linkid = $params['link_id'];
|
$linkid = $params['link_id'];
|
||||||
$role = optional_param('role', '', PARAM_TEXT);
|
$role = optional_param('role', '', PARAM_TEXT);
|
||||||
$limitnum = optional_param('limit', 0, PARAM_INT);
|
$limitnum = optional_param('limit', 0, PARAM_INT);
|
||||||
$limitfrom = optional_param('from', 0, PARAM_INT);
|
$limitfrom = optional_param('from', 0, PARAM_INT);
|
||||||
|
|
||||||
if ($limitnum <= 0) {
|
if ($limitnum <= 0) {
|
||||||
$limitfrom = 0;
|
$limitfrom = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (empty($linkid)) {
|
||||||
if (empty($linkid)) {
|
$response->set_code(404);
|
||||||
throw new \Exception(null, 404);
|
return;
|
||||||
|
}
|
||||||
|
if (!($lti = $DB->get_record('lti', array('id' => $linkid), 'id,course,typeid,servicesalt', IGNORE_MISSING))) {
|
||||||
|
$response->set_code(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$tool = $DB->get_record('lti_types', array('id' => $lti->typeid));
|
||||||
|
if ($tool->toolproxyid == 0) { // We wil use the same permission for this and contextmembers.
|
||||||
|
if (!$this->check_type($lti->typeid, $lti->course, 'ToolProxyBinding.memberships.url:get', null)) {
|
||||||
|
$response->set_code(403);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!($lti = $DB->get_record('lti', array('id' => $linkid), 'id,course,typeid,servicesalt', IGNORE_MISSING))) {
|
} else {
|
||||||
throw new \Exception(null, 404);
|
|
||||||
}
|
|
||||||
$tool = $DB->get_record('lti_types', array('id' => $lti->typeid));
|
|
||||||
$toolproxy = $DB->get_record('lti_tool_proxies', array('id' => $tool->toolproxyid));
|
$toolproxy = $DB->get_record('lti_tool_proxies', array('id' => $tool->toolproxyid));
|
||||||
if (!$this->check_tool_proxy($toolproxy->guid, $response->get_request_data())) {
|
if (!$this->check_tool_proxy($toolproxy->guid, $response->get_request_data())) {
|
||||||
throw new \Exception(null, 401);
|
$response->set_code(403);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!($course = $DB->get_record('course', array('id' => $lti->course), 'id', IGNORE_MISSING))) {
|
|
||||||
throw new \Exception(null, 404);
|
|
||||||
}
|
|
||||||
if (!($context = \context_course::instance($lti->course))) {
|
|
||||||
throw new \Exception(null, 404);
|
|
||||||
}
|
|
||||||
$modinfo = get_fast_modinfo($course);
|
|
||||||
$cm = get_coursemodule_from_instance('lti', $linkid, $lti->course, false, MUST_EXIST);
|
|
||||||
$cm = $modinfo->get_cm($cm->id);
|
|
||||||
$info = new info_module($cm);
|
|
||||||
if ($info->is_available_for_all()) {
|
|
||||||
$info = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$json = memberships::get_users_json($this, $context, $lti->course, $tool, $role, $limitfrom, $limitnum, $lti, $info);
|
|
||||||
|
|
||||||
$response->set_content_type($this->formats[0]);
|
|
||||||
$response->set_body($json);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$response->set_code($e->getCode());
|
|
||||||
}
|
}
|
||||||
|
if (!($course = $DB->get_record('course', array('id' => $lti->course), 'id', IGNORE_MISSING))) {
|
||||||
|
$response->set_code(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!($context = \context_course::instance($lti->course))) {
|
||||||
|
$response->set_code(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$modinfo = get_fast_modinfo($course);
|
||||||
|
$cm = get_coursemodule_from_instance('lti', $linkid, $lti->course, false, MUST_EXIST);
|
||||||
|
$cm = $modinfo->get_cm($cm->id);
|
||||||
|
$info = new info_module($cm);
|
||||||
|
if ($info->is_available_for_all()) {
|
||||||
|
$info = null;
|
||||||
|
}
|
||||||
|
$json = memberships::get_users_json($this, $context, $lti->course, $tool, $role, $limitfrom, $limitnum, $lti, $info);
|
||||||
|
|
||||||
|
$response->set_content_type($this->formats[0]);
|
||||||
|
$response->set_body($json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get permissions from the config of the tool for that resource
|
||||||
|
*
|
||||||
|
* @param string $typeid
|
||||||
|
*
|
||||||
|
* @return array with the permissions related to this resource by the $lti_type or null if none.
|
||||||
|
*/
|
||||||
|
public function get_permissions($typeid) {
|
||||||
|
$tool = lti_get_type_type_config($typeid);
|
||||||
|
if ($tool->memberships == '1') {
|
||||||
|
return array('ToolProxyBinding.memberships.url:get');
|
||||||
|
} else {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -122,13 +146,14 @@ class linkmemberships extends \mod_lti\local\ltiservice\resource_base {
|
||||||
*/
|
*/
|
||||||
public function parse_value($value) {
|
public function parse_value($value) {
|
||||||
|
|
||||||
$id = optional_param('id', 0, PARAM_INT); // Course Module ID.
|
if (strpos($value, '$ToolProxyBinding.memberships.url') !== false) {
|
||||||
if (!empty($id)) {
|
$id = optional_param('id', 0, PARAM_INT); // Course Module ID.
|
||||||
$cm = get_coursemodule_from_id('lti', $id, 0, false, MUST_EXIST);
|
if (!empty($id)) {
|
||||||
$this->params['link_id'] = $cm->instance;
|
$cm = get_coursemodule_from_id('lti', $id, 0, false, MUST_EXIST);
|
||||||
|
$this->params['link_id'] = $cm->instance;
|
||||||
|
}
|
||||||
|
$value = str_replace('$LtiLink.memberships.url', parent::get_endpoint(), $value);
|
||||||
}
|
}
|
||||||
$value = str_replace('$LtiLink.memberships.url', parent::get_endpoint(), $value);
|
|
||||||
|
|
||||||
return $value;
|
return $value;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace ltiservice_memberships\local\service;
|
namespace ltiservice_memberships\local\service;
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
@ -46,6 +45,18 @@ class memberships extends \mod_lti\local\ltiservice\service_base {
|
||||||
const CONTEXT_ROLE_LEARNER = 'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner';
|
const CONTEXT_ROLE_LEARNER = 'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner';
|
||||||
/** Capability used to identify Instructors */
|
/** Capability used to identify Instructors */
|
||||||
const INSTRUCTOR_CAPABILITY = 'moodle/course:manageactivities';
|
const INSTRUCTOR_CAPABILITY = 'moodle/course:manageactivities';
|
||||||
|
/** Name of LTI service component */
|
||||||
|
const LTI_SERVICE_COMPONENT = 'ltiservice_memberships';
|
||||||
|
/** Membership services enabled */
|
||||||
|
const MEMBERSHIP_ENABLED = 1;
|
||||||
|
/** Always include field */
|
||||||
|
const ALWAYS_INCLUDE_FIELD = 1;
|
||||||
|
/** Allow the instructor to decide if included */
|
||||||
|
const DELEGATE_TO_INSTRUCTOR = 2;
|
||||||
|
/** Instructor chose to include field */
|
||||||
|
const INSTRUCTOR_INCLUDED = 1;
|
||||||
|
/** Instructor delegated and approved for include */
|
||||||
|
const INSTRUCTOR_DELEGATE_INCLUDED = array(self::DELEGATE_TO_INSTRUCTOR && self::INSTRUCTOR_INCLUDED);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Class constructor.
|
||||||
|
@ -54,7 +65,7 @@ class memberships extends \mod_lti\local\ltiservice\service_base {
|
||||||
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->id = 'memberships';
|
$this->id = 'memberships';
|
||||||
$this->name = get_string('servicename', 'ltiservice_memberships');
|
$this->name = get_string('servicename', self::LTI_SERVICE_COMPONENT);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,17 +91,18 @@ class memberships extends \mod_lti\local\ltiservice\service_base {
|
||||||
*
|
*
|
||||||
* @param \mod_lti\local\ltiservice\resource_base $resource Resource handling the request
|
* @param \mod_lti\local\ltiservice\resource_base $resource Resource handling the request
|
||||||
* @param \context_course $context Course context
|
* @param \context_course $context Course context
|
||||||
* @param string $id Course ID
|
* @param string $contextid Course ID
|
||||||
* @param object $tool Tool instance object
|
* @param object $tool Tool instance object
|
||||||
* @param string $role User role requested (empty if none)
|
* @param string $role User role requested (empty if none)
|
||||||
* @param int $limitfrom Position of first record to be returned
|
* @param int $limitfrom Position of first record to be returned
|
||||||
* @param int $limitnum Maximum number of records to be returned
|
* @param int $limitnum Maximum number of records to be returned
|
||||||
* @param object $lti LTI instance record
|
* @param object $lti LTI instance record
|
||||||
* @param info_module $info Conditional availability information for LTI instance (null if context-level request)
|
* @param \core_availability\info_module $info Conditional availability information
|
||||||
|
* for LTI instance (null if context-level request)
|
||||||
*
|
*
|
||||||
* @return array
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function get_users_json($resource, $context, $id, $tool, $role, $limitfrom, $limitnum, $lti, $info) {
|
public static function get_users_json($resource, $context, $contextid, $tool, $role, $limitfrom, $limitnum, $lti, $info) {
|
||||||
|
|
||||||
$withcapability = '';
|
$withcapability = '';
|
||||||
$exclude = array();
|
$exclude = array();
|
||||||
|
@ -110,10 +122,9 @@ class memberships extends \mod_lti\local\ltiservice\service_base {
|
||||||
$limitfrom = 0;
|
$limitfrom = 0;
|
||||||
$limitnum = 0;
|
$limitnum = 0;
|
||||||
}
|
}
|
||||||
$json = self::users_to_json($resource, $users, $id, $tool, $exclude, $limitfrom, $limitnum, $lti, $info);
|
$json = self::users_to_json($resource, $users, $contextid, $tool, $exclude, $limitfrom, $limitnum, $lti, $info);
|
||||||
|
|
||||||
return $json;
|
return $json;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -124,7 +135,7 @@ class memberships extends \mod_lti\local\ltiservice\service_base {
|
||||||
*
|
*
|
||||||
* @param \mod_lti\local\ltiservice\resource_base $resource Resource handling the request
|
* @param \mod_lti\local\ltiservice\resource_base $resource Resource handling the request
|
||||||
* @param array $users Array of user records
|
* @param array $users Array of user records
|
||||||
* @param string $id Course ID
|
* @param string $contextid Course ID
|
||||||
* @param object $tool Tool instance object
|
* @param object $tool Tool instance object
|
||||||
* @param array $exclude Array of user records to be excluded from the response
|
* @param array $exclude Array of user records to be excluded from the response
|
||||||
* @param int $limitfrom Position of first record to be returned
|
* @param int $limitfrom Position of first record to be returned
|
||||||
|
@ -134,82 +145,197 @@ class memberships extends \mod_lti\local\ltiservice\service_base {
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private static function users_to_json($resource, $users, $id, $tool, $exclude, $limitfrom, $limitnum,
|
private static function users_to_json($resource, $users, $contextid, $tool, $exclude, $limitfrom, $limitnum,
|
||||||
$lti, $info) {
|
$lti, $info) {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
$arrusers = [
|
||||||
|
'@context' => 'http://purl.imsglobal.org/ctx/lis/v2/MembershipContainer',
|
||||||
|
'@type' => 'Page',
|
||||||
|
'@id' => $resource->get_endpoint(),
|
||||||
|
];
|
||||||
|
|
||||||
$nextpage = 'null';
|
|
||||||
if ($limitnum > 0) {
|
if ($limitnum > 0) {
|
||||||
$limitfrom += $limitnum;
|
$limitfrom += $limitnum;
|
||||||
$nextpage = "\"{$resource->get_endpoint()}?limit={$limitnum}&from={$limitfrom}\"";
|
$nextpage = "{$resource->get_endpoint()}?limit={$limitnum}&from={$limitfrom}";
|
||||||
|
if (!is_null($lti)) {
|
||||||
|
$nextpage .= "&rlid={$lti->id}";
|
||||||
|
}
|
||||||
|
$arrusers['nextPage'] = $nextpage;
|
||||||
}
|
}
|
||||||
$json = <<< EOD
|
|
||||||
{
|
|
||||||
"@context" : "http://purl.imsglobal.org/ctx/lis/v2/MembershipContainer",
|
|
||||||
"@type" : "Page",
|
|
||||||
"@id" : "{$resource->get_endpoint()}",
|
|
||||||
"nextPage" : {$nextpage},
|
|
||||||
"pageOf" : {
|
|
||||||
"@type" : "LISMembershipContainer",
|
|
||||||
"membershipSubject" : {
|
|
||||||
"@type" : "Context",
|
|
||||||
"contextId" : "{$id}",
|
|
||||||
"membership" : [
|
|
||||||
|
|
||||||
EOD;
|
$arrusers['pageOf'] = [
|
||||||
|
'@type' => 'LISMembershipContainer',
|
||||||
|
'membershipSubject' => [
|
||||||
|
'@type' => 'Context',
|
||||||
|
'contextId' => $contextid,
|
||||||
|
'membership' => []
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
$enabledcapabilities = lti_get_enabled_capabilities($tool);
|
$enabledcapabilities = lti_get_enabled_capabilities($tool);
|
||||||
$sep = ' ';
|
$islti2 = $tool->toolproxyid > 0;
|
||||||
foreach ($users as $user) {
|
foreach ($users as $user) {
|
||||||
$include = !in_array($user->id, $exclude);
|
if (in_array($user->id, $exclude)) {
|
||||||
if ($include && !empty($info)) {
|
continue;
|
||||||
$include = $info->is_user_visible($info->get_course_module(), $user->id);
|
|
||||||
}
|
}
|
||||||
if ($include) {
|
if (!empty($info) && !$info->is_user_visible($info->get_course_module(), $user->id)) {
|
||||||
$member = new \stdClass();
|
continue;
|
||||||
if (in_array('User.id', $enabledcapabilities)) {
|
|
||||||
$member->userId = $user->id;
|
|
||||||
}
|
|
||||||
if (in_array('Person.sourcedId', $enabledcapabilities)) {
|
|
||||||
$member->sourcedId = format_string($user->idnumber);
|
|
||||||
}
|
|
||||||
if (in_array('Person.name.full', $enabledcapabilities)) {
|
|
||||||
$member->name = format_string("{$user->firstname} {$user->lastname}");
|
|
||||||
}
|
|
||||||
if (in_array('Person.name.given', $enabledcapabilities)) {
|
|
||||||
$member->givenName = format_string($user->firstname);
|
|
||||||
}
|
|
||||||
if (in_array('Person.name.family', $enabledcapabilities)) {
|
|
||||||
$member->familyName = format_string($user->lastname);
|
|
||||||
}
|
|
||||||
if (in_array('Person.email.primary', $enabledcapabilities)) {
|
|
||||||
$member->email = format_string($user->email);
|
|
||||||
}
|
|
||||||
if (in_array('Result.sourcedId', $enabledcapabilities) && !empty($lti) && !empty($lti->servicesalt)) {
|
|
||||||
$member->resultSourcedId = json_encode(lti_build_sourcedid($lti->id, $user->id, $lti->servicesalt,
|
|
||||||
$lti->typeid));
|
|
||||||
}
|
|
||||||
$roles = explode(',', lti_get_ims_role($user->id, null, $id, true));
|
|
||||||
|
|
||||||
$membership = new \stdClass();
|
|
||||||
$membership->status = 'Active';
|
|
||||||
$membership->member = $member;
|
|
||||||
$membership->role = $roles;
|
|
||||||
|
|
||||||
$json .= $sep . json_encode($membership);
|
|
||||||
$sep = ",\n ";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$member = new \stdClass();
|
||||||
|
$member->{"@type" } = 'LISPerson';
|
||||||
|
$membership = new \stdClass();
|
||||||
|
$membership->status = 'Active';
|
||||||
|
$membership->role = explode(',', lti_get_ims_role($user->id, null, $contextid, true));
|
||||||
|
|
||||||
|
$toolconfig = lti_get_type_type_config($tool->id);
|
||||||
|
$instanceconfig = null;
|
||||||
|
if (!is_null($lti)) {
|
||||||
|
$instanceconfig = lti_get_type_config_from_instance($lti->id);
|
||||||
|
}
|
||||||
|
$isallowedlticonfig = self::is_allowed_field_set($toolconfig, $instanceconfig,
|
||||||
|
['name' => 'lti_sendname', 'email' => 'lti_sendemailaddr']);
|
||||||
|
|
||||||
|
$includedcapabilities = [
|
||||||
|
'User.id' => ['type' => 'id',
|
||||||
|
'member.field' => 'userId',
|
||||||
|
'source.value' => $user->id],
|
||||||
|
'Person.sourcedId' => ['type' => 'id',
|
||||||
|
'member.field' => 'sourcedId',
|
||||||
|
'source.value' => format_string($user->idnumber)],
|
||||||
|
'Person.name.full' => ['type' => 'name',
|
||||||
|
'member.field' => 'name',
|
||||||
|
'source.value' => format_string("{$user->firstname} {$user->lastname}")],
|
||||||
|
'Person.name.given' => ['type' => 'name',
|
||||||
|
'member.field' => 'givenName',
|
||||||
|
'source.value' => format_string($user->firstname)],
|
||||||
|
'Person.name.family' => ['type' => 'name',
|
||||||
|
'member.field' => 'familyName',
|
||||||
|
'source.value' => format_string($user->lastname)],
|
||||||
|
'Person.email.primary' => ['type' => 'email',
|
||||||
|
'member.field' => 'email',
|
||||||
|
'source.value' => format_string($user->email)]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!is_null($lti)) {
|
||||||
|
$message = new \stdClass();
|
||||||
|
$message->message_type = 'basic-lti-launch-request';
|
||||||
|
$conditions = array('courseid' => $contextid, 'itemtype' => 'mod',
|
||||||
|
'itemmodule' => 'lti', 'iteminstance' => $lti->id);
|
||||||
|
|
||||||
|
if (!empty($lti->servicesalt) && $DB->record_exists('grade_items', $conditions)) {
|
||||||
|
$message->lis_result_sourcedid = json_encode(lti_build_sourcedid($lti->id,
|
||||||
|
$user->id,
|
||||||
|
$lti->servicesalt,
|
||||||
|
$lti->typeid));
|
||||||
|
}
|
||||||
|
$membership->message = $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($includedcapabilities as $capabilityname => $capability) {
|
||||||
|
if ($islti2) {
|
||||||
|
if (!in_array($capabilityname, $enabledcapabilities)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (($capability['type'] === 'id')
|
||||||
|
|| ($capability['type'] === 'name' && $isallowedlticonfig['name'])
|
||||||
|
|| ($capability['type'] === 'email' && $isallowedlticonfig['email'])) {
|
||||||
|
$member->{$capability['member.field']} = $capability['source.value'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$membership->member = $member;
|
||||||
|
|
||||||
|
$arrusers['pageOf']['membershipSubject']['membership'][] = $membership;
|
||||||
}
|
}
|
||||||
|
|
||||||
$json .= <<< EOD
|
return json_encode($arrusers);
|
||||||
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
EOD;
|
|
||||||
|
|
||||||
return $json;
|
/**
|
||||||
|
* Determines whether a user attribute may be used as part of LTI membership
|
||||||
|
* @param object $toolconfig Tool config
|
||||||
|
* @param object $instanceconfig Tool instance config
|
||||||
|
* @param array $fields Set of fields to return if allowed or not
|
||||||
|
* @return array Verification which associates an attribute with a boolean (allowed or not)
|
||||||
|
*/
|
||||||
|
private static function is_allowed_field_set($toolconfig, $instanceconfig, $fields) {
|
||||||
|
$isallowedstate = [];
|
||||||
|
foreach ($fields as $key => $field) {
|
||||||
|
$allowed = self::ALWAYS_INCLUDE_FIELD == $toolconfig->{$field};
|
||||||
|
if (!$allowed) {
|
||||||
|
if (self::DELEGATE_TO_INSTRUCTOR == $toolconfig->{$field} && !is_null($instanceconfig)) {
|
||||||
|
$allowed = $instanceconfig->{$field} == self::INSTRUCTOR_INCLUDED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$isallowedstate[$key] = $allowed;
|
||||||
|
}
|
||||||
|
return $isallowedstate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds form elements for membership add/edit page.
|
||||||
|
*
|
||||||
|
* @param \MoodleQuickForm $mform
|
||||||
|
*/
|
||||||
|
public function get_configuration_options(&$mform) {
|
||||||
|
$elementname = 'ltiservice_memberships';
|
||||||
|
$options = [
|
||||||
|
get_string('notallow', self::LTI_SERVICE_COMPONENT),
|
||||||
|
get_string('allow', self::LTI_SERVICE_COMPONENT)
|
||||||
|
];
|
||||||
|
|
||||||
|
$mform->addElement('select', $elementname, get_string($elementname, self::LTI_SERVICE_COMPONENT), $options);
|
||||||
|
$mform->setType($elementname, 'int');
|
||||||
|
$mform->setDefault($elementname, 0);
|
||||||
|
$mform->addHelpButton($elementname, $elementname, self::LTI_SERVICE_COMPONENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array with the names of the parameters that the service will be saving in the configuration
|
||||||
|
*
|
||||||
|
* @return array with the names of the parameters that the service will be saving in the configuration
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function get_configuration_parameter_names() {
|
||||||
|
return array(self::LTI_SERVICE_COMPONENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of key/values to add to the launch parameters.
|
||||||
|
*
|
||||||
|
* @param string $messagetype 'basic-lti-launch-request' or 'ContentItemSelectionRequest'.
|
||||||
|
* @param string $courseid The course id.
|
||||||
|
* @param string $user The user id.
|
||||||
|
* @param string $typeid The tool lti type id.
|
||||||
|
* @param string $modlti The id of the lti activity.
|
||||||
|
*
|
||||||
|
* The type is passed to check the configuration
|
||||||
|
* and not return parameters for services not used.
|
||||||
|
*
|
||||||
|
* @return array of key/value pairs to add as launch parameters.
|
||||||
|
*/
|
||||||
|
public function get_launch_parameters($messagetype, $courseid, $user, $typeid, $modlti = null) {
|
||||||
|
global $COURSE;
|
||||||
|
|
||||||
|
$launchparameters = array();
|
||||||
|
$tool = lti_get_type_type_config($typeid);
|
||||||
|
if (isset($tool->ltiservice_memberships)) {
|
||||||
|
if ($tool->ltiservice_memberships == '1' && $this->is_used_in_context($typeid, $courseid)) {
|
||||||
|
$endpoint = $this->get_service_path();
|
||||||
|
if ($COURSE->id === SITEID) {
|
||||||
|
$contexttype = 'Group';
|
||||||
|
} else {
|
||||||
|
$contexttype = 'CourseSection';
|
||||||
|
}
|
||||||
|
$launchparameters['custom_context_memberships_url'] = $endpoint .
|
||||||
|
"/{$contexttype}/{$courseid}/bindings/{$typeid}/memberships";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $launchparameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,5 +23,9 @@
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
$string['allow'] = 'Use this service to retrieve members\' information as per privacy settings';
|
||||||
|
$string['ltiservice_memberships'] = 'IMS LTI Membership: ';
|
||||||
|
$string['ltiservice_memberships_help'] = 'Allow the tool to retrieve member\'s info from the course using the IMS LTI Membership Service. The privacy settings will apply.';
|
||||||
|
$string['notallow'] = 'Do not use this service';
|
||||||
$string['pluginname'] = 'Memberships LTI Service';
|
$string['pluginname'] = 'Memberships LTI Service';
|
||||||
$string['servicename'] = 'Memberships';
|
$string['servicename'] = 'Memberships';
|
||||||
|
|
|
@ -43,7 +43,7 @@ class profile extends \mod_lti\local\ltiservice\resource_base {
|
||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Class constructor.
|
||||||
*
|
*
|
||||||
* @param ltiservice_profile\local\resources\profile $service Service instance
|
* @param service_base $service Service instance
|
||||||
*/
|
*/
|
||||||
public function __construct($service) {
|
public function __construct($service) {
|
||||||
|
|
||||||
|
@ -76,10 +76,9 @@ class profile extends \mod_lti\local\ltiservice\resource_base {
|
||||||
/**
|
/**
|
||||||
* Execute the request for this resource.
|
* Execute the request for this resource.
|
||||||
*
|
*
|
||||||
* @param mod_lti\local\ltiservice\response $response Response object for this request.
|
* @param \mod_lti\local\ltiservice\response $response Response object for this request.
|
||||||
*/
|
*/
|
||||||
public function execute($response) {
|
public function execute($response) {
|
||||||
|
|
||||||
global $CFG;
|
global $CFG;
|
||||||
|
|
||||||
$version = service_base::LTI_VERSION2P0;
|
$version = service_base::LTI_VERSION2P0;
|
||||||
|
@ -104,6 +103,7 @@ class profile extends \mod_lti\local\ltiservice\resource_base {
|
||||||
foreach ($services as $name => $location) {
|
foreach ($services as $name => $location) {
|
||||||
if (in_array($name, $serviceofferedarr)) {
|
if (in_array($name, $serviceofferedarr)) {
|
||||||
$classname = "\\ltiservice_{$name}\\local\\service\\{$name}";
|
$classname = "\\ltiservice_{$name}\\local\\service\\{$name}";
|
||||||
|
/** @var service_base $service */
|
||||||
$service = new $classname();
|
$service = new $classname();
|
||||||
$service->set_tool_proxy($toolproxy);
|
$service->set_tool_proxy($toolproxy);
|
||||||
$resources = $service->get_resources();
|
$resources = $service->get_resources();
|
||||||
|
@ -218,9 +218,9 @@ EOD;
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function parse_value($value) {
|
public function parse_value($value) {
|
||||||
|
if (strpos($value, '$ToolConsumerProfile.url') !== false) {
|
||||||
$value = str_replace('$ToolConsumerProfile.url', $this->get_endpoint(), $value);
|
$value = str_replace('$ToolConsumerProfile.url', $this->get_endpoint(), $value);
|
||||||
|
}
|
||||||
return $value;
|
return $value;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
|
|
||||||
namespace ltiservice_toolsettings\local\resources;
|
namespace ltiservice_toolsettings\local\resources;
|
||||||
|
|
||||||
use ltiservice_toolsettings\local\resources\systemsettings;
|
|
||||||
use ltiservice_toolsettings\local\service\toolsettings;
|
use ltiservice_toolsettings\local\service\toolsettings;
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
@ -44,7 +43,7 @@ class contextsettings extends \mod_lti\local\ltiservice\resource_base {
|
||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Class constructor.
|
||||||
*
|
*
|
||||||
* @param ltiservice_toolsettings\local\resources\contextsettings $service Service instance
|
* @param \mod_lti\local\ltiservice\service_base $service Service instance
|
||||||
*/
|
*/
|
||||||
public function __construct($service) {
|
public function __construct($service) {
|
||||||
|
|
||||||
|
@ -62,7 +61,7 @@ class contextsettings extends \mod_lti\local\ltiservice\resource_base {
|
||||||
/**
|
/**
|
||||||
* Execute the request for this resource.
|
* Execute the request for this resource.
|
||||||
*
|
*
|
||||||
* @param mod_lti\local\ltiservice\response $response Response object for this request.
|
* @param \mod_lti\local\ltiservice\response $response Response object for this request.
|
||||||
*/
|
*/
|
||||||
public function execute($response) {
|
public function execute($response) {
|
||||||
|
|
||||||
|
@ -166,16 +165,17 @@ class contextsettings extends \mod_lti\local\ltiservice\resource_base {
|
||||||
public function parse_value($value) {
|
public function parse_value($value) {
|
||||||
global $COURSE;
|
global $COURSE;
|
||||||
|
|
||||||
if ($COURSE->format == 'site') {
|
if (strpos($value, '$ToolProxyBinding.custom.url') !== false) {
|
||||||
$this->params['context_type'] = 'Group';
|
if ($COURSE->format == 'site') {
|
||||||
} else {
|
$this->params['context_type'] = 'Group';
|
||||||
$this->params['context_type'] = 'CourseSection';
|
} else {
|
||||||
|
$this->params['context_type'] = 'CourseSection';
|
||||||
|
}
|
||||||
|
$this->params['context_id'] = $COURSE->id;
|
||||||
|
$this->params['vendor_code'] = $this->get_service()->get_tool_proxy()->vendorcode;
|
||||||
|
$this->params['product_code'] = $this->get_service()->get_tool_proxy()->guid;
|
||||||
|
$value = str_replace('$ToolProxyBinding.custom.url', parent::get_endpoint(), $value);
|
||||||
}
|
}
|
||||||
$this->params['context_id'] = $COURSE->id;
|
|
||||||
$this->params['vendor_code'] = $this->get_service()->get_tool_proxy()->vendorcode;
|
|
||||||
$this->params['product_code'] = $this->get_service()->get_tool_proxy()->guid;
|
|
||||||
$value = str_replace('$ToolProxyBinding.custom.url', parent::get_endpoint(), $value);
|
|
||||||
|
|
||||||
return $value;
|
return $value;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,8 @@
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace ltiservice_toolsettings\local\resources;
|
namespace ltiservice_toolsettings\local\resources;
|
||||||
|
|
||||||
use ltiservice_toolsettings\local\resources\systemsettings;
|
|
||||||
use ltiservice_toolsettings\local\resources\contextsettings;
|
|
||||||
use ltiservice_toolsettings\local\service\toolsettings;
|
use ltiservice_toolsettings\local\service\toolsettings;
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
@ -45,7 +42,7 @@ class linksettings extends \mod_lti\local\ltiservice\resource_base {
|
||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Class constructor.
|
||||||
*
|
*
|
||||||
* @param ltiservice_toolsettings\local\resources\linksettings $service Service instance
|
* @param \mod_lti\local\ltiservice\service_base $service Service instance
|
||||||
*/
|
*/
|
||||||
public function __construct($service) {
|
public function __construct($service) {
|
||||||
|
|
||||||
|
@ -63,7 +60,7 @@ class linksettings extends \mod_lti\local\ltiservice\resource_base {
|
||||||
/**
|
/**
|
||||||
* Execute the request for this resource.
|
* Execute the request for this resource.
|
||||||
*
|
*
|
||||||
* @param mod_lti\local\ltiservice\response $response Response object for this request.
|
* @param \mod_lti\local\ltiservice\response $response Response object for this request.
|
||||||
*/
|
*/
|
||||||
public function execute($response) {
|
public function execute($response) {
|
||||||
global $DB, $COURSE;
|
global $DB, $COURSE;
|
||||||
|
@ -82,6 +79,7 @@ class linksettings extends \mod_lti\local\ltiservice\resource_base {
|
||||||
|
|
||||||
$systemsetting = null;
|
$systemsetting = null;
|
||||||
$contextsetting = null;
|
$contextsetting = null;
|
||||||
|
$lti = null;
|
||||||
if ($ok) {
|
if ($ok) {
|
||||||
$ok = !empty($linkid);
|
$ok = !empty($linkid);
|
||||||
if ($ok) {
|
if ($ok) {
|
||||||
|
@ -195,13 +193,14 @@ class linksettings extends \mod_lti\local\ltiservice\resource_base {
|
||||||
*/
|
*/
|
||||||
public function parse_value($value) {
|
public function parse_value($value) {
|
||||||
|
|
||||||
$id = optional_param('id', 0, PARAM_INT); // Course Module ID.
|
if (strpos($value, '$LtiLink.custom.url') !== false) {
|
||||||
if (!empty($id)) {
|
$id = optional_param('id', 0, PARAM_INT); // Course Module ID.
|
||||||
$cm = get_coursemodule_from_id('lti', $id, 0, false, MUST_EXIST);
|
if (!empty($id)) {
|
||||||
$this->params['link_id'] = $cm->instance;
|
$cm = get_coursemodule_from_id('lti', $id, 0, false, MUST_EXIST);
|
||||||
|
$this->params['link_id'] = $cm->instance;
|
||||||
|
}
|
||||||
|
$value = str_replace('$LtiLink.custom.url', parent::get_endpoint(), $value);
|
||||||
}
|
}
|
||||||
$value = str_replace('$LtiLink.custom.url', parent::get_endpoint(), $value);
|
|
||||||
|
|
||||||
return $value;
|
return $value;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
namespace ltiservice_toolsettings\local\resources;
|
namespace ltiservice_toolsettings\local\resources;
|
||||||
|
|
||||||
use ltiservice_toolsettings\local\service\toolsettings;
|
use ltiservice_toolsettings\local\service\toolsettings;
|
||||||
|
use mod_lti\local\ltiservice\resource_base;
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
@ -38,12 +39,12 @@ defined('MOODLE_INTERNAL') || die();
|
||||||
* @copyright 2014 Vital Source Technologies http://vitalsource.com
|
* @copyright 2014 Vital Source Technologies http://vitalsource.com
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
class systemsettings extends \mod_lti\local\ltiservice\resource_base {
|
class systemsettings extends resource_base {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Class constructor.
|
||||||
*
|
*
|
||||||
* @param ltiservice_toolsettings\local\service\toolsettings $service Service instance
|
* @param \mod_lti\local\ltiservice\service_base $service Service instance
|
||||||
*/
|
*/
|
||||||
public function __construct($service) {
|
public function __construct($service) {
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ class systemsettings extends \mod_lti\local\ltiservice\resource_base {
|
||||||
/**
|
/**
|
||||||
* Execute the request for this resource.
|
* Execute the request for this resource.
|
||||||
*
|
*
|
||||||
* @param mod_lti\local\ltiservice\response $response Response object for this request.
|
* @param \mod_lti\local\ltiservice\response $response Response object for this request.
|
||||||
*/
|
*/
|
||||||
public function execute($response) {
|
public function execute($response) {
|
||||||
|
|
||||||
|
@ -143,9 +144,9 @@ class systemsettings extends \mod_lti\local\ltiservice\resource_base {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function parse_value($value) {
|
public function parse_value($value) {
|
||||||
|
if (strpos($value, '$ToolProxy.custom.url') !== false) {
|
||||||
$value = str_replace('$ToolProxy.custom.url', parent::get_endpoint(), $value);
|
$value = str_replace('$ToolProxy.custom.url', parent::get_endpoint(), $value);
|
||||||
|
}
|
||||||
return $value;
|
return $value;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue