<?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'];
$isget = $response->get_request_method() === self::HTTP_GET;
// 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_INT);
$scopes = array(gradebookservices::SCOPE_GRADEBOOKSERVICES_LINEITEM);
if ($response->get_request_method() === self::HTTP_GET) {
$scopes[] = gradebookservices::SCOPE_GRADEBOOKSERVICES_LINEITEM_READ;
}
try {
if (!$this->check_tool($typeid, $response->get_request_data(), $scopes)) {
throw new \Exception(null, 401);
}
$typeid = $this->get_service()->get_type()->id;
if (!($course = $DB->get_record('course', array('id' => $contextid), 'id', IGNORE_MISSING))) {
throw new \Exception("Not Found: Course {$contextid} doesn't exist", 404);
}
if (!$this->get_service()->is_allowed_in_context($typeid, $course->id)) {
throw new \Exception('Not allowed in context', 403);
}
if (!$DB->record_exists('grade_items', array('id' => $itemid))) {
throw new \Exception("Not Found: Grade item {$itemid} doesn't exist", 404);
}
$item = $this->get_service()->get_lineitem($contextid, $itemid, $typeid);
if ($item === false) {
throw new \Exception('Line item does not exist', 404);
}
require_once($CFG->libdir.'/gradelib.php');
switch ($response->get_request_method()) {
case self::HTTP_GET:
$this->get_request($response, $item, $typeid);
break;
case self::HTTP_PUT:
$json = $this->process_put_request($response->get_request_data(), $item, $typeid);
$response->set_body($json);
$response->set_code(200);
break;
case self::HTTP_DELETE:
$this->process_delete_request($item);
$response->set_code(204);
break;
}
} catch (\Exception $e) {
$response->set_code($e->getCode());
$response->set_reason($e->getMessage());
}
}
/**
* 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);
}
if ($gbs) {
$resourceid = (isset($json->resourceId)) ? $json->resourceId : '';
$tag = (isset($json->tag)) ? $json->tag : '';
if ($gbs->tag !== $tag || $gbs->resourceid !== $resourceid) {
$upgradegradebookservices = true;
}
$gbs->tag = $tag;
$gbs->resourceid = $resourceid;
> $incomingurl = null;
}
> $incomingparams = null;
$ltilinkid = null;
> if (isset($json->submissionReview)) {
if (isset($json->resourceLinkId)) {
> $incomingurl = $json->submissionReview->url ?? 'DEFAULT';
if (is_numeric($json->resourceLinkId)) {
> if (isset($json->submissionReview->custom)) {
$ltilinkid = $json->resourceLinkId;
> $incomingparams = params_to_string($json->submissionReview->custom);
if ($gbs) {
> }
if (intval($gbs->ltilinkid) !== intval($json->resourceLinkId)) {
> }
$gbs->ltilinkid = $json->resourceLinkId;
> if ($gbs->subreviewurl ?? null !== $incomingurl || $gbs->subreviewparams ?? null !== $incomingparams) {
$upgradegradebookservices = true;
> $upgradegradebookservices = true;
}
> $gbs->subreviewurl = $incomingurl;
} else {
> $gbs->subreviewparams = $incomingparams;
if (intval($item->iteminstance) !== intval($json->resourceLinkId)) {
> }
$item->iteminstance = intval($json->resourceLinkId);
$updategradeitem = true;
}
}
} else {
throw new \Exception(null, 400);
}
} else 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,
'resourceid' => $resourceid,
< 'tag' => $gbs->tag
> 'tag' => $gbs->tag,
> 'subreviewurl' => $gbs->subreviewurl,
> 'subreviewparams' => $gbs->subreviewparams
));
}
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);
}
}
}
/**
* 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;
if ($tool = $this->get_service()->get_type()) {
$this->params['tool_code'] = $tool->id;
}
$id = optional_param('id', 0, PARAM_INT); // Course Module ID.
if (empty($id)) {
< $id = optional_param('lti_message_hint', 0, PARAM_INT);
> $hint = optional_param('lti_message_hint', "", PARAM_TEXT);
> if ($hint) {
> $hintdec = json_decode($hint);
> if (isset($hintdec->cmid)) {
> $id = $hintdec->cmid;
> }
> }
}
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();
$resolved .= "?type_id={$tool->id}";
}
}
$value = str_replace('$LineItem.url', $resolved, $value);
}
return $value;
}
}