Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * This file contains a class definition for the LineItem resource
  19   *
  20   * @package    ltiservice_gradebookservices
  21   * @copyright  2017 Cengage Learning http://www.cengage.com
  22   * @author     Dirk Singels, Diego del Blanco, Claude Vervoort
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  namespace ltiservice_gradebookservices\local\resources;
  27  
  28  use ltiservice_gradebookservices\local\service\gradebookservices;
  29  use mod_lti\local\ltiservice\resource_base;
  30  
  31  defined('MOODLE_INTERNAL') || die();
  32  
  33  /**
  34   * A resource implementing LineItem.
  35   *
  36   * @package    ltiservice_gradebookservices
  37   * @copyright  2017 Cengage Learning http://www.cengage.com
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class lineitem extends resource_base {
  41  
  42      /**
  43       * Class constructor.
  44       *
  45       * @param gradebookservices $service Service instance
  46       */
  47      public function __construct($service) {
  48  
  49          parent::__construct($service);
  50          $this->id = 'LineItem.item';
  51          $this->template = '/{context_id}/lineitems/{item_id}/lineitem';
  52          $this->variables[] = 'LineItem.url';
  53          $this->formats[] = 'application/vnd.ims.lis.v2.lineitem+json';
  54          $this->methods[] = self::HTTP_GET;
  55          $this->methods[] = self::HTTP_PUT;
  56          $this->methods[] = self::HTTP_DELETE;
  57  
  58      }
  59  
  60      /**
  61       * Execute the request for this resource.
  62       *
  63       * @param \mod_lti\local\ltiservice\response $response  Response object for this request.
  64       */
  65      public function execute($response) {
  66          global $CFG, $DB;
  67  
  68          $params = $this->parse_template();
  69          $contextid = $params['context_id'];
  70          $itemid = $params['item_id'];
  71          $isget = $response->get_request_method() === self::HTTP_GET;
  72          // We will receive typeid when working with LTI 1.x, if not then we are in LTI 2.
  73          $typeid = optional_param('type_id', null, PARAM_INT);
  74  
  75          $scopes = array(gradebookservices::SCOPE_GRADEBOOKSERVICES_LINEITEM);
  76          if ($response->get_request_method() === self::HTTP_GET) {
  77              $scopes[] = gradebookservices::SCOPE_GRADEBOOKSERVICES_LINEITEM_READ;
  78          }
  79  
  80          try {
  81              if (!$this->check_tool($typeid, $response->get_request_data(), $scopes)) {
  82                  throw new \Exception(null, 401);
  83              }
  84              $typeid = $this->get_service()->get_type()->id;
  85              if (!($course = $DB->get_record('course', array('id' => $contextid), 'id', IGNORE_MISSING))) {
  86                  throw new \Exception("Not Found: Course {$contextid} doesn't exist", 404);
  87              }
  88              if (!$this->get_service()->is_allowed_in_context($typeid, $course->id)) {
  89                  throw new \Exception('Not allowed in context', 403);
  90              }
  91              if (!$DB->record_exists('grade_items', array('id' => $itemid))) {
  92                  throw new \Exception("Not Found: Grade item {$itemid} doesn't exist", 404);
  93              }
  94              $item = $this->get_service()->get_lineitem($contextid, $itemid, $typeid);
  95              if ($item === false) {
  96                  throw new \Exception('Line item does not exist', 404);
  97              }
  98              require_once($CFG->libdir.'/gradelib.php');
  99              switch ($response->get_request_method()) {
 100                  case self::HTTP_GET:
 101                      $this->get_request($response, $item, $typeid);
 102                      break;
 103                  case self::HTTP_PUT:
 104                      $json = $this->process_put_request($response->get_request_data(), $item, $typeid);
 105                      $response->set_body($json);
 106                      $response->set_code(200);
 107                      break;
 108                  case self::HTTP_DELETE:
 109                      $this->process_delete_request($item);
 110                      $response->set_code(204);
 111                      break;
 112              }
 113          } catch (\Exception $e) {
 114              $response->set_code($e->getCode());
 115              $response->set_reason($e->getMessage());
 116          }
 117      }
 118  
 119      /**
 120       * Process a GET request.
 121       *
 122       * @param \mod_lti\local\ltiservice\response $response Response object for this request.
 123       * @param object $item Grade item instance.
 124       * @param string $typeid Tool Type Id
 125       */
 126      private function get_request($response, $item, $typeid) {
 127  
 128          $response->set_content_type($this->formats[0]);
 129          $lineitem = gradebookservices::item_for_json($item, substr(parent::get_endpoint(),
 130              0, strrpos(parent::get_endpoint(), "/", -10)), $typeid);
 131          $response->set_body(json_encode($lineitem));
 132  
 133      }
 134  
 135      /**
 136       * Process a PUT request.
 137       *
 138       * @param string $body PUT body
 139       * @param \ltiservice_gradebookservices\local\resources\lineitem $olditem Grade item instance
 140       * @param string $typeid Tool Type Id
 141       *
 142       * @return string
 143       * @throws \Exception
 144       */
 145      private function process_put_request($body, $olditem, $typeid) {
 146          global $DB;
 147          $json = json_decode($body);
 148          if (empty($json) ||
 149                  !isset($json->scoreMaximum) ||
 150                  !isset($json->label)) {
 151              throw new \Exception(null, 400);
 152          }
 153          $item = \grade_item::fetch(array('id' => $olditem->id, 'courseid' => $olditem->courseid));
 154          $gbs = gradebookservices::find_ltiservice_gradebookservice_for_lineitem($olditem->id);
 155          $updategradeitem = false;
 156          $rescalegrades = false;
 157          $oldgrademax = grade_floatval($item->grademax);
 158          $upgradegradebookservices = false;
 159          if ($item->itemname !== $json->label) {
 160              $updategradeitem = true;
 161          }
 162          $item->itemname = $json->label;
 163          if (!is_numeric($json->scoreMaximum)) {
 164              throw new \Exception(null, 400);
 165          } else {
 166              if (grade_floats_different($oldgrademax,
 167                      grade_floatval($json->scoreMaximum))) {
 168                  $updategradeitem = true;
 169                  $rescalegrades = true;
 170              }
 171              $item->grademax = grade_floatval($json->scoreMaximum);
 172          }
 173          if ($gbs) {
 174              $resourceid = (isset($json->resourceId)) ? $json->resourceId : '';
 175              $tag = (isset($json->tag)) ? $json->tag : '';
 176              if ($gbs->tag !== $tag || $gbs->resourceid !== $resourceid) {
 177                  $upgradegradebookservices = true;
 178              }
 179              $gbs->tag = $tag;
 180              $gbs->resourceid = $resourceid;
 181          }
 182          $ltilinkid = null;
 183          if (isset($json->resourceLinkId)) {
 184              if (is_numeric($json->resourceLinkId)) {
 185                  $ltilinkid = $json->resourceLinkId;
 186                  if ($gbs) {
 187                      if (intval($gbs->ltilinkid) !== intval($json->resourceLinkId)) {
 188                          $gbs->ltilinkid = $json->resourceLinkId;
 189                          $upgradegradebookservices = true;
 190                      }
 191                  } else {
 192                      if (intval($item->iteminstance) !== intval($json->resourceLinkId)) {
 193                          $item->iteminstance = intval($json->resourceLinkId);
 194                          $updategradeitem = true;
 195                      }
 196                  }
 197              } else {
 198                  throw new \Exception(null, 400);
 199              }
 200          } else if (isset($json->ltiLinkId)) {
 201              if (is_numeric($json->ltiLinkId)) {
 202                  $ltilinkid = $json->ltiLinkId;
 203                  if ($gbs) {
 204                      if (intval($gbs->ltilinkid) !== intval($json->ltiLinkId)) {
 205                          $gbs->ltilinkid = $json->ltiLinkId;
 206                          $upgradegradebookservices = true;
 207                      }
 208                  } else {
 209                      if (intval($item->iteminstance) !== intval($json->ltiLinkId)) {
 210                          $item->iteminstance = intval($json->ltiLinkId);
 211                          $updategradeitem = true;
 212                      }
 213                  }
 214              } else {
 215                  throw new \Exception(null, 400);
 216              }
 217          }
 218          if ($ltilinkid != null) {
 219              if (is_null($typeid)) {
 220                  if (!gradebookservices::check_lti_id($ltilinkid, $item->courseid,
 221                          $this->get_service()->get_tool_proxy()->id)) {
 222                              throw new \Exception(null, 403);
 223                  }
 224              } else {
 225                  if (!gradebookservices::check_lti_1x_id($ltilinkid, $item->courseid,
 226                          $typeid)) {
 227                              throw new \Exception(null, 403);
 228                  }
 229              }
 230          }
 231          if ($updategradeitem) {
 232              if (!$item->update('mod/ltiservice_gradebookservices')) {
 233                  throw new \Exception(null, 500);
 234              }
 235              if ($rescalegrades) {
 236                  $item->rescale_grades_keep_percentage(0, $oldgrademax, 0, $item->grademax);
 237              }
 238          }
 239  
 240          $lineitem = new lineitem($this->get_service());
 241          $endpoint = $lineitem->get_endpoint();
 242  
 243          if ($upgradegradebookservices) {
 244              if (is_null($typeid)) {
 245                  $toolproxyid = $this->get_service()->get_tool_proxy()->id;
 246                  $baseurl = null;
 247              } else {
 248                  $toolproxyid = null;
 249                  $baseurl = lti_get_type_type_config($typeid)->lti_toolurl;
 250              }
 251              $DB->update_record('ltiservice_gradebookservices', (object)array(
 252                      'id' => $gbs->id,
 253                      'gradeitemid' => $gbs->gradeitemid,
 254                      'courseid' => $gbs->courseid,
 255                      'toolproxyid' => $toolproxyid,
 256                      'typeid' => $typeid,
 257                      'baseurl' => $baseurl,
 258                      'ltilinkid' => $ltilinkid,
 259                      'resourceid' => $resourceid,
 260                      'tag' => $gbs->tag
 261              ));
 262          }
 263  
 264          if (is_null($typeid)) {
 265              $id = "{$endpoint}";
 266              $json->id = $id;
 267          } else {
 268              $id = "{$endpoint}?type_id={$typeid}";
 269              $json->id = $id;
 270          }
 271          return json_encode($json, JSON_UNESCAPED_SLASHES);
 272  
 273      }
 274  
 275      /**
 276       * Process a DELETE request.
 277       *
 278       * @param \ltiservice_gradebookservices\local\resources\lineitem $item Grade item instance
 279       * @throws \Exception
 280       */
 281      private function process_delete_request($item) {
 282          global $DB;
 283  
 284          $gradeitem = \grade_item::fetch(array('id' => $item->id));
 285          if (($gbs = gradebookservices::find_ltiservice_gradebookservice_for_lineitem($item->id)) == false) {
 286              throw new \Exception(null, 403);
 287          }
 288          if (!$gradeitem->delete('mod/ltiservice_gradebookservices')) {
 289              throw new \Exception(null, 500);
 290          } else {
 291              $sqlparams = array();
 292              $sqlparams['id'] = $gbs->id;
 293              if (!$DB->delete_records('ltiservice_gradebookservices', $sqlparams)) {
 294                  throw new \Exception(null, 500);
 295              }
 296          }
 297      }
 298  
 299      /**
 300       * Parse a value for custom parameter substitution variables.
 301       *
 302       * @param string $value String to be parsed
 303       *
 304       * @return string
 305       */
 306      public function parse_value($value) {
 307          global $COURSE, $CFG;
 308          if (strpos($value, '$LineItem.url') !== false) {
 309              $resolved = '';
 310              require_once($CFG->libdir . '/gradelib.php');
 311  
 312              $this->params['context_id'] = $COURSE->id;
 313              if ($tool = $this->get_service()->get_type()) {
 314                  $this->params['tool_code'] = $tool->id;
 315              }
 316              $id = optional_param('id', 0, PARAM_INT); // Course Module ID.
 317              if (empty($id)) {
 318                  $id = optional_param('lti_message_hint', 0, PARAM_INT);
 319              }
 320              if (!empty($id)) {
 321                  $cm = get_coursemodule_from_id('lti', $id, 0, false, MUST_EXIST);
 322                  $id = $cm->instance;
 323                  $item = grade_get_grades($COURSE->id, 'mod', 'lti', $id);
 324                  if ($item && $item->items) {
 325                      $this->params['item_id'] = $item->items[0]->id;
 326                      $resolved = parent::get_endpoint();
 327                      $resolved .= "?type_id={$tool->id}";
 328                  }
 329              }
 330              $value = str_replace('$LineItem.url', $resolved, $value);
 331          }
 332          return $value;
 333      }
 334  }