PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/mod/lti/service/gradebookservices/classes/local/resources/results.php

https://gitlab.com/unofficial-mirrors/moodle
PHP | 304 lines | 212 code | 21 blank | 71 comment | 60 complexity | bba5511538b91e301ea4d7b7f1f68fa9 MD5 | raw file
  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. * This file contains a class definition for the LISResults container resource
  18. *
  19. * @package ltiservice_gradebookservices
  20. * @copyright 2017 Cengage Learning http://www.cengage.com
  21. * @author Dirk Singels, Diego del Blanco, Claude Vervoort
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23. */
  24. namespace ltiservice_gradebookservices\local\resources;
  25. use ltiservice_gradebookservices\local\service\gradebookservices;
  26. use mod_lti\local\ltiservice\resource_base;
  27. defined('MOODLE_INTERNAL') || die();
  28. /**
  29. * A resource implementing LISResults container.
  30. *
  31. * @package ltiservice_gradebookservices
  32. * @copyright 2017 Cengage Learning http://www.cengage.com
  33. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34. */
  35. class results extends resource_base {
  36. /**
  37. * Class constructor.
  38. *
  39. * @param \ltiservice_gradebookservices\local\service\gradebookservices $service Service instance
  40. */
  41. public function __construct($service) {
  42. parent::__construct($service);
  43. $this->id = 'Result.collection';
  44. $this->template = '/{context_id}/lineitems/{item_id}/lineitem/results';
  45. $this->variables[] = 'Results.url';
  46. $this->formats[] = 'application/vnd.ims.lis.v2.resultcontainer+json';
  47. $this->methods[] = 'GET';
  48. }
  49. /**
  50. * Execute the request for this resource.
  51. *
  52. * @param \mod_lti\local\ltiservice\response $response Response object for this request.
  53. */
  54. public function execute($response) {
  55. global $CFG, $DB;
  56. $params = $this->parse_template();
  57. $contextid = $params['context_id'];
  58. $itemid = $params['item_id'];
  59. $isget = $response->get_request_method() === 'GET';
  60. if ($isget) {
  61. $contenttype = $response->get_accept();
  62. } else {
  63. $contenttype = $response->get_content_type();
  64. }
  65. // We will receive typeid when working with LTI 1.x, if not the we are in LTI 2.
  66. $typeid = optional_param('type_id', null, PARAM_ALPHANUM);
  67. if (is_null($typeid)) {
  68. if (!$this->check_tool_proxy(null, $response->get_request_data())) {
  69. $response->set_code(403);
  70. $response->set_reason("Invalid tool proxy specified.");
  71. return;
  72. }
  73. } else {
  74. if (!$this->check_type($typeid, $contextid, 'Result.collection:get', $response->get_request_data())) {
  75. $response->set_code(403);
  76. $response->set_reason("This resource does not support GET requests.");
  77. return;
  78. }
  79. }
  80. if (empty($contextid) || (!empty($contenttype) && !in_array($contenttype, $this->formats))) {
  81. $response->set_code(400);
  82. $response->set_reason("Invalid request made.");
  83. return;
  84. }
  85. if (!$DB->record_exists('course', array('id' => $contextid))) {
  86. $response->set_code(404);
  87. $response->set_reason("Not Found: Course $contextid doesn't exist.");
  88. return;
  89. }
  90. if (!$DB->record_exists('grade_items', array('id' => $itemid))) {
  91. $response->set_code(404);
  92. $response->set_reason("Not Found: Grade item $itemid doesn't exist.");
  93. return;
  94. }
  95. $item = $this->get_service()->get_lineitem($contextid, $itemid, $typeid);
  96. if ($item === false) {
  97. $response->set_code(403);
  98. $response->set_reason("Line item does not exist.");
  99. return;
  100. }
  101. $gbs = gradebookservices::find_ltiservice_gradebookservice_for_lineitem($itemid);
  102. $ltilinkid = null;
  103. if (isset($item->iteminstance)) {
  104. $ltilinkid = $item->iteminstance;
  105. } else if ($gbs && isset($gbs->ltilinkid)) {
  106. $ltilinkid = $gbs->ltilinkid;
  107. }
  108. if ($ltilinkid != null) {
  109. if (is_null($typeid)) {
  110. if (isset($item->iteminstance) && (!gradebookservices::check_lti_id($ltilinkid, $item->courseid,
  111. $this->get_service()->get_tool_proxy()->id))) {
  112. $response->set_code(403);
  113. $response->set_reason("Invalid LTI id supplied.");
  114. return;
  115. }
  116. } else {
  117. if (isset($item->iteminstance) && (!gradebookservices::check_lti_1x_id($ltilinkid, $item->courseid,
  118. $typeid))) {
  119. $response->set_code(403);
  120. $response->set_reason("Invalid LTI id supplied.");
  121. return;
  122. }
  123. }
  124. }
  125. require_once($CFG->libdir.'/gradelib.php');
  126. switch ($response->get_request_method()) {
  127. case 'GET':
  128. $useridfilter = optional_param('user_id', 0, PARAM_INT);
  129. $limitnum = optional_param('limit', 0, PARAM_INT);
  130. $limitfrom = optional_param('from', 0, PARAM_INT);
  131. $typeid = optional_param('type_id', null, PARAM_TEXT);
  132. $json = $this->get_json_for_get_request($item->id, $limitfrom, $limitnum,
  133. $useridfilter, $typeid, $response);
  134. $response->set_content_type($this->formats[0]);
  135. $response->set_body($json);
  136. break;
  137. default: // Should not be possible.
  138. $response->set_code(405);
  139. $response->set_reason("Invalid request method specified.");
  140. return;
  141. }
  142. $response->set_body($json);
  143. }
  144. /**
  145. * Generate the JSON for a GET request.
  146. *
  147. * @param int $itemid Grade item instance ID
  148. * @param int $limitfrom Offset for the first result to include in this paged set
  149. * @param int $limitnum Maximum number of results to include in the response, ignored if zero
  150. * @param int $useridfilter The user id to filter the results.
  151. * @param int $typeid Lti tool typeid (or null)
  152. * @param \mod_lti\local\ltiservice\response $response The response element needed to add a header.
  153. *
  154. * @return string
  155. */
  156. private function get_json_for_get_request($itemid, $limitfrom, $limitnum, $useridfilter, $typeid, $response) {
  157. if ($useridfilter > 0) {
  158. $grades = \grade_grade::fetch_all(array('itemid' => $itemid, 'userid' => $useridfilter));
  159. } else {
  160. $grades = \grade_grade::fetch_all(array('itemid' => $itemid));
  161. }
  162. $firstpage = null;
  163. $nextpage = null;
  164. $prevpage = null;
  165. $lastpage = null;
  166. if ($grades && isset($limitnum) && $limitnum > 0) {
  167. // Since we only display grades that have been modified, we need to filter first in order to support
  168. // paging.
  169. $resultgrades = array_filter($grades, function ($grade) {
  170. return !empty($grade->timemodified);
  171. });
  172. // We save the total count to calculate the last page.
  173. $totalcount = count($resultgrades);
  174. // We slice to the requested item offset to insure proper item is always first, and we always return
  175. // first pageset of any remaining items.
  176. $grades = array_slice($resultgrades, $limitfrom);
  177. if (count($grades) > 0) {
  178. $pagedgrades = array_chunk($grades, $limitnum);
  179. $pageset = 0;
  180. $grades = $pagedgrades[$pageset];
  181. }
  182. if ($limitfrom >= $totalcount || $limitfrom < 0) {
  183. $outofrange = true;
  184. } else {
  185. $outofrange = false;
  186. }
  187. $limitprev = $limitfrom - $limitnum >= 0 ? $limitfrom - $limitnum : 0;
  188. $limitcurrent = $limitfrom;
  189. $limitlast = $totalcount - $limitnum + 1 >= 0 ? $totalcount - $limitnum + 1 : 0;
  190. $limitfrom += $limitnum;
  191. $baseurl = new \moodle_url($this->get_endpoint());
  192. if (is_null($typeid)) {
  193. $baseurl->param('limit', $limitnum);
  194. if (($limitfrom <= $totalcount - 1) && (!$outofrange)) {
  195. $nextpage = new \moodle_url($baseurl, ['from' => $limitfrom]);
  196. }
  197. $firstpage = new \moodle_url($baseurl, ['from' => 0]);
  198. $canonicalpage = new \moodle_url($baseurl, ['from' => $limitcurrent]);
  199. $lastpage = new \moodle_url($baseurl, ['from' => $limitlast]);
  200. if (($limitcurrent > 0) && (!$outofrange)) {
  201. $prevpage = new \moodle_url($baseurl, ['from' => $limitprev]);
  202. }
  203. } else {
  204. $baseurl->params(['type_id' => $typeid, 'limit' => $limitnum]);
  205. if (($limitfrom <= $totalcount - 1) && (!$outofrange)) {
  206. $nextpage = new \moodle_url($baseurl, ['from' => $limitfrom]);
  207. }
  208. $firstpage = new \moodle_url($baseurl, ['from' => 0]);
  209. $canonicalpage = new \moodle_url($baseurl, ['from' => $limitcurrent]);
  210. if (($limitcurrent > 0) && (!$outofrange)) {
  211. $prevpage = new \moodle_url($baseurl, ['from' => $limitprev]);
  212. }
  213. }
  214. }
  215. $jsonresults = [];
  216. $lineitem = new lineitem($this->get_service());
  217. $endpoint = $lineitem->get_endpoint();
  218. if ($grades) {
  219. foreach ($grades as $grade) {
  220. if (!empty($grade->timemodified)) {
  221. array_push($jsonresults, gradebookservices::result_for_json($grade, $endpoint, $typeid));
  222. }
  223. }
  224. }
  225. if (isset($canonicalpage) && ($canonicalpage)) {
  226. $links = 'Link: <' . $firstpage->out() . '>; rel=“first”';
  227. if (!is_null($prevpage)) {
  228. $links .= ', <' . $prevpage->out() . '>; rel=“prev”';
  229. }
  230. $links .= ', <' . $canonicalpage->out() . '>; rel=“canonical”';
  231. if (!is_null($nextpage)) {
  232. $links .= ', <' . $nextpage->out() . '>; rel=“next”';
  233. }
  234. $links .= ', <' . $lastpage->out() . '>; rel=“last”';
  235. $response->add_additional_header($links);
  236. }
  237. return json_encode($jsonresults);
  238. }
  239. /**
  240. * get permissions from the config of the tool for that resource
  241. *
  242. * @param int $typeid
  243. *
  244. * @return array with the permissions related to this resource by the $lti_type or null if none.
  245. */
  246. public function get_permissions($typeid) {
  247. $tool = lti_get_type_type_config($typeid);
  248. if ($tool->ltiservice_gradesynchronization == '1') {
  249. return array('Result.collection:get');
  250. } else if ($tool->ltiservice_gradesynchronization == '2') {
  251. return array('Result.collection:get');
  252. } else {
  253. return array();
  254. }
  255. }
  256. /**
  257. * Parse a value for custom parameter substitution variables.
  258. *
  259. * @param string $value String to be parsed
  260. *
  261. * @return string
  262. */
  263. public function parse_value($value) {
  264. global $COURSE, $CFG;
  265. if (strpos($value, '$Results.url') !== false) {
  266. require_once($CFG->libdir . '/gradelib.php');
  267. $resolved = '';
  268. $this->params['context_id'] = $COURSE->id;
  269. $id = optional_param('id', 0, PARAM_INT); // Course Module ID.
  270. if (!empty($id)) {
  271. $cm = get_coursemodule_from_id('lti', $id, 0, false, MUST_EXIST);
  272. $id = $cm->instance;
  273. $item = grade_get_grades($COURSE->id, 'mod', 'lti', $id);
  274. if ($item && $item->items) {
  275. $this->params['item_id'] = $item->items[0]->id;
  276. $resolved = parent::get_endpoint();
  277. }
  278. }
  279. $value = str_replace('$Results.url', $resolved, $value);
  280. }
  281. return $value;
  282. }
  283. }