weekly release 4.5dev
[moodle.git] / enrol / lti / classes / data_connector.php
blob430e14dd3ea31b7ee6dc0cfff046fd3f4daa32da
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.
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/>.
17 /**
18 * Extends the IMS Tool provider library data connector for moodle.
20 * @package enrol_lti
21 * @copyright 2016 John Okely <john@moodle.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 namespace enrol_lti;
27 defined('MOODLE_INTERNAL') || die;
29 use IMSGlobal\LTI\ToolProvider;
30 use IMSGlobal\LTI\ToolProvider\ConsumerNonce;
31 use IMSGlobal\LTI\ToolProvider\Context;
32 use IMSGlobal\LTI\ToolProvider\DataConnector\DataConnector;
33 use IMSGlobal\LTI\ToolProvider\ResourceLink;
34 use IMSGlobal\LTI\ToolProvider\ResourceLinkShare;
35 use IMSGlobal\LTI\ToolProvider\ResourceLinkShareKey;
36 use IMSGlobal\LTI\ToolProvider\ToolConsumer;
37 use IMSGlobal\LTI\ToolProvider\ToolProxy;
38 use IMSGlobal\LTI\ToolProvider\User;
39 use stdClass;
41 /**
42 * Extends the IMS Tool provider library data connector for moodle.
44 * @package enrol_lti
45 * @copyright 2016 John Okely <john@moodle.com>
46 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
48 class data_connector extends DataConnector {
50 /** @var string Tool consumer table name. */
51 protected $consumertable;
52 /** @var string Context table name. */
53 protected $contexttable;
54 /** @var string Consumer nonce table name. */
55 protected $noncetable;
56 /** @var string Resource link table name. */
57 protected $resourcelinktable;
58 /** @var string Resource link share key table name. */
59 protected $sharekeytable;
60 /** @var string Tool proxy table name. */
61 protected $toolproxytable;
62 /** @var string User result table name. */
63 protected $userresulttable;
65 /**
66 * data_connector constructor.
68 public function __construct() {
69 parent::__construct(null, 'enrol_lti_');
71 // Set up table names.
72 $this->consumertable = $this->dbTableNamePrefix . DataConnector::CONSUMER_TABLE_NAME;
73 $this->contexttable = $this->dbTableNamePrefix . DataConnector::CONTEXT_TABLE_NAME;
74 $this->noncetable = $this->dbTableNamePrefix . DataConnector::NONCE_TABLE_NAME;
75 $this->resourcelinktable = $this->dbTableNamePrefix . DataConnector::RESOURCE_LINK_TABLE_NAME;
76 $this->sharekeytable = $this->dbTableNamePrefix . DataConnector::RESOURCE_LINK_SHARE_KEY_TABLE_NAME;
77 $this->toolproxytable = $this->dbTableNamePrefix . DataConnector::TOOL_PROXY_TABLE_NAME;
78 $this->userresulttable = $this->dbTableNamePrefix . DataConnector::USER_RESULT_TABLE_NAME;
81 /**
82 * Load tool consumer object.
84 * @param ToolConsumer $consumer ToolConsumer object
85 * @return boolean True if the tool consumer object was successfully loaded
87 public function loadToolConsumer($consumer) {
88 global $DB;
90 $id = $consumer->getRecordId();
91 $key = $consumer->getKey();
92 $result = false;
94 if (!empty($id)) {
95 $result = $DB->get_record($this->consumertable, ['id' => $id]);
96 } else if (!empty($key)) {
97 $key256 = DataConnector::getConsumerKey($key);
98 $result = $DB->get_record($this->consumertable, ['consumerkey256' => $key256]);
101 if ($result) {
102 if (empty($key256) || empty($result->consumerkey) || ($key === $result->consumerkey)) {
103 $this->build_tool_consumer_object($result, $consumer);
104 return true;
108 return false;
112 * Save tool consumer object.
114 * @param ToolConsumer $consumer Consumer object
115 * @return boolean True if the tool consumer object was successfully saved
117 public function saveToolConsumer($consumer) {
118 global $DB;
120 $key = $consumer->getKey();
121 $key256 = DataConnector::getConsumerKey($key);
122 if ($key === $key256) {
123 $key = null;
125 $protected = ($consumer->protected) ? 1 : 0;
126 $enabled = ($consumer->enabled) ? 1 : 0;
127 $profile = (!empty($consumer->profile)) ? json_encode($consumer->profile) : null;
128 $settingsvalue = serialize($consumer->getSettings());
129 $now = time();
130 $consumer->updated = $now;
131 $data = [
132 'consumerkey256' => $key256,
133 'consumerkey' => $key,
134 'name' => $consumer->name,
135 'secret' => $consumer->secret,
136 'ltiversion' => $consumer->ltiVersion,
137 'consumername' => $consumer->consumerName,
138 'consumerversion' => $consumer->consumerVersion,
139 'consumerguid' => $consumer->consumerGuid,
140 'profile' => $profile,
141 'toolproxy' => $consumer->toolProxy,
142 'settings' => $settingsvalue,
143 'protected' => $protected,
144 'enabled' => $enabled,
145 'enablefrom' => $consumer->enableFrom,
146 'enableuntil' => $consumer->enableUntil,
147 'lastaccess' => $consumer->lastAccess,
148 'updated' => $consumer->updated,
151 $id = $consumer->getRecordId();
153 if (empty($id)) {
154 $consumer->created = $now;
155 $data['created'] = $consumer->created;
156 $id = $DB->insert_record($this->consumertable, (object) $data);
157 if ($id) {
158 $consumer->setRecordId($id);
159 return true;
161 } else {
162 $data['id'] = $id;
163 return $DB->update_record($this->consumertable, (object) $data);
166 return false;
170 * Delete tool consumer object and related records.
172 * @param ToolConsumer $consumer Consumer object
173 * @return boolean True if the tool consumer object was successfully deleted
175 public function deleteToolConsumer($consumer) {
176 global $DB;
178 $consumerpk = $consumer->getRecordId();
179 $deletecondition = ['consumerid' => $consumerpk];
181 // Delete any nonce values for this consumer.
182 $DB->delete_records($this->noncetable, $deletecondition);
184 // Delete any outstanding share keys for resource links for this consumer.
185 $where = "resourcelinkid IN (
186 SELECT rl.id
187 FROM {{$this->resourcelinktable}} rl
188 WHERE rl.consumerid = :consumerid
190 $DB->delete_records_select($this->sharekeytable, $where, $deletecondition);
192 // Delete any outstanding share keys for resource links for contexts in this consumer.
193 $where = "resourcelinkid IN (
194 SELECT rl.id
195 FROM {{$this->resourcelinktable}} rl
196 INNER JOIN {{$this->contexttable}} c
197 ON rl.contextid = c.id
198 WHERE c.consumerid = :consumerid
200 $DB->delete_records_select($this->sharekeytable, $where, $deletecondition);
202 // Delete any users in resource links for this consumer.
203 $where = "resourcelinkid IN (
204 SELECT rl.id
205 FROM {{$this->resourcelinktable}} rl
206 WHERE rl.consumerid = :consumerid
208 $DB->delete_records_select($this->userresulttable, $where, $deletecondition);
210 // Delete any users in resource links for contexts in this consumer.
211 $where = "resourcelinkid IN (
212 SELECT rl.id
213 FROM {{$this->resourcelinktable}} rl
214 INNER JOIN {{$this->contexttable}} c
215 ON rl.contextid = c.id
216 WHERE c.consumerid = :consumerid
218 $DB->delete_records_select($this->userresulttable, $where, $deletecondition);
220 // Update any resource links for which this consumer is acting as a primary resource link.
221 $where = "primaryresourcelinkid IN (
222 SELECT rl.id
223 FROM {{$this->resourcelinktable}} rl
224 WHERE rl.consumerid = :consumerid
226 $updaterecords = $DB->get_records_select($this->resourcelinktable, $where, $deletecondition);
227 foreach ($updaterecords as $record) {
228 $record->primaryresourcelinkid = null;
229 $record->shareapproved = null;
230 $DB->update_record($this->resourcelinktable, $record);
233 // Update any resource links for contexts in which this consumer is acting as a primary resource link.
234 $where = "primaryresourcelinkid IN (
235 SELECT rl.id
236 FROM {{$this->resourcelinktable}} rl
237 INNER JOIN {{$this->contexttable}} c
238 ON rl.contextid = c.id
239 WHERE c.consumerid = :consumerid
241 $updaterecords = $DB->get_records_select($this->resourcelinktable, $where, $deletecondition);
242 foreach ($updaterecords as $record) {
243 $record->primaryresourcelinkid = null;
244 $record->shareapproved = null;
245 $DB->update_record($this->resourcelinktable, $record);
248 // Delete any resource links for contexts in this consumer.
249 $where = "contextid IN (
250 SELECT c.id
251 FROM {{$this->contexttable}} c
252 WHERE c.consumerid = :consumerid
254 $DB->delete_records_select($this->resourcelinktable, $where, $deletecondition);
256 // Delete any resource links for this consumer.
257 $DB->delete_records($this->resourcelinktable, $deletecondition);
259 // Delete any contexts for this consumer.
260 $DB->delete_records($this->contexttable, $deletecondition);
262 // Delete consumer.
263 $DB->delete_records($this->consumertable, ['id' => $consumerpk]);
265 $consumer->initialize();
267 return true;
271 * Load all tool consumers from the database.
272 * @return array
274 public function getToolConsumers() {
275 global $DB;
276 $consumers = [];
278 $rsconsumers = $DB->get_recordset($this->consumertable, null, 'name');
279 foreach ($rsconsumers as $row) {
280 $consumer = new ToolProvider\ToolConsumer($row->consumerkey, $this);
281 $this->build_tool_consumer_object($row, $consumer);
282 $consumers[] = $consumer;
284 $rsconsumers->close();
286 return $consumers;
290 * ToolProxy methods.
294 * Load the tool proxy from the database.
296 * @param ToolProxy $toolproxy
297 * @return bool
299 public function loadToolProxy($toolproxy) {
300 return false;
304 * Save the tool proxy to the database.
306 * @param ToolProxy $toolproxy
307 * @return bool
309 public function saveToolProxy($toolproxy) {
310 return false;
314 * Delete the tool proxy from the database.
316 * @param ToolProxy $toolproxy
317 * @return bool
319 public function deleteToolProxy($toolproxy) {
320 return false;
324 * Context methods.
328 * Load context object.
330 * @param Context $context Context object
331 * @return boolean True if the context object was successfully loaded
333 public function loadContext($context) {
334 global $DB;
336 if (!empty($context->getRecordId())) {
337 $params = ['id' => $context->getRecordId()];
338 } else {
339 $params = [
340 'consumerid' => $context->getConsumer()->getRecordId(),
341 'lticontextkey' => $context->ltiContextId
344 if ($row = $DB->get_record($this->contexttable, $params)) {
345 $context->setRecordId($row->id);
346 $context->setConsumerId($row->consumerid);
347 $context->ltiContextId = $row->lticontextkey;
348 $context->type = $row->type;
349 $settings = unserialize($row->settings);
350 if (!is_array($settings)) {
351 $settings = array();
353 $context->setSettings($settings);
354 $context->created = $row->created;
355 $context->updated = $row->updated;
356 return true;
359 return false;
363 * Save context object.
365 * @param Context $context Context object
366 * @return boolean True if the context object was successfully saved
368 public function saveContext($context) {
369 global $DB;
370 $now = time();
371 $context->updated = $now;
372 $settingsvalue = serialize($context->getSettings());
373 $id = $context->getRecordId();
374 $consumerpk = $context->getConsumer()->getRecordId();
376 $isinsert = empty($id);
377 if ($isinsert) {
378 $context->created = $now;
379 $params = [
380 'consumerid' => $consumerpk,
381 'lticontextkey' => $context->ltiContextId,
382 'type' => $context->type,
383 'settings' => $settingsvalue,
384 'created' => $context->created,
385 'updated' => $context->updated,
387 $id = $DB->insert_record($this->contexttable, (object) $params);
388 if ($id) {
389 $context->setRecordId($id);
390 return true;
392 } else {
393 $data = (object) [
394 'id' => $id,
395 'contextid' => $consumerpk,
396 'lticontextkey' => $context->ltiContextId,
397 'type' => $context->type,
398 'settings' => $settingsvalue,
399 'updated' => $context->updated,
401 return $DB->update_record($this->contexttable, $data);
404 return false;
408 * Delete context object.
410 * @param Context $context Context object
411 * @return boolean True if the Context object was successfully deleted
413 public function deleteContext($context) {
414 global $DB;
416 $contextid = $context->getRecordId();
418 $params = ['id' => $contextid];
420 // Delete any outstanding share keys for resource links for this context.
421 $where = "resourcelinkid IN (
422 SELECT rl.id
423 FROM {{$this->resourcelinktable}} rl
424 WHERE rl.contextid = :id
426 $DB->delete_records_select($this->sharekeytable, $where, $params);
428 // Delete any users in resource links for this context.
429 $DB->delete_records_select($this->userresulttable, $where, $params);
431 // Update any resource links for which this consumer is acting as a primary resource link.
432 $where = "primaryresourcelinkid IN (
433 SELECT rl.id
434 FROM {{$this->resourcelinktable}} rl
435 WHERE rl.contextid = :id
437 $updaterecords = $DB->get_records_select($this->resourcelinktable, $where, $params);
438 foreach ($updaterecords as $record) {
439 $record->primaryresourcelinkid = null;
440 $record->shareapproved = null;
441 $DB->update_record($this->resourcelinktable, $record);
444 // Delete any resource links for this context.
445 $DB->delete_records($this->resourcelinktable, ['contextid' => $contextid]);
447 // Delete context.
448 $DB->delete_records($this->contexttable, $params);
450 $context->initialize();
452 return true;
456 * ResourceLink methods
460 * Load resource link object.
462 * @param ResourceLink $resourcelink ResourceLink object
463 * @return boolean True if the resource link object was successfully loaded
465 public function loadResourceLink($resourcelink) {
466 global $DB;
468 $resourceid = $resourcelink->getRecordId();
469 if (!empty($resourceid)) {
470 $params = ['id' => $resourceid];
471 $row = $DB->get_record($this->resourcelinktable, $params);
472 } else if (!empty($resourcelink->getContext())) {
473 $params = [
474 'contextid' => $resourcelink->getContext()->getRecordId(),
475 'ltiresourcelinkkey' => $resourcelink->getId()
477 $row = $DB->get_record($this->resourcelinktable, $params);
478 } else {
479 $sql = "SELECT r.*
480 FROM {{$this->resourcelinktable}} r
481 LEFT OUTER JOIN {{$this->contexttable}} c
482 ON r.contextid = c.id
483 WHERE (r.consumerid = ? OR c.consumerid = ?)
484 AND ltiresourcelinkkey = ?";
485 $params = [
486 $resourcelink->getConsumer()->getRecordId(),
487 $resourcelink->getConsumer()->getRecordId(),
488 $resourcelink->getId()
490 $row = $DB->get_record_sql($sql, $params);
492 if ($row) {
493 $resourcelink->setRecordId($row->id);
494 if (!is_null($row->contextid)) {
495 $resourcelink->setContextId($row->contextid);
496 } else {
497 $resourcelink->setContextId(null);
499 if (!is_null($row->consumerid)) {
500 $resourcelink->setConsumerId($row->consumerid);
501 } else {
502 $resourcelink->setConsumerId(null);
504 $resourcelink->ltiResourceLinkId = $row->ltiresourcelinkkey;
505 $settings = unserialize($row->settings);
506 if (!is_array($settings)) {
507 $settings = array();
509 $resourcelink->setSettings($settings);
510 if (!is_null($row->primaryresourcelinkid)) {
511 $resourcelink->primaryResourceLinkId = $row->primaryresourcelinkid;
512 } else {
513 $resourcelink->primaryResourceLinkId = null;
515 $resourcelink->shareApproved = (is_null($row->shareapproved)) ? null : ($row->shareapproved == 1);
516 $resourcelink->created = $row->created;
517 $resourcelink->updated = $row->updated;
518 return true;
521 return false;
525 * Save resource link object.
527 * @param ResourceLink $resourcelink Resource_Link object
528 * @return boolean True if the resource link object was successfully saved
530 public function saveResourceLink($resourcelink) {
531 global $DB;
533 if (is_null($resourcelink->shareApproved)) {
534 $approved = null;
535 } else if ($resourcelink->shareApproved) {
536 $approved = 1;
537 } else {
538 $approved = 0;
540 if (empty($resourcelink->primaryResourceLinkId)) {
541 $primaryresourcelinkid = null;
542 } else {
543 $primaryresourcelinkid = $resourcelink->primaryResourceLinkId;
545 $now = time();
546 $resourcelink->updated = $now;
547 $settingsvalue = serialize($resourcelink->getSettings());
548 if (!empty($resourcelink->getContext())) {
549 $consumerid = null;
550 $contextid = $resourcelink->getContext()->getRecordId();
551 } else if (!empty($resourcelink->getContextId())) {
552 $consumerid = null;
553 $contextid = $resourcelink->getContextId();
554 } else {
555 $consumerid = $resourcelink->getConsumer()->getRecordId();
556 $contextid = null;
558 $id = $resourcelink->getRecordId();
560 $data = [
561 'consumerid' => $consumerid,
562 'contextid' => $contextid,
563 'ltiresourcelinkkey' => $resourcelink->getId(),
564 'settings' => $settingsvalue,
565 'primaryresourcelinkid' => $primaryresourcelinkid,
566 'shareapproved' => $approved,
567 'updated' => $resourcelink->updated,
570 $returnid = null;
572 if (empty($id)) {
573 $resourcelink->created = $now;
574 $data['created'] = $resourcelink->created;
575 $id = $DB->insert_record($this->resourcelinktable, (object) $data);
576 if ($id) {
577 $resourcelink->setRecordId($id);
578 return true;
581 } else {
582 $data['id'] = $id;
583 return $DB->update_record($this->resourcelinktable, (object) $data);
586 return false;
590 * Delete resource link object.
592 * @param ResourceLink $resourcelink ResourceLink object
593 * @return boolean True if the resource link object and its related records were successfully deleted.
594 * Otherwise, a DML exception is thrown.
596 public function deleteResourceLink($resourcelink) {
597 global $DB;
599 $resourcelinkid = $resourcelink->getRecordId();
601 // Delete any outstanding share keys for resource links for this consumer.
602 $DB->delete_records($this->sharekeytable, ['resourcelinkid' => $resourcelinkid]);
604 // Delete users.
605 $DB->delete_records($this->userresulttable, ['resourcelinkid' => $resourcelinkid]);
607 // Update any resource links for which this is the primary resource link.
608 $records = $DB->get_records($this->resourcelinktable, ['primaryresourcelinkid' => $resourcelinkid]);
609 foreach ($records as $record) {
610 $record->primaryresourcelinkid = null;
611 $DB->update_record($this->resourcelinktable, $record);
614 // Delete resource link.
615 $DB->delete_records($this->resourcelinktable, ['id' => $resourcelinkid]);
617 $resourcelink->initialize();
619 return true;
623 * Get array of user objects.
625 * Obtain an array of User objects for users with a result sourcedId. The array may include users from other
626 * resource links which are sharing this resource link. It may also be optionally indexed by the user ID of a specified scope.
628 * @param ResourceLink $resourcelink Resource link object
629 * @param boolean $localonly True if only users within the resource link are to be returned
630 * (excluding users sharing this resource link)
631 * @param int $idscope Scope value to use for user IDs
632 * @return array Array of User objects
634 public function getUserResultSourcedIDsResourceLink($resourcelink, $localonly, $idscope) {
635 global $DB;
637 $users = [];
639 $params = ['resourcelinkid' => $resourcelink->getRecordId()];
641 // Where clause for the subquery.
642 $subwhere = "(id = :resourcelinkid AND primaryresourcelinkid IS NULL)";
643 if (!$localonly) {
644 $subwhere .= " OR (primaryresourcelinkid = :resourcelinkid2 AND shareapproved = 1)";
645 $params['resourcelinkid2'] = $resourcelink->getRecordId();
648 // The subquery.
649 $subsql = "SELECT id
650 FROM {{$this->resourcelinktable}}
651 WHERE {$subwhere}";
653 // Our main where clause.
654 $where = "resourcelinkid IN ($subsql)";
656 // Fields to be queried.
657 $fields = 'id, ltiresultsourcedid, ltiuserkey, created, updated';
659 // Fetch records.
660 $rs = $DB->get_recordset_select($this->userresulttable, $where, $params, '', $fields);
661 foreach ($rs as $row) {
662 $user = User::fromResourceLink($resourcelink, $row->ltiuserkey);
663 $user->setRecordId($row->id);
664 $user->ltiResultSourcedId = $row->ltiresultsourcedid;
665 $user->created = $row->created;
666 $user->updated = $row->updated;
667 if (is_null($idscope)) {
668 $users[] = $user;
669 } else {
670 $users[$user->getId($idscope)] = $user;
673 $rs->close();
675 return $users;
679 * Get array of shares defined for this resource link.
681 * @param ResourceLink $resourcelink ResourceLink object
682 * @return array Array of ResourceLinkShare objects
684 public function getSharesResourceLink($resourcelink) {
685 global $DB;
687 $shares = [];
689 $params = ['primaryresourcelinkid' => $resourcelink->getRecordId()];
690 $fields = 'id, shareapproved, consumerid';
691 $records = $DB->get_records($this->resourcelinktable, $params, 'consumerid', $fields);
692 foreach ($records as $record) {
693 $share = new ResourceLinkShare();
694 $share->resourceLinkId = $record->id;
695 $share->approved = $record->shareapproved == 1;
696 $shares[] = $share;
699 return $shares;
703 * ConsumerNonce methods
707 * Load nonce object.
709 * @param ConsumerNonce $nonce Nonce object
710 * @return boolean True if the nonce object was successfully loaded
712 public function loadConsumerNonce($nonce) {
713 global $DB;
715 // Delete any expired nonce values.
716 $now = time();
717 $DB->delete_records_select($this->noncetable, "expires <= ?", [$now]);
719 // Load the nonce.
720 $params = [
721 'consumerid' => $nonce->getConsumer()->getRecordId(),
722 'value' => $nonce->getValue()
724 $result = $DB->get_field($this->noncetable, 'value', $params);
726 return !empty($result);
730 * Save nonce object.
732 * @param ConsumerNonce $nonce Nonce object
733 * @return boolean True if the nonce object was successfully saved
735 public function saveConsumerNonce($nonce) {
736 global $DB;
738 $data = [
739 'consumerid' => $nonce->getConsumer()->getRecordId(),
740 'value' => $nonce->getValue(),
741 'expires' => $nonce->expires
744 return $DB->insert_record($this->noncetable, (object) $data, false);
748 * ResourceLinkShareKey methods.
752 * Load resource link share key object.
754 * @param ResourceLinkShareKey $sharekey ResourceLink share key object
755 * @return boolean True if the resource link share key object was successfully loaded
757 public function loadResourceLinkShareKey($sharekey) {
758 global $DB;
760 // Clear expired share keys.
761 $now = time();
762 $where = "expires <= :expires";
764 $DB->delete_records_select($this->sharekeytable, $where, ['expires' => $now]);
766 // Load share key.
767 $fields = 'resourcelinkid, autoapprove, expires';
768 if ($sharekeyrecord = $DB->get_record($this->sharekeytable, ['sharekey' => $sharekey->getId()], $fields)) {
769 if ($sharekeyrecord->resourcelinkid == $sharekey->resourceLinkId) {
770 $sharekey->autoApprove = $sharekeyrecord->autoapprove == 1;
771 $sharekey->expires = $sharekeyrecord->expires;
772 return true;
776 return false;
780 * Save resource link share key object.
782 * @param ResourceLinkShareKey $sharekey Resource link share key object
783 * @return boolean True if the resource link share key object was successfully saved
785 public function saveResourceLinkShareKey($sharekey) {
786 global $DB;
788 if ($sharekey->autoApprove) {
789 $approve = 1;
790 } else {
791 $approve = 0;
794 $expires = $sharekey->expires;
796 $params = [
797 'sharekey' => $sharekey->getId(),
798 'resourcelinkid' => $sharekey->resourceLinkId,
799 'autoapprove' => $approve,
800 'expires' => $expires
803 return $DB->insert_record($this->sharekeytable, (object) $params, false);
807 * Delete resource link share key object.
809 * @param ResourceLinkShareKey $sharekey Resource link share key object
810 * @return boolean True if the resource link share key object was successfully deleted
812 public function deleteResourceLinkShareKey($sharekey) {
813 global $DB;
815 $DB->delete_records($this->sharekeytable, ['sharekey' => $sharekey->getId()]);
816 $sharekey->initialize();
818 return true;
822 * User methods
826 * Load user object.
828 * @param User $user User object
829 * @return boolean True if the user object was successfully loaded
831 public function loadUser($user) {
832 global $DB;
834 $userid = $user->getRecordId();
835 $fields = 'id, resourcelinkid, ltiuserkey, ltiresultsourcedid, created, updated';
836 if (!empty($userid)) {
837 $row = $DB->get_record($this->userresulttable, ['id' => $userid], $fields);
838 } else {
839 $resourcelinkid = $user->getResourceLink()->getRecordId();
840 $userid = $user->getId(ToolProvider\ToolProvider::ID_SCOPE_ID_ONLY);
841 $row = $DB->get_record_select(
842 $this->userresulttable,
843 "resourcelinkid = ? AND ltiuserkey = ?",
844 [$resourcelinkid, $userid],
845 $fields
848 if ($row) {
849 $user->setRecordId($row->id);
850 $user->setResourceLinkId($row->resourcelinkid);
851 $user->ltiUserId = $row->ltiuserkey;
852 $user->ltiResultSourcedId = $row->ltiresultsourcedid;
853 $user->created = $row->created;
854 $user->updated = $row->updated;
855 return true;
858 return false;
862 * Save user object.
864 * @param User $user User object
865 * @return boolean True if the user object was successfully saved
867 public function saveUser($user) {
868 global $DB;
870 $now = time();
871 $isinsert = is_null($user->created);
872 $user->updated = $now;
874 $params = [
875 'ltiresultsourcedid' => $user->ltiResultSourcedId,
876 'updated' => $user->updated
879 if ($isinsert) {
880 $params['resourcelinkid'] = $user->getResourceLink()->getRecordId();
881 $params['ltiuserkey'] = $user->getId(ToolProvider\ToolProvider::ID_SCOPE_ID_ONLY);
882 $user->created = $now;
883 $params['created'] = $user->created;
884 $id = $DB->insert_record($this->userresulttable, (object) $params);
885 if ($id) {
886 $user->setRecordId($id);
887 return true;
890 } else {
891 $params['id'] = $user->getRecordId();
892 return $DB->update_record($this->userresulttable, (object) $params);
895 return false;
899 * Delete user object.
901 * @param User $user User object
902 * @return boolean True if the user object was successfully deleted
904 public function deleteUser($user) {
905 global $DB;
907 $DB->delete_records($this->userresulttable, ['id' => $user->getRecordId()]);
908 $user->initialize();
910 return true;
914 * Fetches the list of Context objects that are linked to a ToolConsumer.
916 * @param ToolConsumer $consumer
917 * @return Context[]
919 public function get_contexts_from_consumer(ToolConsumer $consumer) {
920 global $DB;
922 $contexts = [];
923 $contextrecords = $DB->get_records($this->contexttable, ['consumerid' => $consumer->getRecordId()], '', 'lticontextkey');
924 foreach ($contextrecords as $record) {
925 $context = Context::fromConsumer($consumer, $record->lticontextkey);
926 $contexts[] = $context;
929 return $contexts;
933 * Fetches a resource link record that is associated with a ToolConsumer.
935 * @param ToolConsumer $consumer
936 * @return ResourceLink
938 public function get_resourcelink_from_consumer(ToolConsumer $consumer) {
939 global $DB;
941 $resourcelink = null;
942 if ($resourcelinkrecord = $DB->get_record($this->resourcelinktable, ['consumerid' => $consumer->getRecordId()],
943 'ltiresourcelinkkey')) {
944 $resourcelink = ResourceLink::fromConsumer($consumer, $resourcelinkrecord->ltiresourcelinkkey);
947 return $resourcelink;
951 * Fetches a resource link record that is associated with a Context object.
953 * @param Context $context
954 * @return ResourceLink
956 public function get_resourcelink_from_context(Context $context) {
957 global $DB;
959 $resourcelink = null;
960 if ($resourcelinkrecord = $DB->get_record($this->resourcelinktable, ['contextid' => $context->getRecordId()],
961 'ltiresourcelinkkey')) {
962 $resourcelink = ResourceLink::fromContext($context, $resourcelinkrecord->ltiresourcelinkkey);
965 return $resourcelink;
970 * Fetches the list of ToolConsumer objects that are linked to a tool.
972 * @param int $toolid
973 * @return ToolConsumer[]
975 public function get_consumers_mapped_to_tool($toolid) {
976 global $DB;
978 $consumers = [];
979 $consumerrecords = $DB->get_records('enrol_lti_tool_consumer_map', ['toolid' => $toolid], '', 'consumerid');
980 foreach ($consumerrecords as $record) {
981 $consumers[] = ToolConsumer::fromRecordId($record->consumerid, $this);
983 return $consumers;
987 * Builds a ToolConsumer object from a record object from the DB.
989 * @param stdClass $record The DB record object.
990 * @param ToolConsumer $consumer
992 protected function build_tool_consumer_object($record, ToolConsumer $consumer) {
993 $consumer->setRecordId($record->id);
994 $consumer->name = $record->name;
995 $key = empty($record->consumerkey) ? $record->consumerkey256 : $record->consumerkey;
996 $consumer->setKey($key);
997 $consumer->secret = $record->secret;
998 $consumer->ltiVersion = $record->ltiversion;
999 $consumer->consumerName = $record->consumername;
1000 $consumer->consumerVersion = $record->consumerversion;
1001 $consumer->consumerGuid = $record->consumerguid;
1002 $consumer->profile = json_decode($record->profile ?? '');
1003 $consumer->toolProxy = $record->toolproxy;
1004 $settings = unserialize($record->settings);
1005 if (!is_array($settings)) {
1006 $settings = array();
1008 $consumer->setSettings($settings);
1009 $consumer->protected = $record->protected == 1;
1010 $consumer->enabled = $record->enabled == 1;
1011 $consumer->enableFrom = null;
1012 if (!is_null($record->enablefrom)) {
1013 $consumer->enableFrom = $record->enablefrom;
1015 $consumer->enableUntil = null;
1016 if (!is_null($record->enableuntil)) {
1017 $consumer->enableUntil = $record->enableuntil;
1019 $consumer->lastAccess = null;
1020 if (!is_null($record->lastaccess)) {
1021 $consumer->lastAccess = $record->lastaccess;
1023 $consumer->created = $record->created;
1024 $consumer->updated = $record->updated;