See Release Notes
Long Term Support Release
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 container 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 container. 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 lineitems extends resource_base { 41 42 /** 43 * Class constructor. 44 * 45 * @param \ltiservice_gradebookservices\local\service\gradebookservices $service Service instance 46 */ 47 public function __construct($service) { 48 49 parent::__construct($service); 50 $this->id = 'LineItem.collection'; 51 $this->template = '/{context_id}/lineitems'; 52 $this->variables[] = 'LineItems.url'; 53 $this->formats[] = 'application/vnd.ims.lis.v2.lineitemcontainer+json'; 54 $this->formats[] = 'application/vnd.ims.lis.v2.lineitem+json'; 55 $this->methods[] = self::HTTP_GET; 56 $this->methods[] = self::HTTP_POST; 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 $DB; 67 68 $params = $this->parse_template(); 69 $contextid = $params['context_id']; 70 $isget = $response->get_request_method() === self::HTTP_GET; 71 if ($isget) { 72 $contenttype = $response->get_accept(); 73 } else { 74 $contenttype = $response->get_content_type(); 75 } 76 $container = empty($contenttype) || ($contenttype === $this->formats[0]); 77 // We will receive typeid when working with LTI 1.x, if not then we are in LTI 2. 78 $typeid = optional_param('type_id', null, PARAM_INT); 79 80 $scopes = array(gradebookservices::SCOPE_GRADEBOOKSERVICES_LINEITEM); 81 if ($response->get_request_method() === self::HTTP_GET) { 82 $scopes[] = gradebookservices::SCOPE_GRADEBOOKSERVICES_LINEITEM_READ; 83 } 84 85 try { 86 if (!$this->check_tool($typeid, $response->get_request_data(), $scopes)) { 87 throw new \Exception(null, 401); 88 } 89 $typeid = $this->get_service()->get_type()->id; 90 if (empty($contextid) || !($container ^ ($response->get_request_method() === self::HTTP_POST)) || 91 (!empty($contenttype) && !in_array($contenttype, $this->formats))) { 92 throw new \Exception('No context or unsupported content type', 400); 93 } 94 if (!($course = $DB->get_record('course', array('id' => $contextid), 'id', IGNORE_MISSING))) { 95 throw new \Exception("Not Found: Course {$contextid} doesn't exist", 404); 96 } 97 if (!$this->get_service()->is_allowed_in_context($typeid, $course->id)) { 98 throw new \Exception('Not allowed in context', 403); 99 } 100 if ($response->get_request_method() !== self::HTTP_POST) { 101 $resourceid = optional_param('resource_id', null, PARAM_TEXT); 102 $ltilinkid = optional_param('resource_link_id', null, PARAM_TEXT); 103 if (is_null($ltilinkid)) { 104 $ltilinkid = optional_param('lti_link_id', null, PARAM_TEXT); 105 } 106 $tag = optional_param('tag', null, PARAM_TEXT); 107 $limitnum = optional_param('limit', 0, PARAM_INT); 108 $limitfrom = optional_param('from', 0, PARAM_INT); 109 $itemsandcount = $this->get_service()->get_lineitems($contextid, $resourceid, $ltilinkid, $tag, $limitfrom, 110 $limitnum, $typeid); 111 $items = $itemsandcount[1]; 112 $totalcount = $itemsandcount[0]; 113 $json = $this->get_json_for_get_request($items, $resourceid, $ltilinkid, $tag, $limitfrom, 114 $limitnum, $totalcount, $typeid, $response); 115 $response->set_content_type($this->formats[0]); 116 } else { 117 $json = $this->get_json_for_post_request($response->get_request_data(), $contextid, $typeid); 118 $response->set_code(201); 119 $response->set_content_type($this->formats[1]); 120 } 121 $response->set_body($json); 122 123 } catch (\Exception $e) { 124 $response->set_code($e->getCode()); 125 $response->set_reason($e->getMessage()); 126 } 127 128 } 129 130 /** 131 * Generate the JSON for a GET request. 132 * 133 * @param array $items Array of lineitems 134 * @param string $resourceid Resource identifier used for filtering, may be null 135 * @param string $ltilinkid Resource Link identifier used for filtering, may be null 136 * @param string $tag Tag identifier used for filtering, may be null 137 * @param int $limitfrom Offset of the first line item to return 138 * @param int $limitnum Maximum number of line items to return, ignored if zero or less 139 * @param int $totalcount Number of total lineitems before filtering for paging 140 * @param int $typeid Maximum number of line items to return, ignored if zero or less 141 * @param \mod_lti\local\ltiservice\response $response 142 143 * @return string 144 */ 145 private function get_json_for_get_request($items, $resourceid, $ltilinkid, 146 $tag, $limitfrom, $limitnum, $totalcount, $typeid, $response) { 147 148 $firstpage = null; 149 $nextpage = null; 150 $prevpage = null; 151 $lastpage = null; 152 if (isset($limitnum) && $limitnum > 0) { 153 if ($limitfrom >= $totalcount || $limitfrom < 0) { 154 $outofrange = true; 155 } else { 156 $outofrange = false; 157 } 158 $limitprev = $limitfrom - $limitnum >= 0 ? $limitfrom - $limitnum : 0; 159 $limitcurrent = $limitfrom; 160 $limitlast = $totalcount - $limitnum + 1 >= 0 ? $totalcount - $limitnum + 1 : 0; 161 $limitfrom += $limitnum; 162 163 $baseurl = new \moodle_url($this->get_endpoint()); 164 if (isset($resourceid)) { 165 $baseurl->param('resource_id', $resourceid); 166 } 167 if (isset($ltilinkid)) { 168 $baseurl->param('resource_link_id', $ltilinkid); 169 } 170 if (isset($tag)) { 171 $baseurl->param('tag', $tag); 172 } 173 174 if (is_null($typeid)) { 175 $baseurl->param('limit', $limitnum); 176 if (($limitfrom <= $totalcount - 1) && (!$outofrange)) { 177 $nextpage = new \moodle_url($baseurl, ['from' => $limitfrom]); 178 } 179 $firstpage = new \moodle_url($baseurl, ['from' => 0]); 180 $canonicalpage = new \moodle_url($baseurl, ['from' => $limitcurrent]); 181 $lastpage = new \moodle_url($baseurl, ['from' > $limitlast]); 182 if (($limitcurrent > 0) && (!$outofrange)) { 183 $prevpage = new \moodle_url($baseurl, ['from' => $limitprev]); 184 } 185 } else { 186 $baseurl->params(['type_id' => $typeid, 'limit' => $limitnum]); 187 if (($limitfrom <= $totalcount - 1) && (!$outofrange)) { 188 $nextpage = new \moodle_url($baseurl, ['from' => $limitfrom]); 189 } 190 $firstpage = new \moodle_url($baseurl, ['from' => 0]); 191 $canonicalpage = new \moodle_url($baseurl, ['from' => $limitcurrent]); 192 $lastpage = new \moodle_url($baseurl, ['from' => $limitlast]); 193 if (($limitcurrent > 0) && (!$outofrange)) { 194 $prevpage = new \moodle_url($baseurl, ['from' => $limitprev]); 195 } 196 } 197 } 198 199 $jsonitems = []; 200 $endpoint = parent::get_endpoint(); 201 foreach ($items as $item) { 202 array_push($jsonitems, gradebookservices::item_for_json($item, $endpoint, $typeid)); 203 } 204 205 if (isset($canonicalpage) && ($canonicalpage)) { 206 $links = 'Link: <' . $firstpage->out() . '>; rel=“first”'; 207 if (!is_null($prevpage)) { 208 $links .= ', <' . $prevpage->out() . '>; rel=“prev”'; 209 } 210 $links .= ', <' . $canonicalpage->out(). '>; rel=“canonical”'; 211 if (!is_null($nextpage)) { 212 $links .= ', <' . $nextpage->out() . '>; rel=“next”'; 213 } 214 $links .= ', <' . $lastpage->out() . '>; rel=“last”'; 215 $response->add_additional_header($links); 216 } 217 return json_encode($jsonitems); 218 } 219 220 /** 221 * Generate the JSON for a POST request. 222 * 223 * @param string $body POST body 224 * @param string $contextid Course ID 225 * @param string $typeid 226 * 227 * @return string 228 * @throws \Exception 229 */ 230 private function get_json_for_post_request($body, $contextid, $typeid) { 231 global $CFG, $DB; 232 233 $json = json_decode($body); 234 if (empty($json) || 235 !isset($json->scoreMaximum) || 236 !isset($json->label)) { 237 throw new \Exception('No label or Score Maximum', 400); 238 } 239 if (is_numeric($json->scoreMaximum)) { 240 $max = $json->scoreMaximum; 241 } else { 242 throw new \Exception(null, 400); 243 } 244 require_once($CFG->libdir.'/gradelib.php'); 245 $resourceid = (isset($json->resourceId)) ? $json->resourceId : ''; 246 $ltilinkid = (isset($json->resourceLinkId)) ? $json->resourceLinkId : null; 247 if ($ltilinkid == null) { 248 $ltilinkid = (isset($json->ltiLinkId)) ? $json->ltiLinkId : null; 249 } 250 if ($ltilinkid != null) { 251 if (is_null($typeid)) { 252 if (!gradebookservices::check_lti_id($ltilinkid, $contextid, $this->get_service()->get_tool_proxy()->id)) { 253 throw new \Exception(null, 403); 254 } 255 } else { 256 if (!gradebookservices::check_lti_1x_id($ltilinkid, $contextid, $typeid)) { 257 throw new \Exception(null, 403); 258 } 259 } 260 } 261 $tag = (isset($json->tag)) ? $json->tag : ''; 262 if (is_null($typeid)) { 263 $toolproxyid = $this->get_service()->get_tool_proxy()->id; 264 $baseurl = null; 265 } else { 266 $toolproxyid = null; 267 $baseurl = lti_get_type_type_config($typeid)->lti_toolurl; 268 } 269 $gradebookservices = new gradebookservices(); 270 $id = $gradebookservices->add_standalone_lineitem($contextid, $json->label, 271 $max, $baseurl, $ltilinkid, $resourceid, $tag, $typeid, $toolproxyid); 272 if (is_null($typeid)) { 273 $json->id = parent::get_endpoint() . "/{$id}/lineitem"; 274 } else { 275 $json->id = parent::get_endpoint() . "/{$id}/lineitem?type_id={$typeid}"; 276 } 277 return json_encode($json, JSON_UNESCAPED_SLASHES); 278 } 279 280 /** 281 * Parse a value for custom parameter substitution variables. 282 * 283 * @param string $value String to be parsed 284 * 285 * @return string 286 */ 287 public function parse_value($value) { 288 global $COURSE; 289 290 if (strpos($value, '$LineItems.url') !== false) { 291 $this->params['context_id'] = $COURSE->id; 292 $query = ''; 293 if (($tool = $this->get_service()->get_type())) { 294 $query = "?type_id={$tool->id}"; 295 } 296 $value = str_replace('$LineItems.url', parent::get_endpoint() . $query, $value); 297 } 298 299 return $value; 300 301 } 302 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body