├── LICENSE
├── README.md
├── composer.json
└── src
├── HTTPMessage.php
├── OAuth
├── OAuthConsumer.php
├── OAuthDataStore.php
├── OAuthException.php
├── OAuthRequest.php
├── OAuthServer.php
├── OAuthSignatureMethod.php
├── OAuthSignatureMethod_HMAC_SHA1.php
├── OAuthSignatureMethod_HMAC_SHA256.php
├── OAuthToken.php
└── OAuthUtil.php
├── Profile
├── Item.php
├── Message.php
├── ResourceHandler.php
└── ServiceDefinition.php
└── ToolProvider
├── ConsumerNonce.php
├── ContentItem.php
├── ContentItemImage.php
├── ContentItemPlacement.php
├── Context.php
├── DataConnector
├── DataConnector.php
├── DataConnector_mysql.php
├── DataConnector_pdo.php
└── DataConnector_pdo_sqlite.php
├── MediaType
├── Message.php
├── ResourceHandler.php
├── SecurityContract.php
├── ToolProfile.php
└── ToolProxy.php
├── OAuthDataStore.php
├── Outcome.php
├── ResourceLink.php
├── ResourceLinkShare.php
├── ResourceLinkShareKey.php
├── Service
├── Membership.php
├── Service.php
└── ToolSettings.php
├── ToolConsumer.php
├── ToolProvider.php
├── ToolProxy.php
└── User.php
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This set of PHP classes encapsulates the code required by a Learning Tools Interoperability® (LTI®) compliant tool provider to communicate with an LTI tool consumer.
2 | It includes support for LTI 1.1 and the unofficial extensions to LTI 1.0, as well as the registration process and services of LTI 2.0.
3 | These classes are an extension of the LTI Tool Provider class library created by the ceLTIc project (http://www.spvsoftwareproducts.com/php/lti_tool_provider/).
4 |
5 | Whilst supporting LTI is relatively simple, the benefits to using a class library like this one are:
6 | * the abstraction layer provided by the classes keeps the LTI communications separate from the application code;
7 | * the code can be re-used between multiple tool providers;
8 | * LTI data is transformed into useful objects and missing data automatically replaced with sensible defaults;
9 | * the outcomes service function uses LTI 1.1 or the unofficial outcomes extension according to whichever is supported by the tool consumer;
10 | * the unofficial extensions for memberships and setting services are supported;
11 | * additional functionality is included to:
12 | * enable/disable a consumer key;
13 | * set start and end times for enabling access for each consumer key;
14 | * set up arrangements such that users from different resource links can all collaborate together within a single tool provider link;
15 | * tool providers can take advantage of LTI updates with minimal impact on their application code.
16 |
17 | The wiki area of this repository contains [documentation](https://github.com/IMSGlobal/LTI-Tool-Provider-Library-PHP/wiki) for this library. The [rating LTI application](https://github.com/IMSGlobal/LTI-Sample-Tool-Provider-PHP) is based on this library to further illustrate how it can be used.
18 |
19 | © 2016 IMS Global Learning Consortium Inc. All Rights Reserved. Trademark Policy - (www.imsglobal.org/trademarks)
20 |
21 | Learning Tools Interoperability and LTI are registered trademarks of IMS Global Learning Consortium Inc.
22 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "imsglobal/lti",
3 | "version" : "3.0.2",
4 | "description": "LTI Tool Provider Library",
5 | "keywords": ["lti"],
6 | "homepage": "https://www.imsglobal.org/lti",
7 | "type": "library",
8 | "license": "Apache-2.0",
9 | "authors":[
10 | {
11 | "name": "Stephen Vickers",
12 | "email": "svickers@imsglobal.org"
13 | }
14 | ],
15 | "require":{
16 | "php": ">=5.6.0"
17 | },
18 | "autoload":{
19 | "psr-4": {
20 | "IMSGlobal\\LTI\\": "src/"
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/HTTPMessage.php:
--------------------------------------------------------------------------------
1 |
9 | * @copyright IMS Global Learning Consortium Inc
10 | * @date 2016
11 | * @version 3.0.0
12 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
13 | */
14 | class HTTPMessage
15 | {
16 |
17 | /**
18 | * True if message was sent successfully.
19 | *
20 | * @var boolean $ok
21 | */
22 | public $ok = false;
23 |
24 | /**
25 | * Request body.
26 | *
27 | * @var request $request
28 | */
29 | public $request = null;
30 |
31 | /**
32 | * Request headers.
33 | *
34 | * @var request_headers $requestHeaders
35 | */
36 | public $requestHeaders = '';
37 |
38 | /**
39 | * Response body.
40 | *
41 | * @var response $response
42 | */
43 | public $response = null;
44 |
45 | /**
46 | * Response headers.
47 | *
48 | * @var response_headers $responseHeaders
49 | */
50 | public $responseHeaders = '';
51 |
52 | /**
53 | * Status of response (0 if undetermined).
54 | *
55 | * @var status $status
56 | */
57 | public $status = 0;
58 |
59 | /**
60 | * Error message
61 | *
62 | * @var error $error
63 | */
64 | public $error = '';
65 |
66 | /**
67 | * Request URL.
68 | *
69 | * @var url $url
70 | */
71 | private $url = null;
72 |
73 | /**
74 | * Request method.
75 | *
76 | * @var method $method
77 | */
78 | private $method = null;
79 |
80 | /**
81 | * Class constructor.
82 | *
83 | * @param string $url URL to send request to
84 | * @param string $method Request method to use (optional, default is GET)
85 | * @param mixed $params Associative array of parameter values to be passed or message body (optional, default is none)
86 | * @param string $header Values to include in the request header (optional, default is none)
87 | */
88 | function __construct($url, $method = 'GET', $params = null, $header = null)
89 | {
90 |
91 | $this->url = $url;
92 | $this->method = strtoupper($method);
93 | if (is_array($params)) {
94 | $this->request = http_build_query($params);
95 | } else {
96 | $this->request = $params;
97 | }
98 | if (!empty($header)) {
99 | $this->requestHeaders = explode("\n", $header);
100 | }
101 |
102 | }
103 |
104 | /**
105 | * Send the request to the target URL.
106 | *
107 | * @return boolean True if the request was successful
108 | */
109 | public function send()
110 | {
111 |
112 | $this->ok = false;
113 | // Try using curl if available
114 | if (function_exists('curl_init')) {
115 | $resp = '';
116 | $ch = curl_init();
117 | curl_setopt($ch, CURLOPT_URL, $this->url);
118 | if (!empty($this->requestHeaders)) {
119 | curl_setopt($ch, CURLOPT_HTTPHEADER, $this->requestHeaders);
120 | } else {
121 | curl_setopt($ch, CURLOPT_HEADER, 0);
122 | }
123 | if ($this->method === 'POST') {
124 | curl_setopt($ch, CURLOPT_POST, true);
125 | curl_setopt($ch, CURLOPT_POSTFIELDS, $this->request);
126 | } else if ($this->method !== 'GET') {
127 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->method);
128 | if (!is_null($this->request)) {
129 | curl_setopt($ch, CURLOPT_POSTFIELDS, $this->request);
130 | }
131 | }
132 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
133 | curl_setopt($ch, CURLINFO_HEADER_OUT, true);
134 | curl_setopt($ch, CURLOPT_HEADER, true);
135 | //curl_setopt($ch, CURLOPT_SSLVERSION,3);
136 | $chResp = curl_exec($ch);
137 | $this->ok = $chResp !== false;
138 | if ($this->ok) {
139 | $chResp = str_replace("\r\n", "\n", $chResp);
140 | $chRespSplit = explode("\n\n", $chResp, 2);
141 | if ((count($chRespSplit) > 1) && (substr($chRespSplit[1], 0, 5) === 'HTTP/')) {
142 | $chRespSplit = explode("\n\n", $chRespSplit[1], 2);
143 | }
144 | $this->responseHeaders = $chRespSplit[0];
145 | $resp = $chRespSplit[1];
146 | $this->status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
147 | $this->ok = $this->status < 400;
148 | if (!$this->ok) {
149 | $this->error = curl_error($ch);
150 | }
151 | }
152 | $this->requestHeaders = str_replace("\r\n", "\n", curl_getinfo($ch, CURLINFO_HEADER_OUT));
153 | curl_close($ch);
154 | $this->response = $resp;
155 | } else {
156 | // Try using fopen if curl was not available
157 | $opts = array('method' => $this->method,
158 | 'content' => $this->request
159 | );
160 | if (!empty($this->requestHeaders)) {
161 | $opts['header'] = $this->requestHeaders;
162 | }
163 | try {
164 | $ctx = stream_context_create(array('http' => $opts));
165 | $fp = @fopen($this->url, 'rb', false, $ctx);
166 | if ($fp) {
167 | $resp = @stream_get_contents($fp);
168 | $this->ok = $resp !== false;
169 | }
170 | } catch (\Exception $e) {
171 | $this->ok = false;
172 | }
173 | }
174 |
175 | return $this->ok;
176 |
177 | }
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/src/OAuth/OAuthConsumer.php:
--------------------------------------------------------------------------------
1 | key = $key;
19 | $this->secret = $secret;
20 | $this->callback_url = $callback_url;
21 | }
22 |
23 | function __toString() {
24 | return "OAuthConsumer[key=$this->key,secret=$this->secret]";
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/OAuth/OAuthDataStore.php:
--------------------------------------------------------------------------------
1 | parameters = $parameters;
27 | $this->http_method = $http_method;
28 | $this->http_url = $http_url;
29 |
30 | }
31 |
32 |
33 | /**
34 | * attempt to build up a request from what was passed to the server
35 | */
36 | public static function from_request($http_method = null, $http_url = null, $parameters = null) {
37 |
38 | $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
39 | ? 'http'
40 | : 'https';
41 | $http_url = ($http_url) ? $http_url : $scheme .
42 | '://' . $_SERVER['SERVER_NAME'] .
43 | ':' .
44 | $_SERVER['SERVER_PORT'] .
45 | $_SERVER['REQUEST_URI'];
46 | $http_method = ($http_method) ? $http_method : $_SERVER['REQUEST_METHOD'];
47 |
48 | // We weren't handed any parameters, so let's find the ones relevant to
49 | // this request.
50 | // If you run XML-RPC or similar you should use this to provide your own
51 | // parsed parameter-list
52 | if (!$parameters) {
53 | // Find request headers
54 | $request_headers = OAuthUtil::get_headers();
55 |
56 | // Parse the query-string to find GET parameters
57 | if (isset($_SERVER['QUERY_STRING'])) {
58 | $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
59 | } else {
60 | $parameters = array();
61 | }
62 |
63 | // It's a POST request of the proper content-type, so parse POST
64 | // parameters and add those overriding any duplicates from GET
65 | if ($http_method == "POST"
66 | && isset($request_headers['Content-Type'])
67 | && strstr($request_headers['Content-Type'], 'application/x-www-form-urlencoded')) {
68 | $post_data = OAuthUtil::parse_parameters(file_get_contents(self::$POST_INPUT));
69 | $parameters = array_merge($parameters, $post_data);
70 | }
71 |
72 | // We have a Authorization-header with OAuth data. Parse the header
73 | // and add those overriding any duplicates from GET or POST
74 | if (isset($request_headers['Authorization']) && substr($request_headers['Authorization'], 0, 6) == 'OAuth ') {
75 | $header_parameters = OAuthUtil::split_header($request_headers['Authorization']);
76 | $parameters = array_merge($parameters, $header_parameters);
77 | }
78 |
79 | }
80 |
81 | return new OAuthRequest($http_method, $http_url, $parameters);
82 | }
83 |
84 | /**
85 | * pretty much a helper function to set up the request
86 | */
87 | public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters = null) {
88 |
89 | $parameters = ($parameters) ? $parameters : array();
90 | $defaults = array('oauth_version' => OAuthRequest::$version,
91 | 'oauth_nonce' => OAuthRequest::generate_nonce(),
92 | 'oauth_timestamp' => OAuthRequest::generate_timestamp(),
93 | 'oauth_consumer_key' => $consumer->key);
94 | if ($token)
95 | $defaults['oauth_token'] = $token->key;
96 |
97 | $parameters = array_merge($defaults, $parameters);
98 |
99 | return new OAuthRequest($http_method, $http_url, $parameters);
100 |
101 | }
102 |
103 | public function set_parameter($name, $value, $allow_duplicates = true) {
104 |
105 | if ($allow_duplicates && isset($this->parameters[$name])) {
106 | // We have already added parameter(s) with this name, so add to the list
107 | if (is_scalar($this->parameters[$name])) {
108 | // This is the first duplicate, so transform scalar (string)
109 | // into an array so we can add the duplicates
110 | $this->parameters[$name] = array($this->parameters[$name]);
111 | }
112 |
113 | $this->parameters[$name][] = $value;
114 | } else {
115 | $this->parameters[$name] = $value;
116 | }
117 | }
118 |
119 | public function get_parameter($name) {
120 | return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
121 | }
122 |
123 | public function get_parameters() {
124 | return $this->parameters;
125 | }
126 |
127 | public function unset_parameter($name) {
128 | unset($this->parameters[$name]);
129 | }
130 |
131 | /**
132 | * The request parameters, sorted and concatenated into a normalized string.
133 | * @return string
134 | */
135 | public function get_signable_parameters() {
136 |
137 | // Grab all parameters
138 | $params = $this->parameters;
139 |
140 | // Remove oauth_signature if present
141 | // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
142 | if (isset($params['oauth_signature'])) {
143 | unset($params['oauth_signature']);
144 | }
145 |
146 | return OAuthUtil::build_http_query($params);
147 |
148 | }
149 |
150 | /**
151 | * Returns the base string of this request
152 | *
153 | * The base string defined as the method, the url
154 | * and the parameters (normalized), each urlencoded
155 | * and the concated with &.
156 | */
157 | public function get_signature_base_string() {
158 | $parts = array(
159 | $this->get_normalized_http_method(),
160 | $this->get_normalized_http_url(),
161 | $this->get_signable_parameters()
162 | );
163 |
164 | $parts = OAuthUtil::urlencode_rfc3986($parts);
165 |
166 | return implode('&', $parts);
167 |
168 | }
169 |
170 | /**
171 | * just uppercases the http method
172 | */
173 | public function get_normalized_http_method() {
174 | return strtoupper($this->http_method);
175 | }
176 |
177 | /**
178 | * parses the url and rebuilds it to be
179 | * scheme://host/path
180 | */
181 | public function get_normalized_http_url() {
182 |
183 | $parts = parse_url($this->http_url);
184 |
185 | $scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http';
186 | $port = (isset($parts['port'])) ? $parts['port'] : (($scheme == 'https') ? '443' : '80');
187 | $host = (isset($parts['host'])) ? strtolower($parts['host']) : '';
188 | $path = (isset($parts['path'])) ? $parts['path'] : '';
189 |
190 | if (($scheme == 'https' && $port != '443')
191 | || ($scheme == 'http' && $port != '80')) {
192 | $host = "$host:$port";
193 | }
194 |
195 | return "$scheme://$host$path";
196 |
197 | }
198 |
199 | /**
200 | * builds a url usable for a GET request
201 | */
202 | public function to_url() {
203 |
204 | $post_data = $this->to_postdata();
205 | $out = $this->get_normalized_http_url();
206 | if ($post_data) {
207 | $out .= '?'.$post_data;
208 | }
209 |
210 | return $out;
211 |
212 | }
213 |
214 | /**
215 | * builds the data one would send in a POST request
216 | */
217 | public function to_postdata() {
218 | return OAuthUtil::build_http_query($this->parameters);
219 | }
220 |
221 | /**
222 | * builds the Authorization: header
223 | */
224 | public function to_header($realm = null) {
225 |
226 | $first = true;
227 | if($realm) {
228 | $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
229 | $first = false;
230 | } else
231 | $out = 'Authorization: OAuth';
232 |
233 | $total = array();
234 | foreach ($this->parameters as $k => $v) {
235 | if (substr($k, 0, 5) != "oauth") continue;
236 | if (is_array($v)) {
237 | throw new OAuthException('Arrays not supported in headers');
238 | }
239 | $out .= ($first) ? ' ' : ',';
240 | $out .= OAuthUtil::urlencode_rfc3986($k) .
241 | '="' .
242 | OAuthUtil::urlencode_rfc3986($v) .
243 | '"';
244 | $first = false;
245 | }
246 |
247 | return $out;
248 |
249 | }
250 |
251 | public function __toString() {
252 | return $this->to_url();
253 | }
254 |
255 |
256 | public function sign_request($signature_method, $consumer, $token) {
257 |
258 | $this->set_parameter(
259 | "oauth_signature_method",
260 | $signature_method->get_name(),
261 | false
262 | );
263 | $signature = $this->build_signature($signature_method, $consumer, $token);
264 | $this->set_parameter("oauth_signature", $signature, false);
265 |
266 | }
267 |
268 | public function build_signature($signature_method, $consumer, $token) {
269 | $signature = $signature_method->build_signature($this, $consumer, $token);
270 | return $signature;
271 | }
272 |
273 | /**
274 | * util function: current timestamp
275 | */
276 | private static function generate_timestamp() {
277 | return time();
278 | }
279 |
280 | /**
281 | * util function: current nonce
282 | */
283 | private static function generate_nonce() {
284 | $mt = microtime();
285 | $rand = mt_rand();
286 |
287 | return md5($mt . $rand); // md5s look nicer than numbers
288 | }
289 |
290 | }
291 |
--------------------------------------------------------------------------------
/src/OAuth/OAuthServer.php:
--------------------------------------------------------------------------------
1 | data_store = $data_store;
22 | }
23 |
24 | public function add_signature_method($signature_method) {
25 | $this->signature_methods[$signature_method->get_name()] = $signature_method;
26 | }
27 |
28 | // high level functions
29 |
30 | /**
31 | * process a request_token request
32 | * returns the request token on success
33 | */
34 | public function fetch_request_token(&$request) {
35 |
36 | $this->get_version($request);
37 |
38 | $consumer = $this->get_consumer($request);
39 |
40 | // no token required for the initial token request
41 | $token = NULL;
42 |
43 | $this->check_signature($request, $consumer, $token);
44 |
45 | // Rev A change
46 | $callback = $request->get_parameter('oauth_callback');
47 | $new_token = $this->data_store->new_request_token($consumer, $callback);
48 |
49 | return $new_token;
50 |
51 | }
52 |
53 | /**
54 | * process an access_token request
55 | * returns the access token on success
56 | */
57 | public function fetch_access_token(&$request) {
58 |
59 | $this->get_version($request);
60 |
61 | $consumer = $this->get_consumer($request);
62 |
63 | // requires authorized request token
64 | $token = $this->get_token($request, $consumer, "request");
65 |
66 | $this->check_signature($request, $consumer, $token);
67 |
68 | // Rev A change
69 | $verifier = $request->get_parameter('oauth_verifier');
70 | $new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
71 |
72 | return $new_token;
73 |
74 | }
75 |
76 | /**
77 | * verify an api call, checks all the parameters
78 | */
79 | public function verify_request(&$request) {
80 |
81 | $this->get_version($request);
82 | $consumer = $this->get_consumer($request);
83 | $token = $this->get_token($request, $consumer, "access");
84 | $this->check_signature($request, $consumer, $token);
85 |
86 | return array($consumer, $token);
87 |
88 | }
89 |
90 | // Internals from here
91 | /**
92 | * version 1
93 | */
94 | private function get_version(&$request) {
95 |
96 | $version = $request->get_parameter("oauth_version");
97 | if (!$version) {
98 | // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
99 | // Chapter 7.0 ("Accessing Protected Ressources")
100 | $version = '1.0';
101 | }
102 | if ($version !== $this->version) {
103 | throw new OAuthException("OAuth version '$version' not supported");
104 | }
105 |
106 | return $version;
107 |
108 | }
109 |
110 | /**
111 | * figure out the signature with some defaults
112 | */
113 | private function get_signature_method($request) {
114 |
115 | $signature_method = $request instanceof OAuthRequest
116 | ? $request->get_parameter('oauth_signature_method') : NULL;
117 |
118 | if (!$signature_method) {
119 | // According to chapter 7 ("Accessing Protected Ressources") the signature-method
120 | // parameter is required, and we can't just fallback to PLAINTEXT
121 | throw new OAuthException('No signature method parameter. This parameter is required');
122 | }
123 |
124 | if (!in_array($signature_method,
125 | array_keys($this->signature_methods))) {
126 | throw new OAuthException(
127 | "Signature method '$signature_method' not supported " .
128 | 'try one of the following: ' .
129 | implode(', ', array_keys($this->signature_methods))
130 | );
131 | }
132 |
133 | return $this->signature_methods[$signature_method];
134 |
135 | }
136 |
137 | /**
138 | * try to find the consumer for the provided request's consumer key
139 | */
140 | private function get_consumer($request) {
141 |
142 | $consumer_key = $request instanceof OAuthRequest
143 | ? $request->get_parameter('oauth_consumer_key') : NULL;
144 |
145 | if (!$consumer_key) {
146 | throw new OAuthException('Invalid consumer key');
147 | }
148 |
149 | $consumer = $this->data_store->lookup_consumer($consumer_key);
150 | if (!$consumer) {
151 | throw new OAuthException('Invalid consumer');
152 | }
153 |
154 | return $consumer;
155 |
156 | }
157 |
158 | /**
159 | * try to find the token for the provided request's token key
160 | */
161 | private function get_token($request, $consumer, $token_type="access") {
162 |
163 | $token_field = $request instanceof OAuthRequest
164 | ? $request->get_parameter('oauth_token') : NULL;
165 |
166 | $token = $this->data_store->lookup_token($consumer, $token_type, $token_field);
167 | if (!$token) {
168 | throw new OAuthException("Invalid $token_type token: $token_field");
169 | }
170 |
171 | return $token;
172 |
173 | }
174 |
175 | /**
176 | * all-in-one function to check the signature on a request
177 | * should guess the signature method appropriately
178 | */
179 | private function check_signature($request, $consumer, $token) {
180 |
181 | // this should probably be in a different method
182 | $timestamp = $request instanceof OAuthRequest
183 | ? $request->get_parameter('oauth_timestamp')
184 | : NULL;
185 | $nonce = $request instanceof OAuthRequest
186 | ? $request->get_parameter('oauth_nonce')
187 | : NULL;
188 |
189 | $this->check_timestamp($timestamp);
190 | $this->check_nonce($consumer, $token, $nonce, $timestamp);
191 |
192 | $signature_method = $this->get_signature_method($request);
193 |
194 | $signature = $request->get_parameter('oauth_signature');
195 | $valid_sig = $signature_method->check_signature($request, $consumer, $token, $signature);
196 |
197 | if (!$valid_sig) {
198 | throw new OAuthException('Invalid signature');
199 | }
200 | }
201 |
202 | /**
203 | * check that the timestamp is new enough
204 | */
205 | private function check_timestamp($timestamp) {
206 | if(!$timestamp)
207 | throw new OAuthException('Missing timestamp parameter. The parameter is required');
208 |
209 | // verify that timestamp is recentish
210 | $now = time();
211 | if (abs($now - $timestamp) > $this->timestamp_threshold) {
212 | throw new OAuthException("Expired timestamp, yours $timestamp, ours $now");
213 | }
214 |
215 | }
216 |
217 | /**
218 | * check that the nonce is not repeated
219 | */
220 | private function check_nonce($consumer, $token, $nonce, $timestamp) {
221 |
222 | if(!$nonce)
223 | throw new OAuthException('Missing nonce parameter. The parameter is required');
224 |
225 | // verify that the nonce is uniqueish
226 | $found = $this->data_store->lookup_nonce($consumer, $token, $nonce, $timestamp);
227 | if ($found) {
228 | throw new OAuthException("Nonce already used: $nonce");
229 | }
230 |
231 | }
232 |
233 | }
234 |
--------------------------------------------------------------------------------
/src/OAuth/OAuthSignatureMethod.php:
--------------------------------------------------------------------------------
1 | build_signature($request, $consumer, $token);
46 |
47 | // Check for zero length, although unlikely here
48 | if (strlen($built) == 0 || strlen($signature) == 0) {
49 | return false;
50 | }
51 |
52 | if (strlen($built) != strlen($signature)) {
53 | return false;
54 | }
55 |
56 | // Avoid a timing leak with a (hopefully) time insensitive compare
57 | $result = 0;
58 | for ($i = 0; $i < strlen($signature); $i++) {
59 | $result |= ord($built{$i}) ^ ord($signature{$i});
60 | }
61 |
62 | return $result == 0;
63 |
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/OAuth/OAuthSignatureMethod_HMAC_SHA1.php:
--------------------------------------------------------------------------------
1 | get_signature_base_string();
28 | $request->base_string = $base_string;
29 |
30 | $key_parts = array(
31 | $consumer->secret,
32 | ($token) ? $token->secret : ""
33 | );
34 |
35 | $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
36 | $key = implode('&', $key_parts);
37 |
38 | return base64_encode(hash_hmac('sha1', $base_string, $key, true));
39 |
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/OAuth/OAuthSignatureMethod_HMAC_SHA256.php:
--------------------------------------------------------------------------------
1 |
9 | * @copyright IMS Global Learning Consortium Inc
10 | * @date 2016
11 | * @version 2015-11-30
12 | * @license https://opensource.org/licenses/MIT The MIT License
13 | */
14 | /**
15 | * The HMAC-SHA256 signature method uses the HMAC-SHA256 signature algorithm as defined in [RFC6234]
16 | * where the Signature Base String is the text and the key is the concatenated values (each first
17 | * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
18 | * character (ASCII code 38) even if empty.
19 | */
20 | class OAuthSignatureMethod_HMAC_SHA256 extends OAuthSignatureMethod {
21 |
22 | function get_name() {
23 | return "HMAC-SHA256";
24 | }
25 |
26 | public function build_signature($request, $consumer, $token) {
27 |
28 | $base_string = $request->get_signature_base_string();
29 | $request->base_string = $base_string;
30 |
31 | $key_parts = array(
32 | $consumer->secret,
33 | ($token) ? $token->secret : ""
34 | );
35 |
36 | $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
37 | $key = implode('&', $key_parts);
38 |
39 | return base64_encode(hash_hmac('sha256', $base_string, $key, true));
40 |
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/OAuth/OAuthToken.php:
--------------------------------------------------------------------------------
1 | key = $key;
24 | $this->secret = $secret;
25 | }
26 |
27 | /**
28 | * generates the basic string serialization of a token that a server
29 | * would respond to request_token and access_token calls with
30 | */
31 | function to_string() {
32 | return 'oauth_token=' .
33 | OAuthUtil::urlencode_rfc3986($this->key) .
34 | '&oauth_token_secret=' .
35 | OAuthUtil::urlencode_rfc3986($this->secret);
36 | }
37 |
38 | function __toString() {
39 | return $this->to_string();
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/OAuth/OAuthUtil.php:
--------------------------------------------------------------------------------
1 | $h) {
42 | $params[$h] = OAuthUtil::urldecode_rfc3986(empty($matches[3][$i]) ? $matches[4][$i] : $matches[3][$i]);
43 | }
44 | if (isset($params['realm'])) {
45 | unset($params['realm']);
46 | }
47 | }
48 |
49 | return $params;
50 |
51 | }
52 |
53 | // helper to try to sort out headers for people who aren't running apache
54 | public static function get_headers() {
55 |
56 | if (function_exists('apache_request_headers')) {
57 | // we need this to get the actual Authorization: header
58 | // because apache tends to tell us it doesn't exist
59 | $headers = apache_request_headers();
60 |
61 | // sanitize the output of apache_request_headers because
62 | // we always want the keys to be Cased-Like-This and arh()
63 | // returns the headers in the same case as they are in the
64 | // request
65 | $out = array();
66 | foreach ($headers AS $key => $value) {
67 | $key = str_replace(" ", "-", ucwords(strtolower(str_replace("-", " ", $key))));
68 | $out[$key] = $value;
69 | }
70 | } else {
71 | // otherwise we don't have apache and are just going to have to hope
72 | // that $_SERVER actually contains what we need
73 | $out = array();
74 | if( isset($_SERVER['CONTENT_TYPE']) )
75 | $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
76 | if( isset($_ENV['CONTENT_TYPE']) )
77 | $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
78 |
79 | foreach ($_SERVER as $key => $value) {
80 | if (substr($key, 0, 5) == 'HTTP_') {
81 | // this is chaos, basically it is just there to capitalize the first
82 | // letter of every word that is not an initial HTTP and strip HTTP
83 | // code from przemek
84 | $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))));
85 | $out[$key] = $value;
86 | }
87 | }
88 | }
89 | return $out;
90 | }
91 |
92 | // This function takes a input like a=b&a=c&d=e and returns the parsed
93 | // parameters like this
94 | // array('a' => array('b','c'), 'd' => 'e')
95 | public static function parse_parameters( $input ) {
96 |
97 | if (!isset($input) || !$input) return array();
98 |
99 | $pairs = explode('&', $input);
100 |
101 | $parsed_parameters = array();
102 | foreach ($pairs as $pair) {
103 | $split = explode('=', $pair, 2);
104 | $parameter = self::urldecode_rfc3986($split[0]);
105 | $value = isset($split[1]) ? self::urldecode_rfc3986($split[1]) : '';
106 |
107 | if (isset($parsed_parameters[$parameter])) {
108 | // We have already recieved parameter(s) with this name, so add to the list
109 | // of parameters with this name
110 |
111 | if (is_scalar($parsed_parameters[$parameter])) {
112 | // This is the first duplicate, so transform scalar (string) into an array
113 | // so we can add the duplicates
114 | $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]);
115 | }
116 |
117 | $parsed_parameters[$parameter][] = $value;
118 | } else {
119 | $parsed_parameters[$parameter] = $value;
120 | }
121 | }
122 |
123 | return $parsed_parameters;
124 |
125 | }
126 |
127 | public static function build_http_query($params) {
128 |
129 | if (!$params) return '';
130 |
131 | // Urlencode both keys and values
132 | $keys = OAuthUtil::urlencode_rfc3986(array_keys($params));
133 | $values = OAuthUtil::urlencode_rfc3986(array_values($params));
134 | $params = array_combine($keys, $values);
135 |
136 | // Parameters are sorted by name, using lexicographical byte value ordering.
137 | // Ref: Spec: 9.1.1 (1)
138 | uksort($params, 'strcmp');
139 |
140 | $pairs = array();
141 | foreach ($params as $parameter => $value) {
142 | if (is_array($value)) {
143 | // If two or more parameters share the same name, they are sorted by their value
144 | // Ref: Spec: 9.1.1 (1)
145 | // June 12th, 2010 - changed to sort because of issue 164 by hidetaka
146 | sort($value, SORT_STRING);
147 | foreach ($value as $duplicate_value) {
148 | $pairs[] = $parameter . '=' . $duplicate_value;
149 | }
150 | } else {
151 | $pairs[] = $parameter . '=' . $value;
152 | }
153 | }
154 |
155 | // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
156 | // Each name-value pair is separated by an '&' character (ASCII code 38)
157 | return implode('&', $pairs);
158 |
159 | }
160 |
161 | }
162 |
--------------------------------------------------------------------------------
/src/Profile/Item.php:
--------------------------------------------------------------------------------
1 |
9 | * @copyright IMS Global Learning Consortium Inc
10 | * @date 2016
11 | * @version 3.0.0
12 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
13 | */
14 | class Item
15 | {
16 |
17 | /**
18 | * ID of item.
19 | *
20 | * @var string $id
21 | */
22 | public $id = null;
23 | /**
24 | * Name of item.
25 | *
26 | * @var string $name
27 | */
28 | public $name = null;
29 | /**
30 | * Description of item.
31 | *
32 | * @var string $description
33 | */
34 | public $description = null;
35 | /**
36 | * URL of item.
37 | *
38 | * @var string $url
39 | */
40 | public $url = null;
41 | /**
42 | * Version of item.
43 | *
44 | * @var string $version
45 | */
46 | public $version = null;
47 | /**
48 | * Timestamp of item.
49 | *
50 | * @var int $timestamp
51 | */
52 | public $timestamp = null;
53 |
54 | /**
55 | * Class constructor.
56 | *
57 | * @param string $id ID of item (optional)
58 | * @param string $name Name of item (optional)
59 | * @param string $description Description of item (optional)
60 | * @param string $url URL of item (optional)
61 | * @param string $version Version of item (optional)
62 | * @param int $timestamp Timestamp of item (optional)
63 | */
64 |
65 | function __construct($id = null, $name = null, $description = null, $url = null, $version = null, $timestamp = null)
66 | {
67 |
68 | $this->id = $id;
69 | $this->name = $name;
70 | $this->description = $description;
71 | $this->url = $url;
72 | $this->version = $version;
73 | $this->timestamp = $timestamp;
74 |
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/src/Profile/Message.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright IMS Global Learning Consortium Inc
11 | * @date 2016
12 | * @version 3.0.0
13 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
14 | */
15 |
16 | class Message
17 | {
18 |
19 | /**
20 | * LTI message type.
21 | *
22 | * @var string $type
23 | */
24 | public $type = null;
25 | /**
26 | * Path to send message request to (used in conjunction with a base URL for the Tool Provider).
27 | *
28 | * @var string $path
29 | */
30 | public $path = null;
31 | /**
32 | * Capabilities required by message.
33 | *
34 | * @var array $capabilities
35 | */
36 | public $capabilities = null;
37 | /**
38 | * Variable parameters to accompany message request.
39 | *
40 | * @var array $variables
41 | */
42 | public $variables = null;
43 | /**
44 | * Fixed parameters to accompany message request.
45 | *
46 | * @var array $constants
47 | */
48 | public $constants = null;
49 |
50 |
51 | /**
52 | * Class constructor.
53 | *
54 | * @param string $type LTI message type
55 | * @param string $path Path to send message request to
56 | * @param array $capabilities Array of capabilities required by message
57 | * @param array $variables Array of variable parameters to accompany message request
58 | * @param array $constants Array of fixed parameters to accompany message request
59 | */
60 | function __construct($type, $path, $capabilities = array(), $variables = array(), $constants = array())
61 | {
62 |
63 | $this->type = $type;
64 | $this->path = $path;
65 | $this->capabilities = $capabilities;
66 | $this->variables = $variables;
67 | $this->constants = $constants;
68 |
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/Profile/ResourceHandler.php:
--------------------------------------------------------------------------------
1 |
9 | * @copyright IMS Global Learning Consortium Inc
10 | * @date 2016
11 | * @version 3.0.0
12 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
13 | */
14 |
15 | class ResourceHandler
16 | {
17 |
18 | /**
19 | * General details of resource handler.
20 | *
21 | * @var Item $item
22 | */
23 | public $item = null;
24 | /**
25 | * URL of icon.
26 | *
27 | * @var string $icon
28 | */
29 | public $icon = null;
30 | /**
31 | * Required Message objects for resource handler.
32 | *
33 | * @var array $requiredMessages
34 | */
35 | public $requiredMessages = null;
36 | /**
37 | * Optional Message objects for resource handler.
38 | *
39 | * @var array $optionalMessages
40 | */
41 | public $optionalMessages = null;
42 |
43 | /**
44 | * Class constructor.
45 | *
46 | * @param Item $item General details of resource handler
47 | * @param string $icon URL of icon
48 | * @param array $requiredMessages Array of required Message objects for resource handler
49 | * @param array $optionalMessages Array of optional Message objects for resource handler
50 | */
51 | function __construct($item, $icon, $requiredMessages, $optionalMessages)
52 | {
53 |
54 | $this->item = $item;
55 | $this->icon = $icon;
56 | $this->requiredMessages = $requiredMessages;
57 | $this->optionalMessages = $optionalMessages;
58 |
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/src/Profile/ServiceDefinition.php:
--------------------------------------------------------------------------------
1 |
9 | * @copyright IMS Global Learning Consortium Inc
10 | * @date 2016
11 | * @version 3.0.0
12 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
13 | */
14 |
15 | class ServiceDefinition
16 | {
17 |
18 | /**
19 | * Media types supported by service.
20 | *
21 | * @var array $formats
22 | */
23 | public $formats = null;
24 | /**
25 | * HTTP actions accepted by service.
26 | *
27 | * @var array $actions
28 | */
29 | public $actions = null;
30 | /**
31 | * ID of service.
32 | *
33 | * @var string $id
34 | */
35 | public $id = null;
36 | /**
37 | * URL for service requests.
38 | *
39 | * @var string $endpoint
40 | */
41 | public $endpoint = null;
42 |
43 | /**
44 | * Class constructor.
45 | *
46 | * @param array $formats Array of media types supported by service
47 | * @param array $actions Array of HTTP actions accepted by service
48 | * @param string $id ID of service (optional)
49 | * @param string $endpoint URL for service requests (optional)
50 | */
51 |
52 | function __construct($formats, $actions, $id = null, $endpoint = null)
53 | {
54 |
55 | $this->formats = $formats;
56 | $this->actions = $actions;
57 | $this->id = $id;
58 | $this->endpoint = $endpoint;
59 |
60 | }
61 |
62 | function setId($id) {
63 |
64 | $this->id = $id;
65 |
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/ToolProvider/ConsumerNonce.php:
--------------------------------------------------------------------------------
1 |
9 | * @copyright IMS Global Learning Consortium Inc
10 | * @date 2016
11 | * @version 3.0.2
12 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
13 | */
14 | class ConsumerNonce
15 | {
16 |
17 | /**
18 | * Maximum age nonce values will be retained for (in minutes).
19 | */
20 | const MAX_NONCE_AGE = 30; // in minutes
21 |
22 | /**
23 | * Date/time when the nonce value expires.
24 | *
25 | * @var int $expires
26 | */
27 | public $expires = null;
28 |
29 | /**
30 | * Tool Consumer to which this nonce applies.
31 | *
32 | * @var ToolConsumer $consumer
33 | */
34 | private $consumer = null;
35 | /**
36 | * Nonce value.
37 | *
38 | * @var string $value
39 | */
40 | private $value = null;
41 |
42 | /**
43 | * Class constructor.
44 | *
45 | * @param ToolConsumer $consumer Consumer object
46 | * @param string $value Nonce value (optional, default is null)
47 | */
48 | public function __construct($consumer, $value = null)
49 | {
50 |
51 | $this->consumer = $consumer;
52 | $this->value = $value;
53 | $this->expires = time() + (self::MAX_NONCE_AGE * 60);
54 |
55 | }
56 |
57 | /**
58 | * Load a nonce value from the database.
59 | *
60 | * @return boolean True if the nonce value was successfully loaded
61 | */
62 | public function load()
63 | {
64 |
65 | return $this->consumer->getDataConnector()->loadConsumerNonce($this);
66 |
67 | }
68 |
69 | /**
70 | * Save a nonce value in the database.
71 | *
72 | * @return boolean True if the nonce value was successfully saved
73 | */
74 | public function save()
75 | {
76 |
77 | return $this->consumer->getDataConnector()->saveConsumerNonce($this);
78 |
79 | }
80 |
81 | /**
82 | * Get tool consumer.
83 | *
84 | * @return ToolConsumer Consumer for this nonce
85 | */
86 | public function getConsumer()
87 | {
88 |
89 | return $this->consumer;
90 |
91 | }
92 |
93 | /**
94 | * Get outcome value.
95 | *
96 | * @return string Outcome value
97 | */
98 | public function getValue()
99 | {
100 |
101 | return $this->value;
102 |
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/src/ToolProvider/ContentItem.php:
--------------------------------------------------------------------------------
1 |
9 | * @copyright IMS Global Learning Consortium Inc
10 | * @date 2016
11 | * @version 3.0.2
12 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
13 | */
14 | class ContentItem
15 | {
16 |
17 | /**
18 | * Media type for LTI launch links.
19 | */
20 | const LTI_LINK_MEDIA_TYPE = 'application/vnd.ims.lti.v1.ltilink';
21 |
22 | /**
23 | * Class constructor.
24 | *
25 | * @param string $type Class type of content-item
26 | * @param ContentItemPlacement $placementAdvice Placement object for item (optional)
27 | * @param string $id URL of content-item (optional)
28 | */
29 | function __construct($type, $placementAdvice = null, $id = null)
30 | {
31 |
32 | $this->{'@type'} = $type;
33 | if (is_object($placementAdvice) && (count(get_object_vars($placementAdvice)) > 0)) {
34 | $this->placementAdvice = $placementAdvice;
35 | }
36 | if (!empty($id)) {
37 | $this->{'@id'} = $id;
38 | }
39 |
40 | }
41 |
42 | /**
43 | * Set a URL value for the content-item.
44 | *
45 | * @param string $url URL value
46 | */
47 | public function setUrl($url)
48 | {
49 |
50 | if (!empty($url)) {
51 | $this->url = $url;
52 | } else {
53 | unset($this->url);
54 | }
55 |
56 | }
57 |
58 | /**
59 | * Set a media type value for the content-item.
60 | *
61 | * @param string $mediaType Media type value
62 | */
63 | public function setMediaType($mediaType)
64 | {
65 |
66 | if (!empty($mediaType)) {
67 | $this->mediaType = $mediaType;
68 | } else {
69 | unset($this->mediaType);
70 | }
71 |
72 | }
73 |
74 | /**
75 | * Set a title value for the content-item.
76 | *
77 | * @param string $title Title value
78 | */
79 | public function setTitle($title)
80 | {
81 |
82 | if (!empty($title)) {
83 | $this->title = $title;
84 | } else if (isset($this->title)) {
85 | unset($this->title);
86 | }
87 |
88 | }
89 |
90 | /**
91 | * Set a link text value for the content-item.
92 | *
93 | * @param string $text Link text value
94 | */
95 | public function setText($text)
96 | {
97 |
98 | if (!empty($text)) {
99 | $this->text = $text;
100 | } else if (isset($this->text)) {
101 | unset($this->text);
102 | }
103 |
104 | }
105 |
106 | /**
107 | * Wrap the content items to form a complete application/vnd.ims.lti.v1.contentitems+json media type instance.
108 | *
109 | * @param mixed $items An array of content items or a single item
110 | * @return string
111 | */
112 | public static function toJson($items)
113 | {
114 | /*
115 | $data = array();
116 | if (!is_array($items)) {
117 | $data[] = json_encode($items);
118 | } else {
119 | foreach ($items as $item) {
120 | $data[] = json_encode($item);
121 | }
122 | }
123 | $json = '{ "@context" : "http://purl.imsglobal.org/ctx/lti/v1/ContentItem", "@graph" : [' . implode(", ", $data) . '] }';
124 | */
125 | $obj = new \stdClass();
126 | $obj->{'@context'} = 'http://purl.imsglobal.org/ctx/lti/v1/ContentItem';
127 | if (!is_array($items)) {
128 | $obj->{'@graph'} = array();
129 | $obj->{'@graph'}[] = $items;
130 | } else {
131 | $obj->{'@graph'} = $items;
132 | }
133 |
134 | return json_encode($obj);
135 |
136 | }
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/src/ToolProvider/ContentItemImage.php:
--------------------------------------------------------------------------------
1 |
9 | * @copyright IMS Global Learning Consortium Inc
10 | * @date 2016
11 | * @version 3.0.2
12 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
13 | */
14 | class ContentItemImage
15 | {
16 |
17 | /**
18 | * Class constructor.
19 | *
20 | * @param string $id URL of image
21 | * @param int $height Height of image in pixels (optional)
22 | * @param int $width Width of image in pixels (optional)
23 | */
24 | function __construct($id, $height = null, $width = null)
25 | {
26 |
27 | $this->{'@id'} = $id;
28 | if (!is_null($height)) {
29 | $this->height = $height;
30 | }
31 | if (!is_null($width)) {
32 | $this->width = $width;
33 | }
34 |
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/ToolProvider/ContentItemPlacement.php:
--------------------------------------------------------------------------------
1 |
9 | * @copyright IMS Global Learning Consortium Inc
10 | * @date 2016
11 | * @version 3.0.2
12 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
13 | */
14 | class ContentItemPlacement
15 | {
16 |
17 | /**
18 | * Class constructor.
19 | *
20 | * @param int $displayWidth Width of item location
21 | * @param int $displayHeight Height of item location
22 | * @param string $documentTarget Location to open content in
23 | * @param string $windowTarget Name of window target
24 | */
25 | function __construct($displayWidth, $displayHeight, $documentTarget, $windowTarget)
26 | {
27 |
28 | if (!empty($displayWidth)) {
29 | $this->displayWidth = $displayWidth;
30 | }
31 | if (!empty($displayHeight)) {
32 | $this->displayHeight = $displayHeight;
33 | }
34 | if (!empty($documentTarget)) {
35 | $this->documentTarget = $documentTarget;
36 | }
37 | if (!empty($windowTarget)) {
38 | $this->windowTarget = $windowTarget;
39 | }
40 |
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/ToolProvider/Context.php:
--------------------------------------------------------------------------------
1 |
12 | * @copyright IMS Global Learning Consortium Inc
13 | * @date 2016
14 | * @version 3.0.2
15 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
16 | */
17 | class Context
18 | {
19 |
20 | /**
21 | * Context ID as supplied in the last connection request.
22 | *
23 | * @var string $ltiContextId
24 | */
25 | public $ltiContextId = null;
26 | /**
27 | * Context title.
28 | *
29 | * @var string $title
30 | */
31 | public $title = null;
32 | /**
33 | * Setting values (LTI parameters, custom parameters and local parameters).
34 | *
35 | * @var array $settings
36 | */
37 | public $settings = null;
38 | /**
39 | * Date/time when the object was created.
40 | *
41 | * @var int $created
42 | */
43 | public $created = null;
44 | /**
45 | * Date/time when the object was last updated.
46 | *
47 | * @var int $updated
48 | */
49 | public $updated = null;
50 |
51 | /**
52 | * Tool Consumer for this context.
53 | *
54 | * @var ToolConsumer $consumer
55 | */
56 | private $consumer = null;
57 | /**
58 | * Tool Consumer ID for this context.
59 | *
60 | * @var int $consumerId
61 | */
62 | private $consumerId = null;
63 | /**
64 | * ID for this context.
65 | *
66 | * @var int $id
67 | */
68 | private $id = null;
69 | /**
70 | * Whether the settings value have changed since last saved.
71 | *
72 | * @var boolean $settingsChanged
73 | */
74 | private $settingsChanged = false;
75 | /**
76 | * Data connector object or string.
77 | *
78 | * @var mixed $dataConnector
79 | */
80 | private $dataConnector = null;
81 |
82 | /**
83 | * Class constructor.
84 | */
85 | public function __construct()
86 | {
87 |
88 | $this->initialize();
89 |
90 | }
91 |
92 | /**
93 | * Initialise the context.
94 | */
95 | public function initialize()
96 | {
97 |
98 | $this->title = '';
99 | $this->settings = array();
100 | $this->created = null;
101 | $this->updated = null;
102 |
103 | }
104 |
105 | /**
106 | * Initialise the context.
107 | *
108 | * Pseudonym for initialize().
109 | */
110 | public function initialise()
111 | {
112 |
113 | $this->initialize();
114 |
115 | }
116 |
117 | /**
118 | * Save the context to the database.
119 | *
120 | * @return boolean True if the context was successfully saved.
121 | */
122 | public function save()
123 | {
124 |
125 | $ok = $this->getDataConnector()->saveContext($this);
126 | if ($ok) {
127 | $this->settingsChanged = false;
128 | }
129 |
130 | return $ok;
131 |
132 | }
133 |
134 | /**
135 | * Delete the context from the database.
136 | *
137 | * @return boolean True if the context was successfully deleted.
138 | */
139 | public function delete()
140 | {
141 |
142 | return $this->getDataConnector()->deleteContext($this);
143 |
144 | }
145 |
146 | /**
147 | * Get tool consumer.
148 | *
149 | * @return ToolConsumer Tool consumer object for this context.
150 | */
151 | public function getConsumer()
152 | {
153 |
154 | if (is_null($this->consumer)) {
155 | $this->consumer = ToolConsumer::fromRecordId($this->consumerId, $this->getDataConnector());
156 | }
157 |
158 | return $this->consumer;
159 |
160 | }
161 | /**
162 | * Set tool consumer ID.
163 | *
164 | * @param int $consumerId Tool Consumer ID for this resource link.
165 | */
166 | public function setConsumerId($consumerId)
167 | {
168 |
169 | $this->consumer = null;
170 | $this->consumerId = $consumerId;
171 |
172 | }
173 |
174 | /**
175 | * Get tool consumer key.
176 | *
177 | * @return string Consumer key value for this context.
178 | */
179 | public function getKey()
180 | {
181 |
182 | return $this->getConsumer()->getKey();
183 |
184 | }
185 |
186 | /**
187 | * Get context ID.
188 | *
189 | * @return string ID for this context.
190 | */
191 | public function getId()
192 | {
193 |
194 | return $this->ltiContextId;
195 |
196 | }
197 |
198 | /**
199 | * Get the context record ID.
200 | *
201 | * @return int Context record ID value
202 | */
203 | public function getRecordId()
204 | {
205 |
206 | return $this->id;
207 |
208 | }
209 |
210 | /**
211 | * Sets the context record ID.
212 | *
213 | * @return int $id Context record ID value
214 | */
215 | public function setRecordId($id)
216 | {
217 |
218 | $this->id = $id;
219 |
220 | }
221 |
222 | /**
223 | * Get the data connector.
224 | *
225 | * @return mixed Data connector object or string
226 | */
227 | public function getDataConnector()
228 | {
229 |
230 | return $this->dataConnector;
231 |
232 | }
233 |
234 | /**
235 | * Get a setting value.
236 | *
237 | * @param string $name Name of setting
238 | * @param string $default Value to return if the setting does not exist (optional, default is an empty string)
239 | *
240 | * @return string Setting value
241 | */
242 | public function getSetting($name, $default = '')
243 | {
244 |
245 | if (array_key_exists($name, $this->settings)) {
246 | $value = $this->settings[$name];
247 | } else {
248 | $value = $default;
249 | }
250 |
251 | return $value;
252 |
253 | }
254 |
255 | /**
256 | * Set a setting value.
257 | *
258 | * @param string $name Name of setting
259 | * @param string $value Value to set, use an empty value to delete a setting (optional, default is null)
260 | */
261 | public function setSetting($name, $value = null)
262 | {
263 |
264 | $old_value = $this->getSetting($name);
265 | if ($value !== $old_value) {
266 | if (!empty($value)) {
267 | $this->settings[$name] = $value;
268 | } else {
269 | unset($this->settings[$name]);
270 | }
271 | $this->settingsChanged = true;
272 | }
273 |
274 | }
275 |
276 | /**
277 | * Get an array of all setting values.
278 | *
279 | * @return array Associative array of setting values
280 | */
281 | public function getSettings()
282 | {
283 |
284 | return $this->settings;
285 |
286 | }
287 |
288 | /**
289 | * Set an array of all setting values.
290 | *
291 | * @param array $settings Associative array of setting values
292 | */
293 | public function setSettings($settings)
294 | {
295 |
296 | $this->settings = $settings;
297 |
298 | }
299 |
300 | /**
301 | * Save setting values.
302 | *
303 | * @return boolean True if the settings were successfully saved
304 | */
305 | public function saveSettings()
306 | {
307 |
308 | if ($this->settingsChanged) {
309 | $ok = $this->save();
310 | } else {
311 | $ok = true;
312 | }
313 |
314 | return $ok;
315 |
316 | }
317 |
318 | /**
319 | * Check if the Tool Settings service is supported.
320 | *
321 | * @return boolean True if this context supports the Tool Settings service
322 | */
323 | public function hasToolSettingsService()
324 | {
325 |
326 | $url = $this->getSetting('custom_context_setting_url');
327 |
328 | return !empty($url);
329 |
330 | }
331 |
332 | /**
333 | * Get Tool Settings.
334 | *
335 | * @param int $mode Mode for request (optional, default is current level only)
336 | * @param boolean $simple True if all the simple media type is to be used (optional, default is true)
337 | *
338 | * @return mixed The array of settings if successful, otherwise false
339 | */
340 | public function getToolSettings($mode = Service\ToolSettings::MODE_CURRENT_LEVEL, $simple = true)
341 | {
342 |
343 | $url = $this->getSetting('custom_context_setting_url');
344 | $service = new Service\ToolSettings($this, $url, $simple);
345 | $response = $service->get($mode);
346 |
347 | return $response;
348 |
349 | }
350 |
351 | /**
352 | * Perform a Tool Settings service request.
353 | *
354 | * @param array $settings An associative array of settings (optional, default is none)
355 | *
356 | * @return boolean True if action was successful, otherwise false
357 | */
358 | public function setToolSettings($settings = array())
359 | {
360 |
361 | $url = $this->getSetting('custom_context_setting_url');
362 | $service = new Service\ToolSettings($this, $url);
363 | $response = $service->set($settings);
364 |
365 | return $response;
366 |
367 | }
368 |
369 | /**
370 | * Check if the Membership service is supported.
371 | *
372 | * @return boolean True if this context supports the Membership service
373 | */
374 | public function hasMembershipService()
375 | {
376 |
377 | $url = $this->getSetting('custom_context_memberships_url');
378 |
379 | return !empty($url);
380 |
381 | }
382 |
383 | /**
384 | * Get Memberships.
385 | *
386 | * @return mixed The array of User objects if successful, otherwise false
387 | */
388 | public function getMembership()
389 | {
390 |
391 | $url = $this->getSetting('custom_context_memberships_url');
392 | $service = new Service\Membership($this, $url);
393 | $response = $service->get();
394 |
395 | return $response;
396 |
397 | }
398 |
399 | /**
400 | * Load the context from the database.
401 | *
402 | * @param int $id Record ID of context
403 | * @param DataConnector $dataConnector Database connection object
404 | *
405 | * @return Context Context object
406 | */
407 | public static function fromRecordId($id, $dataConnector)
408 | {
409 |
410 | $context = new Context();
411 | $context->dataConnector = $dataConnector;
412 | $context->load($id);
413 |
414 | return $context;
415 |
416 | }
417 |
418 | /**
419 | * Class constructor from consumer.
420 | *
421 | * @param ToolConsumer $consumer Consumer instance
422 | * @param string $ltiContextId LTI Context ID value
423 | * @return Context
424 | */
425 | public static function fromConsumer($consumer, $ltiContextId)
426 | {
427 |
428 | $context = new Context();
429 | $context->consumer = $consumer;
430 | $context->dataConnector = $consumer->getDataConnector();
431 | $context->ltiContextId = $ltiContextId;
432 | if (!empty($ltiContextId)) {
433 | $context->load();
434 | }
435 |
436 | return $context;
437 |
438 | }
439 |
440 | ###
441 | ### PRIVATE METHODS
442 | ###
443 |
444 | /**
445 | * Load the context from the database.
446 | *
447 | * @param int $id Record ID of context (optional, default is null)
448 | *
449 | * @return boolean True if context was successfully loaded
450 | */
451 | private function load($id = null)
452 | {
453 |
454 | $this->initialize();
455 | $this->id = $id;
456 | return $this->getDataConnector()->loadContext($this);
457 |
458 | }
459 |
460 | }
461 |
--------------------------------------------------------------------------------
/src/ToolProvider/DataConnector/DataConnector.php:
--------------------------------------------------------------------------------
1 |
20 | * @copyright IMS Global Learning Consortium Inc
21 | * @date 2016
22 | * @version 3.0.0
23 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
24 | */
25 | class DataConnector
26 | {
27 |
28 | /**
29 | * Default name for database table used to store tool consumers.
30 | */
31 | const CONSUMER_TABLE_NAME = 'lti2_consumer';
32 | /**
33 | * Default name for database table used to store pending tool proxies.
34 | */
35 | const TOOL_PROXY_TABLE_NAME = 'lti2_tool_proxy';
36 | /**
37 | * Default name for database table used to store contexts.
38 | */
39 | const CONTEXT_TABLE_NAME = 'lti2_context';
40 | /**
41 | * Default name for database table used to store resource links.
42 | */
43 | const RESOURCE_LINK_TABLE_NAME = 'lti2_resource_link';
44 | /**
45 | * Default name for database table used to store users.
46 | */
47 | const USER_RESULT_TABLE_NAME = 'lti2_user_result';
48 | /**
49 | * Default name for database table used to store resource link share keys.
50 | */
51 | const RESOURCE_LINK_SHARE_KEY_TABLE_NAME = 'lti2_share_key';
52 | /**
53 | * Default name for database table used to store nonce values.
54 | */
55 | const NONCE_TABLE_NAME = 'lti2_nonce';
56 |
57 | /**
58 | * Database object.
59 | *
60 | * @var object $db
61 | */
62 | protected $db = null;
63 | /**
64 | * Prefix for database table names.
65 | *
66 | * @var string $dbTableNamePrefix
67 | */
68 | protected $dbTableNamePrefix = '';
69 | /**
70 | * SQL date format (default = 'Y-m-d')
71 | *
72 | * @var string $dateFormat
73 | */
74 | protected $dateFormat = 'Y-m-d';
75 | /**
76 | * SQL time format (default = 'H:i:s')
77 | *
78 | * @var string $timeFormat
79 | */
80 | protected $timeFormat = 'H:i:s';
81 |
82 | /**
83 | * Class constructor
84 | *
85 | * @param object $db Database connection object
86 | * @param string $dbTableNamePrefix Prefix for database table names (optional, default is none)
87 | */
88 | public function __construct($db, $dbTableNamePrefix = '')
89 | {
90 |
91 | $this->db = $db;
92 | $this->dbTableNamePrefix = $dbTableNamePrefix;
93 |
94 | }
95 |
96 | ###
97 | ### ToolConsumer methods
98 | ###
99 |
100 | /**
101 | * Load tool consumer object.
102 | *
103 | * @param ToolConsumer $consumer ToolConsumer object
104 | *
105 | * @return boolean True if the tool consumer object was successfully loaded
106 | */
107 | public function loadToolConsumer($consumer)
108 | {
109 |
110 | $consumer->secret = 'secret';
111 | $consumer->enabled = true;
112 | $now = time();
113 | $consumer->created = $now;
114 | $consumer->updated = $now;
115 |
116 | return true;
117 |
118 | }
119 |
120 | /**
121 | * Save tool consumer object.
122 | *
123 | * @param ToolConsumer $consumer Consumer object
124 | *
125 | * @return boolean True if the tool consumer object was successfully saved
126 | */
127 | public function saveToolConsumer($consumer)
128 | {
129 |
130 | $consumer->updated = time();
131 |
132 | return true;
133 |
134 | }
135 |
136 | /**
137 | * Delete tool consumer object.
138 | *
139 | * @param ToolConsumer $consumer Consumer object
140 | *
141 | * @return boolean True if the tool consumer object was successfully deleted
142 | */
143 | public function deleteToolConsumer($consumer)
144 | {
145 |
146 | $consumer->initialize();
147 |
148 | return true;
149 |
150 | }
151 |
152 | /**
153 | * Load tool consumer objects.
154 | *
155 | * @return array Array of all defined ToolConsumer objects
156 | */
157 | public function getToolConsumers()
158 | {
159 |
160 | return array();
161 |
162 | }
163 |
164 |
165 | ###
166 | ### ToolProxy methods
167 | ###
168 |
169 | /**
170 | * Load tool proxy object.
171 | *
172 | * @param ToolProxy $toolProxy ToolProxy object
173 | *
174 | * @return boolean True if the tool proxy object was successfully loaded
175 | */
176 | public function loadToolProxy($toolProxy)
177 | {
178 |
179 | $now = time();
180 | $toolProxy->created = $now;
181 | $toolProxy->updated = $now;
182 |
183 | return true;
184 |
185 | }
186 |
187 | /**
188 | * Save tool proxy object.
189 | *
190 | * @param ToolProxy $toolProxy ToolProxy object
191 | *
192 | * @return boolean True if the tool proxy object was successfully saved
193 | */
194 | public function saveToolProxy($toolProxy)
195 | {
196 |
197 | $toolProxy->updated = time();
198 |
199 | return true;
200 |
201 | }
202 |
203 | /**
204 | * Delete tool proxy object.
205 | *
206 | * @param ToolProxy $toolProxy ToolProxy object
207 | *
208 | * @return boolean True if the tool proxy object was successfully deleted
209 | */
210 | public function deleteToolProxy($toolProxy)
211 | {
212 |
213 | $toolProxy->initialize();
214 |
215 | return true;
216 |
217 | }
218 |
219 | ###
220 | ### Context methods
221 | ###
222 |
223 | /**
224 | * Load context object.
225 | *
226 | * @param Context $context Context object
227 | *
228 | * @return boolean True if the context object was successfully loaded
229 | */
230 | public function loadContext($context)
231 | {
232 |
233 | $now = time();
234 | $context->created = $now;
235 | $context->updated = $now;
236 |
237 | return true;
238 |
239 | }
240 |
241 | /**
242 | * Save context object.
243 | *
244 | * @param Context $context Context object
245 | *
246 | * @return boolean True if the context object was successfully saved
247 | */
248 | public function saveContext($context)
249 | {
250 |
251 | $context->updated = time();
252 |
253 | return true;
254 |
255 | }
256 |
257 | /**
258 | * Delete context object.
259 | *
260 | * @param Context $context Context object
261 | *
262 | * @return boolean True if the Context object was successfully deleted
263 | */
264 | public function deleteContext($context)
265 | {
266 |
267 | $context->initialize();
268 |
269 | return true;
270 |
271 | }
272 |
273 | ###
274 | ### ResourceLink methods
275 | ###
276 |
277 | /**
278 | * Load resource link object.
279 | *
280 | * @param ResourceLink $resourceLink Resource_Link object
281 | *
282 | * @return boolean True if the resource link object was successfully loaded
283 | */
284 | public function loadResourceLink($resourceLink)
285 | {
286 |
287 | $now = time();
288 | $resourceLink->created = $now;
289 | $resourceLink->updated = $now;
290 |
291 | return true;
292 |
293 | }
294 |
295 | /**
296 | * Save resource link object.
297 | *
298 | * @param ResourceLink $resourceLink Resource_Link object
299 | *
300 | * @return boolean True if the resource link object was successfully saved
301 | */
302 | public function saveResourceLink($resourceLink)
303 | {
304 |
305 | $resourceLink->updated = time();
306 |
307 | return true;
308 |
309 | }
310 |
311 | /**
312 | * Delete resource link object.
313 | *
314 | * @param ResourceLink $resourceLink Resource_Link object
315 | *
316 | * @return boolean True if the resource link object was successfully deleted
317 | */
318 | public function deleteResourceLink($resourceLink)
319 | {
320 |
321 | $resourceLink->initialize();
322 |
323 | return true;
324 |
325 | }
326 |
327 | /**
328 | * Get array of user objects.
329 | *
330 | * Obtain an array of User objects for users with a result sourcedId. The array may include users from other
331 | * resource links which are sharing this resource link. It may also be optionally indexed by the user ID of a specified scope.
332 | *
333 | * @param ResourceLink $resourceLink Resource link object
334 | * @param boolean $localOnly True if only users within the resource link are to be returned (excluding users sharing this resource link)
335 | * @param int $idScope Scope value to use for user IDs
336 | *
337 | * @return array Array of User objects
338 | */
339 | public function getUserResultSourcedIDsResourceLink($resourceLink, $localOnly, $idScope)
340 | {
341 |
342 | return array();
343 |
344 | }
345 |
346 | /**
347 | * Get array of shares defined for this resource link.
348 | *
349 | * @param ResourceLink $resourceLink Resource_Link object
350 | *
351 | * @return array Array of ResourceLinkShare objects
352 | */
353 | public function getSharesResourceLink($resourceLink)
354 | {
355 |
356 | return array();
357 |
358 | }
359 |
360 | ###
361 | ### ConsumerNonce methods
362 | ###
363 |
364 | /**
365 | * Load nonce object.
366 | *
367 | * @param ConsumerNonce $nonce Nonce object
368 | *
369 | * @return boolean True if the nonce object was successfully loaded
370 | */
371 | public function loadConsumerNonce($nonce)
372 | {
373 | return false; // assume the nonce does not already exist
374 |
375 | }
376 |
377 | /**
378 | * Save nonce object.
379 | *
380 | * @param ConsumerNonce $nonce Nonce object
381 | *
382 | * @return boolean True if the nonce object was successfully saved
383 | */
384 | public function saveConsumerNonce($nonce)
385 | {
386 |
387 | return true;
388 |
389 | }
390 |
391 | ###
392 | ### ResourceLinkShareKey methods
393 | ###
394 |
395 | /**
396 | * Load resource link share key object.
397 | *
398 | * @param ResourceLinkShareKey $shareKey Resource_Link share key object
399 | *
400 | * @return boolean True if the resource link share key object was successfully loaded
401 | */
402 | public function loadResourceLinkShareKey($shareKey)
403 | {
404 |
405 | return true;
406 |
407 | }
408 |
409 | /**
410 | * Save resource link share key object.
411 | *
412 | * @param ResourceLinkShareKey $shareKey Resource link share key object
413 | *
414 | * @return boolean True if the resource link share key object was successfully saved
415 | */
416 | public function saveResourceLinkShareKey($shareKey)
417 | {
418 |
419 | return true;
420 |
421 | }
422 |
423 | /**
424 | * Delete resource link share key object.
425 | *
426 | * @param ResourceLinkShareKey $shareKey Resource link share key object
427 | *
428 | * @return boolean True if the resource link share key object was successfully deleted
429 | */
430 | public function deleteResourceLinkShareKey($shareKey)
431 | {
432 |
433 | return true;
434 |
435 | }
436 |
437 | ###
438 | ### User methods
439 | ###
440 |
441 | /**
442 | * Load user object.
443 | *
444 | * @param User $user User object
445 | *
446 | * @return boolean True if the user object was successfully loaded
447 | */
448 | public function loadUser($user)
449 | {
450 |
451 | $now = time();
452 | $user->created = $now;
453 | $user->updated = $now;
454 |
455 | return true;
456 |
457 | }
458 |
459 | /**
460 | * Save user object.
461 | *
462 | * @param User $user User object
463 | *
464 | * @return boolean True if the user object was successfully saved
465 | */
466 | public function saveUser($user)
467 | {
468 |
469 | $user->updated = time();
470 |
471 | return true;
472 |
473 | }
474 |
475 | /**
476 | * Delete user object.
477 | *
478 | * @param User $user User object
479 | *
480 | * @return boolean True if the user object was successfully deleted
481 | */
482 | public function deleteUser($user)
483 | {
484 |
485 | $user->initialize();
486 |
487 | return true;
488 |
489 | }
490 |
491 | ###
492 | ### Other methods
493 | ###
494 |
495 | /**
496 | * Return a hash of a consumer key for values longer than 255 characters.
497 | *
498 | * @param string $key
499 | * @return string
500 | */
501 | protected static function getConsumerKey($key)
502 | {
503 |
504 | $len = strlen($key);
505 | if ($len > 255) {
506 | $key = 'sha512:' . hash('sha512', $key);
507 | }
508 |
509 | return $key;
510 |
511 | }
512 |
513 | /**
514 | * Create data connector object.
515 | *
516 | * A data connector provides access to persistent storage for the different objects.
517 | *
518 | * Names of tables may be given a prefix to allow multiple versions to share the same schema. A separate sub-class is defined for
519 | * each different database connection - the class to use is determined by inspecting the database object passed, but this can be overridden
520 | * (for example, to use a bespoke connector) by specifying a type. If no database is passed then this class is used which acts as a dummy
521 | * connector with no persistence.
522 | *
523 | * @param string $dbTableNamePrefix Prefix for database table names (optional, default is none)
524 | * @param object $db A database connection object or string (optional, default is no persistence)
525 | * @param string $type The type of data connector (optional, default is based on $db parameter)
526 | *
527 | * @return DataConnector Data connector object
528 | */
529 | public static function getDataConnector($dbTableNamePrefix = '', $db = null, $type = '')
530 | {
531 |
532 | if (is_null($dbTableNamePrefix)) {
533 | $dbTableNamePrefix = '';
534 | }
535 | if (!is_null($db) && empty($type)) {
536 | if (is_object($db)) {
537 | $type = get_class($db);
538 | }
539 | }
540 | $type = strtolower($type);
541 | if (($type === 'pdo') && ($db->getAttribute(PDO::ATTR_DRIVER_NAME) === 'sqlite')) {
542 | $type .= '_sqlite';
543 | }
544 | if (!empty($type)) {
545 | $type ="DataConnector_{$type}";
546 | } else {
547 | $type ='DataConnector';
548 | }
549 | $type = "\\IMSGlobal\\LTI\\ToolProvider\\DataConnector\\{$type}";
550 | $dataConnector = new $type($db, $dbTableNamePrefix);
551 |
552 | return $dataConnector;
553 |
554 | }
555 |
556 | /**
557 | * Generate a random string.
558 | *
559 | * The generated string will only comprise letters (upper- and lower-case) and digits.
560 | *
561 | * @param int $length Length of string to be generated (optional, default is 8 characters)
562 | *
563 | * @return string Random string
564 | */
565 | static function getRandomString($length = 8)
566 | {
567 |
568 | $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
569 |
570 | $value = '';
571 | $charsLength = strlen($chars) - 1;
572 |
573 | for ($i = 1 ; $i <= $length; $i++) {
574 | $value .= $chars[rand(0, $charsLength)];
575 | }
576 |
577 | return $value;
578 |
579 | }
580 |
581 | /**
582 | * Quote a string for use in a database query.
583 | *
584 | * Any single quotes in the value passed will be replaced with two single quotes. If a null value is passed, a string
585 | * of 'null' is returned (which will never be enclosed in quotes irrespective of the value of the $addQuotes parameter.
586 | *
587 | * @param string $value Value to be quoted
588 | * @param bool $addQuotes If true the returned string will be enclosed in single quotes (optional, default is true)
589 | * @return string The quoted string.
590 | */
591 | static function quoted($value, $addQuotes = true)
592 | {
593 |
594 | if (is_null($value)) {
595 | $value = 'null';
596 | } else {
597 | $value = str_replace('\'', '\'\'', $value);
598 | if ($addQuotes) {
599 | $value = "'{$value}'";
600 | }
601 | }
602 |
603 | return $value;
604 |
605 | }
606 |
607 | }
608 |
--------------------------------------------------------------------------------
/src/ToolProvider/DataConnector/DataConnector_pdo_sqlite.php:
--------------------------------------------------------------------------------
1 |
14 | * @copyright IMS Global Learning Consortium Inc
15 | * @date 2016
16 | * @version 3.0.0
17 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
18 | */
19 |
20 |
21 | class DataConnector_pdo_sqlite extends DataConnector_pdo
22 | {
23 |
24 | ###
25 | ### ToolConsumer methods
26 | ###
27 |
28 | /**
29 | * Delete tool consumer object.
30 | *
31 | * @param ToolConsumer $consumer Consumer object
32 | *
33 | * @return boolean True if the tool consumer object was successfully deleted
34 | */
35 | public function deleteToolConsumer($consumer)
36 | {
37 |
38 | $id = $consumer->getRecordId();
39 |
40 | // Delete any nonce values for this consumer
41 | $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::NONCE_TABLE_NAME . ' WHERE consumer_pk = :id';
42 | $query = $this->db->prepare($sql);
43 | $query->bindValue('id', $id, PDO::PARAM_INT);
44 | $query->execute();
45 |
46 | // Delete any outstanding share keys for resource links for this consumer
47 | $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_SHARE_KEY_TABLE_NAME . ' ' .
48 | "WHERE EXISTS (SELECT * FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ' .
49 | "WHERE ({$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_SHARE_KEY_TABLE_NAME . '.resource_link_pk = rl.resource_link_pk) AND (rl.consumer_pk = :id))';
50 | $query = $this->db->prepare($sql);
51 | $query->bindValue('id', $id, PDO::PARAM_INT);
52 | $query->execute();
53 |
54 | // Delete any outstanding share keys for resource links for contexts in this consumer
55 | $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_SHARE_KEY_TABLE_NAME . ' ' .
56 | "WHERE EXISTS (SELECT * FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ' .
57 | "INNER JOIN {$this->dbTableNamePrefix}" . DataConnector::CONTEXT_TABLE_NAME . ' c ON rl.context_pk = c.context_pk ' .
58 | "WHERE ({$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_SHARE_KEY_TABLE_NAME . '.resource_link_pk = rl.resource_link_pk) AND (c.consumer_pk = :id))';
59 | $query = $this->db->prepare($sql);
60 | $query->bindValue('id', $id, PDO::PARAM_INT);
61 | $query->execute();
62 |
63 | // Delete any users in resource links for this consumer
64 | $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::USER_RESULT_TABLE_NAME . ' ' .
65 | "WHERE EXISTS (SELECT * FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ' .
66 | "WHERE ({$this->dbTableNamePrefix}" . DataConnector::USER_RESULT_TABLE_NAME . '.resource_link_pk = rl.resource_link_pk) AND (rl.consumer_pk = :id))';
67 | $query = $this->db->prepare($sql);
68 | $query->bindValue('id', $id, PDO::PARAM_INT);
69 | $query->execute();
70 |
71 | // Delete any users in resource links for contexts in this consumer
72 | $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::USER_RESULT_TABLE_NAME . ' ' .
73 | "WHERE EXISTS (SELECT * FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ' .
74 | "INNER JOIN {$this->dbTableNamePrefix}" . DataConnector::CONTEXT_TABLE_NAME . ' c ON rl.context_pk = c.context_pk ' .
75 | "WHERE ({$this->dbTableNamePrefix}" . DataConnector::USER_RESULT_TABLE_NAME . '.resource_link_pk = rl.resource_link_pk) AND (c.consumer_pk = :id))';
76 | $query = $this->db->prepare($sql);
77 | $query->bindValue('id', $id, PDO::PARAM_INT);
78 | $query->execute();
79 |
80 | // Update any resource links for which this consumer is acting as a primary resource link
81 | $sql = "UPDATE {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' ' .
82 | 'SET primary_resource_link_pk = NULL, share_approved = NULL ' .
83 | "WHERE EXISTS (SELECT * FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ' .
84 | "WHERE ({$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . '.primary_resource_link_pk = rl.resource_link_pk) AND (rl.consumer_pk = :id))';
85 | $query = $this->db->prepare($sql);
86 | $query->bindValue('id', $id, PDO::PARAM_INT);
87 | $query->execute();
88 |
89 | // Update any resource links for contexts in which this consumer is acting as a primary resource link
90 | $sql = "UPDATE {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' ' .
91 | 'SET primary_resource_link_pk = NULL, share_approved = NULL ' .
92 | "WHERE EXISTS (SELECT * FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ' .
93 | "INNER JOIN {$this->dbTableNamePrefix}" . DataConnector::CONTEXT_TABLE_NAME . ' c ON rl.context_pk = c.context_pk ' .
94 | "WHERE ({$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . '.primary_resource_link_pk = rl.resource_link_pk) AND (c.consumer_pk = :id))';
95 | $query = $this->db->prepare($sql);
96 | $query->bindValue('id', $id, PDO::PARAM_INT);
97 | $query->execute();
98 |
99 | // Delete any resource links for this consumer
100 | $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' ' .
101 | 'WHERE consumer_pk = :id';
102 | $query = $this->db->prepare($sql);
103 | $query->bindValue('id', $id, PDO::PARAM_INT);
104 | $query->execute();
105 |
106 | // Delete any resource links for contexts in this consumer
107 | $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' ' .
108 | "WHERE EXISTS (SELECT * FROM {$this->dbTableNamePrefix}" . DataConnector::CONTEXT_TABLE_NAME . ' c ' .
109 | "WHERE ({$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . '.context_pk = c.context_pk) AND (c.consumer_pk = :id))';
110 | $query = $this->db->prepare($sql);
111 | $query->bindValue('id', $id, PDO::PARAM_INT);
112 | $query->execute();
113 |
114 | // Delete any contexts for this consumer
115 | $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::CONTEXT_TABLE_NAME . ' ' .
116 | 'WHERE consumer_pk = :id';
117 | $query = $this->db->prepare($sql);
118 | $query->bindValue('id', $id, PDO::PARAM_INT);
119 | $query->execute();
120 |
121 | // Delete consumer
122 | $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::CONSUMER_TABLE_NAME . ' ' .
123 | 'WHERE consumer_pk = :id';
124 | $query = $this->db->prepare($sql);
125 | $query->bindValue('id', $id, PDO::PARAM_INT);
126 | $ok = $query->execute();
127 |
128 | if ($ok) {
129 | $consumer->initialize();
130 | }
131 |
132 | return $ok;
133 |
134 | }
135 |
136 | ###
137 | ### Context methods
138 | ###
139 |
140 | /**
141 | * Delete context object.
142 | *
143 | * @param Context $context Context object
144 | *
145 | * @return boolean True if the Context object was successfully deleted
146 | */
147 | public function deleteContext($context)
148 | {
149 |
150 | $id = $context->getRecordId();
151 |
152 | // Delete any outstanding share keys for resource links for this context
153 | $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_SHARE_KEY_TABLE_NAME . ' ' .
154 | "WHERE EXISTS (SELECT * FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ' .
155 | "WHERE ({$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_SHARE_KEY_TABLE_NAME . '.resource_link_pk = rl.resource_link_pk) AND (rl.context_pk = :id))';
156 | $query = $this->db->prepare($sql);
157 | $query->bindValue('id', $id, PDO::PARAM_INT);
158 | $query->execute();
159 |
160 | // Delete any users in resource links for this context
161 | $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::USER_RESULT_TABLE_NAME . ' ' .
162 | "WHERE EXISTS (SELECT * FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ' .
163 | "WHERE ({$this->dbTableNamePrefix}" . DataConnector::USER_RESULT_TABLE_NAME . '.resource_link_pk = rl.resource_link_pk) AND (rl.context_pk = :id))';
164 | $query = $this->db->prepare($sql);
165 | $query->bindValue('id', $id, PDO::PARAM_INT);
166 | $query->execute();
167 |
168 | // Update any resource links for which this consumer is acting as a primary resource link
169 | $sql = "UPDATE {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' ' .
170 | 'SET primary_resource_link_pk = null, share_approved = null ' .
171 | "WHERE EXISTS (SELECT * FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ' .
172 | "WHERE ({$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . '.primary_resource_link_pk = rl.resource_link_pk) AND (rl.context_pk = :id))';
173 | $query = $this->db->prepare($sql);
174 | $query->bindValue('id', $id, PDO::PARAM_INT);
175 | $query->execute();
176 |
177 | // Delete any resource links for this consumer
178 | $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' ' .
179 | 'WHERE context_pk = :id';
180 | $query = $this->db->prepare($sql);
181 | $query->bindValue('id', $id, PDO::PARAM_INT);
182 | $query->execute();
183 |
184 | // Delete context
185 | $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::CONTEXT_TABLE_NAME . ' ' .
186 | 'WHERE context_pk = :id';
187 | $query = $this->db->prepare($sql);
188 | $query->bindValue('id', $id, PDO::PARAM_INT);
189 | $ok = $query->execute();
190 |
191 | if ($ok) {
192 | $context->initialize();
193 | }
194 |
195 | return $ok;
196 |
197 | }
198 |
199 | }
200 |
--------------------------------------------------------------------------------
/src/ToolProvider/MediaType/Message.php:
--------------------------------------------------------------------------------
1 |
9 | * @copyright IMS Global Learning Consortium Inc
10 | * @date 2016
11 | * @version 3.0.0
12 | * @license GNU Lesser General Public License, version 3 ()
13 | */
14 | class Message
15 | {
16 |
17 | /**
18 | * Class constructor.
19 | *
20 | * @param Message $message Message object
21 | * @param array $capabilitiesOffered Capabilities offered
22 | */
23 | function __construct($message, $capabilitiesOffered)
24 | {
25 |
26 | $this->message_type = $message->type;
27 | $this->path = $message->path;
28 | $this->enabled_capability = array();
29 | foreach ($message->capabilities as $capability) {
30 | if (in_array($capability, $capabilitiesOffered)) {
31 | $this->enabled_capability[] = $capability;
32 | }
33 | }
34 | $this->parameter = array();
35 | foreach ($message->constants as $name => $value) {
36 | $parameter = new \stdClass;
37 | $parameter->name = $name;
38 | $parameter->fixed = $value;
39 | $this->parameter[] = $parameter;
40 | }
41 | foreach ($message->variables as $name => $value) {
42 | if (in_array($value, $capabilitiesOffered)) {
43 | $parameter = new \stdClass;
44 | $parameter->name = $name;
45 | $parameter->variable = $value;
46 | $this->parameter[] = $parameter;
47 | }
48 | }
49 |
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/ToolProvider/MediaType/ResourceHandler.php:
--------------------------------------------------------------------------------
1 |
11 | * @copyright IMS Global Learning Consortium Inc
12 | * @date 2016
13 | * @version 3.0.0
14 | * @license GNU Lesser General Public License, version 3 ()
15 | */
16 | class ResourceHandler
17 | {
18 |
19 | /**
20 | * Class constructor.
21 | *
22 | * @param ToolProvider $toolProvider Tool Provider object
23 | * @param ProfileResourceHandler $resourceHandler Resource handler object
24 | */
25 | function __construct($toolProvider, $resourceHandler)
26 | {
27 |
28 | $this->resource_type = new \stdClass;
29 | $this->resource_type->code = $resourceHandler->item->id;
30 | $this->resource_name = new \stdClass;
31 | $this->resource_name->default_value = $resourceHandler->item->name;
32 | $this->resource_name->key = "{$resourceHandler->item->id}.resource.name";
33 | $this->description = new \stdClass;
34 | $this->description->default_value = $resourceHandler->item->description;
35 | $this->description->key = "{$resourceHandler->item->id}.resource.description";
36 | $this->icon_info = new \stdClass;
37 | $this->icon_info->default_location = new \stdClass;
38 | $this->icon_info->default_location->path = $resourceHandler->icon;
39 | $this->icon_info->key = "{$resourceHandler->item->id}.icon.path";
40 | $this->message = array();
41 | foreach ($resourceHandler->requiredMessages as $message) {
42 | $this->message[] = new Message($message, $toolProvider->consumer->profile->capability_offered);
43 | }
44 | foreach ($resourceHandler->optionalMessages as $message) {
45 | if (in_array($message->type, $toolProvider->consumer->profile->capability_offered)) {
46 | $this->message[] = new Message($message, $toolProvider->consumer->profile->capability_offered);
47 | }
48 | }
49 |
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/ToolProvider/MediaType/SecurityContract.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright IMS Global Learning Consortium Inc
11 | * @date 2016
12 | * @version 3.0.0
13 | * @license GNU Lesser General Public License, version 3 ()
14 | */
15 | class SecurityContract
16 | {
17 |
18 | /**
19 | * Class constructor.
20 | *
21 | * @param ToolProvider $toolProvider Tool Provider instance
22 | * @param string $secret Shared secret
23 | */
24 | function __construct($toolProvider, $secret)
25 | {
26 |
27 | $tcContexts = array();
28 | foreach ($toolProvider->consumer->profile->{'@context'} as $context) {
29 | if (is_object($context)) {
30 | $tcContexts = array_merge(get_object_vars($context), $tcContexts);
31 | }
32 | }
33 |
34 | $this->shared_secret = $secret;
35 | $toolServices = array();
36 | foreach ($toolProvider->requiredServices as $requiredService) {
37 | foreach ($requiredService->formats as $format) {
38 | $service = $toolProvider->findService($format, $requiredService->actions);
39 | if (($service !== false) && !array_key_exists($service->{'@id'}, $toolServices)) {
40 | $id = $service->{'@id'};
41 | $parts = explode(':', $id, 2);
42 | if (count($parts) > 1) {
43 | if (array_key_exists($parts[0], $tcContexts)) {
44 | $id = "{$tcContexts[$parts[0]]}{$parts[1]}";
45 | }
46 | }
47 | $toolService = new \stdClass;
48 | $toolService->{'@type'} = 'RestServiceProfile';
49 | $toolService->service = $id;
50 | $toolService->action = $requiredService->actions;
51 | $toolServices[$service->{'@id'}] = $toolService;
52 | }
53 | }
54 | }
55 | foreach ($toolProvider->optionalServices as $optionalService) {
56 | foreach ($optionalService->formats as $format) {
57 | $service = $toolProvider->findService($format, $optionalService->actions);
58 | if (($service !== false) && !array_key_exists($service->{'@id'}, $toolServices)) {
59 | $id = $service->{'@id'};
60 | $parts = explode(':', $id, 2);
61 | if (count($parts) > 1) {
62 | if (array_key_exists($parts[0], $tcContexts)) {
63 | $id = "{$tcContexts[$parts[0]]}{$parts[1]}";
64 | }
65 | }
66 | $toolService = new \stdClass;
67 | $toolService->{'@type'} = 'RestServiceProfile';
68 | $toolService->service = $id;
69 | $toolService->action = $optionalService->actions;
70 | $toolServices[$service->{'@id'}] = $toolService;
71 | }
72 | }
73 | }
74 | $this->tool_service = array_values($toolServices);
75 |
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/ToolProvider/MediaType/ToolProfile.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright IMS Global Learning Consortium Inc
11 | * @date 2016
12 | * @version 3.0.0
13 | * @license GNU Lesser General Public License, version 3 ()
14 | */
15 | class ToolProfile
16 | {
17 |
18 | public $product_instance;
19 |
20 | /**
21 | * Class constructor.
22 | *
23 | * @param ToolProvider $toolProvider Tool Provider object
24 | */
25 | function __construct($toolProvider)
26 | {
27 |
28 | $this->lti_version = 'LTI-2p0';
29 |
30 | if (!empty($toolProvider->product)) {
31 | $this->product_instance = new \stdClass;
32 | }
33 | if (!empty($toolProvider->product->id)) {
34 | $this->product_instance->guid = $toolProvider->product->id;
35 | }
36 | if (!empty($toolProvider->product->name)) {
37 | $this->product_instance->product_info = new \stdClass;
38 | $this->product_instance->product_info->product_name = new \stdClass;
39 | $this->product_instance->product_info->product_name->default_value = $toolProvider->product->name;
40 | $this->product_instance->product_info->product_name->key = 'tool.name';
41 | }
42 | if (!empty($toolProvider->product->description)) {
43 | $this->product_instance->product_info->description = new \stdClass;
44 | $this->product_instance->product_info->description->default_value = $toolProvider->product->description;
45 | $this->product_instance->product_info->description->key = 'tool.description';
46 | }
47 | if (!empty($toolProvider->product->url)) {
48 | $this->product_instance->guid = $toolProvider->product->url;
49 | }
50 | if (!empty($toolProvider->product->version)) {
51 | $this->product_instance->product_info->product_version = $toolProvider->product->version;
52 | }
53 | if (!empty($toolProvider->vendor)) {
54 | $this->product_instance->product_info->product_family = new \stdClass;
55 | $this->product_instance->product_info->product_family->vendor = new \stdClass;
56 | }
57 | if (!empty($toolProvider->vendor->id)) {
58 | $this->product_instance->product_info->product_family->vendor->code = $toolProvider->vendor->id;
59 | }
60 | if (!empty($toolProvider->vendor->name)) {
61 | $this->product_instance->product_info->product_family->vendor->vendor_name = new \stdClass;
62 | $this->product_instance->product_info->product_family->vendor->vendor_name->default_value = $toolProvider->vendor->name;
63 | $this->product_instance->product_info->product_family->vendor->vendor_name->key = 'tool.vendor.name';
64 | }
65 | if (!empty($toolProvider->vendor->description)) {
66 | $this->product_instance->product_info->product_family->vendor->description = new \stdClass;
67 | $this->product_instance->product_info->product_family->vendor->description->default_value = $toolProvider->vendor->description;
68 | $this->product_instance->product_info->product_family->vendor->description->key = 'tool.vendor.description';
69 | }
70 | if (!empty($toolProvider->vendor->url)) {
71 | $this->product_instance->product_info->product_family->vendor->website = $toolProvider->vendor->url;
72 | }
73 | if (!empty($toolProvider->vendor->timestamp)) {
74 | $this->product_instance->product_info->product_family->vendor->timestamp = date('Y-m-d\TH:i:sP', $toolProvider->vendor->timestamp);
75 | }
76 |
77 | $this->resource_handler = array();
78 | foreach ($toolProvider->resourceHandlers as $resourceHandler) {
79 | $this->resource_handler[] = new ResourceHandler($toolProvider, $resourceHandler);
80 | }
81 | if (!empty($toolProvider->baseUrl)) {
82 | $this->base_url_choice = array();
83 | $this->base_url_choice[] = new \stdClass;
84 | $this->base_url_choice[0]->default_base_url = $toolProvider->baseUrl;
85 | }
86 |
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/src/ToolProvider/MediaType/ToolProxy.php:
--------------------------------------------------------------------------------
1 |
12 | * @copyright IMS Global Learning Consortium Inc
13 | * @date 2016
14 | * @version 3.0.0
15 | * @license GNU Lesser General Public License, version 3 ()
16 | */
17 | class ToolProxy
18 | {
19 |
20 | /**
21 | * Class constructor.
22 | *
23 | * @param ToolProvider $toolProvider Tool Provider object
24 | * @param ServiceDefinition $toolProxyService Tool Proxy service
25 | * @param string $secret Shared secret
26 | */
27 | function __construct($toolProvider, $toolProxyService, $secret)
28 | {
29 |
30 | $contexts = array();
31 |
32 | $this->{'@context'} = array_merge(array('http://purl.imsglobal.org/ctx/lti/v2/ToolProxy'), $contexts);
33 | $this->{'@type'} = 'ToolProxy';
34 | $this->{'@id'} = "{$toolProxyService->endpoint}";
35 | $this->lti_version = 'LTI-2p0';
36 | $this->tool_consumer_profile = $toolProvider->consumer->profile->{'@id'};
37 | $this->tool_profile = new ToolProfile($toolProvider);
38 | $this->security_contract = new SecurityContract($toolProvider, $secret);
39 |
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/ToolProvider/OAuthDataStore.php:
--------------------------------------------------------------------------------
1 |
11 | * @copyright IMS Global Learning Consortium Inc
12 | * @date 2016
13 | * @version 3.0.2
14 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
15 | */
16 | class OAuthDataStore extends OAuth\OAuthDataStore
17 | {
18 |
19 | /**
20 | * Tool Provider object.
21 | *
22 | * @var ToolProvider $toolProvider
23 | */
24 | private $toolProvider = null;
25 |
26 | /**
27 | * Class constructor.
28 | *
29 | * @param ToolProvider $toolProvider Tool_Provider object
30 | */
31 | public function __construct($toolProvider)
32 | {
33 |
34 | $this->toolProvider = $toolProvider;
35 |
36 | }
37 |
38 | /**
39 | * Create an OAuthConsumer object for the tool consumer.
40 | *
41 | * @param string $consumerKey Consumer key value
42 | *
43 | * @return OAuthConsumer OAuthConsumer object
44 | */
45 | function lookup_consumer($consumerKey)
46 | {
47 |
48 | return new OAuth\OAuthConsumer($this->toolProvider->consumer->getKey(),
49 | $this->toolProvider->consumer->secret);
50 |
51 | }
52 |
53 | /**
54 | * Create an OAuthToken object for the tool consumer.
55 | *
56 | * @param string $consumer OAuthConsumer object
57 | * @param string $tokenType Token type
58 | * @param string $token Token value
59 | *
60 | * @return OAuthToken OAuthToken object
61 | */
62 | function lookup_token($consumer, $tokenType, $token)
63 | {
64 |
65 | return new OAuth\OAuthToken($consumer, '');
66 |
67 | }
68 |
69 | /**
70 | * Lookup nonce value for the tool consumer.
71 | *
72 | * @param OAuthConsumer $consumer OAuthConsumer object
73 | * @param string $token Token value
74 | * @param string $value Nonce value
75 | * @param string $timestamp Date/time of request
76 | *
77 | * @return boolean True if the nonce value already exists
78 | */
79 | function lookup_nonce($consumer, $token, $value, $timestamp)
80 | {
81 |
82 | $nonce = new ConsumerNonce($this->toolProvider->consumer, $value);
83 | $ok = !$nonce->load();
84 | if ($ok) {
85 | $ok = $nonce->save();
86 | }
87 | if (!$ok) {
88 | $this->toolProvider->reason = 'Invalid nonce.';
89 | }
90 |
91 | return !$ok;
92 |
93 | }
94 |
95 | /**
96 | * Get new request token.
97 | *
98 | * @param OAuthConsumer $consumer OAuthConsumer object
99 | * @param string $callback Callback URL
100 | *
101 | * @return string Null value
102 | */
103 | function new_request_token($consumer, $callback = null)
104 | {
105 |
106 | return null;
107 |
108 | }
109 |
110 | /**
111 | * Get new access token.
112 | *
113 | * @param string $token Token value
114 | * @param OAuthConsumer $consumer OAuthConsumer object
115 | * @param string $verifier Verification code
116 | *
117 | * @return string Null value
118 | */
119 | function new_access_token($token, $consumer, $verifier = null)
120 | {
121 |
122 | return null;
123 |
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/src/ToolProvider/Outcome.php:
--------------------------------------------------------------------------------
1 |
9 | * @copyright IMS Global Learning Consortium Inc
10 | * @date 2016
11 | * @version 3.0.2
12 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
13 | */
14 | class Outcome
15 | {
16 |
17 | /**
18 | * Language value.
19 | *
20 | * @var string $language
21 | */
22 | public $language = null;
23 | /**
24 | * Outcome status value.
25 | *
26 | * @var string $status
27 | */
28 | public $status = null;
29 | /**
30 | * Outcome date value.
31 | *
32 | * @var string $date
33 | */
34 | public $date = null;
35 | /**
36 | * Outcome type value.
37 | *
38 | * @var string $type
39 | */
40 | public $type = null;
41 | /**
42 | * Outcome data source value.
43 | *
44 | * @var string $dataSource
45 | */
46 | public $dataSource = null;
47 |
48 | /**
49 | * Outcome value.
50 | *
51 | * @var string $value
52 | */
53 | private $value = null;
54 |
55 | /**
56 | * Class constructor.
57 | *
58 | * @param string $value Outcome value (optional, default is none)
59 | */
60 | public function __construct($value = null)
61 | {
62 |
63 | $this->value = $value;
64 | $this->language = 'en-US';
65 | $this->date = gmdate('Y-m-d\TH:i:s\Z', time());
66 | $this->type = 'decimal';
67 |
68 | }
69 |
70 | /**
71 | * Get the outcome value.
72 | *
73 | * @return string Outcome value
74 | */
75 | public function getValue()
76 | {
77 |
78 | return $this->value;
79 |
80 | }
81 |
82 | /**
83 | * Set the outcome value.
84 | *
85 | * @param string $value Outcome value
86 | */
87 | public function setValue($value)
88 | {
89 |
90 | $this->value = $value;
91 |
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/src/ToolProvider/ResourceLink.php:
--------------------------------------------------------------------------------
1 |
16 | * @copyright IMS Global Learning Consortium Inc
17 | * @date 2016
18 | * @version 3.0.2
19 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
20 | */
21 | class ResourceLink
22 | {
23 |
24 | /**
25 | * Read action.
26 | */
27 | const EXT_READ = 1;
28 | /**
29 | * Write (create/update) action.
30 | */
31 | const EXT_WRITE = 2;
32 | /**
33 | * Delete action.
34 | */
35 | const EXT_DELETE = 3;
36 | /**
37 | * Create action.
38 | */
39 | const EXT_CREATE = 4;
40 | /**
41 | * Update action.
42 | */
43 | const EXT_UPDATE = 5;
44 |
45 | /**
46 | * Decimal outcome type.
47 | */
48 | const EXT_TYPE_DECIMAL = 'decimal';
49 | /**
50 | * Percentage outcome type.
51 | */
52 | const EXT_TYPE_PERCENTAGE = 'percentage';
53 | /**
54 | * Ratio outcome type.
55 | */
56 | const EXT_TYPE_RATIO = 'ratio';
57 | /**
58 | * Letter (A-F) outcome type.
59 | */
60 | const EXT_TYPE_LETTER_AF = 'letteraf';
61 | /**
62 | * Letter (A-F) with optional +/- outcome type.
63 | */
64 | const EXT_TYPE_LETTER_AF_PLUS = 'letterafplus';
65 | /**
66 | * Pass/fail outcome type.
67 | */
68 | const EXT_TYPE_PASS_FAIL = 'passfail';
69 | /**
70 | * Free text outcome type.
71 | */
72 | const EXT_TYPE_TEXT = 'freetext';
73 |
74 | /**
75 | * Context title.
76 | *
77 | * @var string $title
78 | */
79 | public $title = null;
80 | /**
81 | * Resource link ID as supplied in the last connection request.
82 | *
83 | * @var string $ltiResourceLinkId
84 | */
85 | public $ltiResourceLinkId = null;
86 | /**
87 | * User group sets (null if the consumer does not support the groups enhancement)
88 | *
89 | * @var array $groupSets
90 | */
91 | public $groupSets = null;
92 | /**
93 | * User groups (null if the consumer does not support the groups enhancement)
94 | *
95 | * @var array $groups
96 | */
97 | public $groups = null;
98 | /**
99 | * Request for last service request.
100 | *
101 | * @var string $extRequest
102 | */
103 | public $extRequest = null;
104 | /**
105 | * Request headers for last service request.
106 | *
107 | * @var array $extRequestHeaders
108 | */
109 | public $extRequestHeaders = null;
110 | /**
111 | * Response from last service request.
112 | *
113 | * @var string $extResponse
114 | */
115 | public $extResponse = null;
116 | /**
117 | * Response header from last service request.
118 | *
119 | * @var array $extResponseHeaders
120 | */
121 | public $extResponseHeaders = null;
122 | /**
123 | * Consumer key value for resource link being shared (if any).
124 | *
125 | * @var string $primaryResourceLinkId
126 | */
127 | public $primaryResourceLinkId = null;
128 | /**
129 | * Whether the sharing request has been approved by the primary resource link.
130 | *
131 | * @var boolean $shareApproved
132 | */
133 | public $shareApproved = null;
134 | /**
135 | * Date/time when the object was created.
136 | *
137 | * @var int $created
138 | */
139 | public $created = null;
140 | /**
141 | * Date/time when the object was last updated.
142 | *
143 | * @var int $updated
144 | */
145 | public $updated = null;
146 |
147 | /**
148 | * Record ID for this resource link.
149 | *
150 | * @var int $id
151 | */
152 | private $id = null;
153 | /**
154 | * Tool Consumer for this resource link.
155 | *
156 | * @var ToolConsumer $consumer
157 | */
158 | private $consumer = null;
159 | /**
160 | * Tool Consumer ID for this resource link.
161 | *
162 | * @var int $consumerId
163 | */
164 | private $consumerId = null;
165 | /**
166 | * Context for this resource link.
167 | *
168 | * @var Context $context
169 | */
170 | private $context = null;
171 | /**
172 | * Context ID for this resource link.
173 | *
174 | * @var int $contextId
175 | */
176 | private $contextId = null;
177 | /**
178 | * Setting values (LTI parameters, custom parameters and local parameters).
179 | *
180 | * @var array $settings
181 | */
182 | private $settings = null;
183 | /**
184 | * Whether the settings value have changed since last saved.
185 | *
186 | * @var boolean $settingsChanged
187 | */
188 | private $settingsChanged = false;
189 | /**
190 | * XML document for the last extension service request.
191 | *
192 | * @var string $extDoc
193 | */
194 | private $extDoc = null;
195 | /**
196 | * XML node array for the last extension service request.
197 | *
198 | * @var array $extNodes
199 | */
200 | private $extNodes = null;
201 | /**
202 | * Data connector object or string.
203 | *
204 | * @var mixed $dataConnector
205 | */
206 | private $dataConnector = null;
207 |
208 | /**
209 | * Class constructor.
210 | */
211 | public function __construct()
212 | {
213 |
214 | $this->initialize();
215 |
216 | }
217 |
218 | /**
219 | * Initialise the resource link.
220 | */
221 | public function initialize()
222 | {
223 |
224 | $this->title = '';
225 | $this->settings = array();
226 | $this->groupSets = null;
227 | $this->groups = null;
228 | $this->primaryResourceLinkId = null;
229 | $this->shareApproved = null;
230 | $this->created = null;
231 | $this->updated = null;
232 |
233 | }
234 |
235 | /**
236 | * Initialise the resource link.
237 | *
238 | * Pseudonym for initialize().
239 | */
240 | public function initialise()
241 | {
242 |
243 | $this->initialize();
244 |
245 | }
246 |
247 | /**
248 | * Save the resource link to the database.
249 | *
250 | * @return boolean True if the resource link was successfully saved.
251 | */
252 | public function save()
253 | {
254 |
255 | $ok = $this->getDataConnector()->saveResourceLink($this);
256 | if ($ok) {
257 | $this->settingsChanged = false;
258 | }
259 |
260 | return $ok;
261 |
262 | }
263 |
264 | /**
265 | * Delete the resource link from the database.
266 | *
267 | * @return boolean True if the resource link was successfully deleted.
268 | */
269 | public function delete()
270 | {
271 |
272 | return $this->getDataConnector()->deleteResourceLink($this);
273 |
274 | }
275 |
276 | /**
277 | * Get tool consumer.
278 | *
279 | * @return ToolConsumer Tool consumer object for this resource link.
280 | */
281 | public function getConsumer()
282 | {
283 |
284 | if (is_null($this->consumer)) {
285 | if (!is_null($this->context) || !is_null($this->contextId)) {
286 | $this->consumer = $this->getContext()->getConsumer();
287 | } else {
288 | $this->consumer = ToolConsumer::fromRecordId($this->consumerId, $this->getDataConnector());
289 | }
290 | }
291 |
292 | return $this->consumer;
293 |
294 | }
295 |
296 | /**
297 | * Set tool consumer ID.
298 | *
299 | * @param int $consumerId Tool Consumer ID for this resource link.
300 | */
301 | public function setConsumerId($consumerId)
302 | {
303 |
304 | $this->consumer = null;
305 | $this->consumerId = $consumerId;
306 |
307 | }
308 |
309 | /**
310 | * Get context.
311 | *
312 | * @return object LTIContext object for this resource link.
313 | */
314 | public function getContext()
315 | {
316 |
317 | if (is_null($this->context) && !is_null($this->contextId)) {
318 | $this->context = Context::fromRecordId($this->contextId, $this->getDataConnector());
319 | }
320 |
321 | return $this->context;
322 |
323 | }
324 |
325 | /**
326 | * Get context record ID.
327 | *
328 | * @return int Context record ID for this resource link.
329 | */
330 | public function getContextId()
331 | {
332 |
333 | return $this->contextId;
334 |
335 | }
336 |
337 | /**
338 | * Set context ID.
339 | *
340 | * @param int $contextId Context ID for this resource link.
341 | */
342 | public function setContextId($contextId)
343 | {
344 |
345 | $this->context = null;
346 | $this->contextId = $contextId;
347 |
348 | }
349 |
350 | /**
351 | * Get tool consumer key.
352 | *
353 | * @return string Consumer key value for this resource link.
354 | */
355 | public function getKey()
356 | {
357 |
358 | return $this->getConsumer()->getKey();
359 |
360 | }
361 |
362 | /**
363 | * Get resource link ID.
364 | *
365 | * @return string ID for this resource link.
366 | */
367 | public function getId()
368 | {
369 |
370 | return $this->ltiResourceLinkId;
371 |
372 | }
373 |
374 | /**
375 | * Get resource link record ID.
376 | *
377 | * @return int Record ID for this resource link.
378 | */
379 | public function getRecordId()
380 | {
381 |
382 | return $this->id;
383 |
384 | }
385 |
386 | /**
387 | * Set resource link record ID.
388 | *
389 | * @param int $id Record ID for this resource link.
390 | */
391 | public function setRecordId($id)
392 | {
393 |
394 | $this->id = $id;
395 |
396 | }
397 |
398 | /**
399 | * Get the data connector.
400 | *
401 | * @return mixed Data connector object or string
402 | */
403 | public function getDataConnector()
404 | {
405 |
406 | return $this->dataConnector;
407 |
408 | }
409 |
410 | /**
411 | * Get a setting value.
412 | *
413 | * @param string $name Name of setting
414 | * @param string $default Value to return if the setting does not exist (optional, default is an empty string)
415 | *
416 | * @return string Setting value
417 | */
418 | public function getSetting($name, $default = '')
419 | {
420 |
421 | if (array_key_exists($name, $this->settings)) {
422 | $value = $this->settings[$name];
423 | } else {
424 | $value = $default;
425 | }
426 |
427 | return $value;
428 |
429 | }
430 |
431 | /**
432 | * Set a setting value.
433 | *
434 | * @param string $name Name of setting
435 | * @param string $value Value to set, use an empty value to delete a setting (optional, default is null)
436 | */
437 | public function setSetting($name, $value = null)
438 | {
439 |
440 | $old_value = $this->getSetting($name);
441 | if ($value !== $old_value) {
442 | if (!empty($value)) {
443 | $this->settings[$name] = $value;
444 | } else {
445 | unset($this->settings[$name]);
446 | }
447 | $this->settingsChanged = true;
448 | }
449 |
450 | }
451 |
452 | /**
453 | * Get an array of all setting values.
454 | *
455 | * @return array Associative array of setting values
456 | */
457 | public function getSettings()
458 | {
459 |
460 | return $this->settings;
461 |
462 | }
463 |
464 | /**
465 | * Set an array of all setting values.
466 | *
467 | * @param array $settings Associative array of setting values
468 | */
469 | public function setSettings($settings)
470 | {
471 |
472 | $this->settings = $settings;
473 |
474 | }
475 |
476 | /**
477 | * Save setting values.
478 | *
479 | * @return boolean True if the settings were successfully saved
480 | */
481 | public function saveSettings()
482 | {
483 |
484 | if ($this->settingsChanged) {
485 | $ok = $this->save();
486 | } else {
487 | $ok = true;
488 | }
489 |
490 | return $ok;
491 |
492 | }
493 |
494 | /**
495 | * Check if the Outcomes service is supported.
496 | *
497 | * @return boolean True if this resource link supports the Outcomes service (either the LTI 1.1 or extension service)
498 | */
499 | public function hasOutcomesService()
500 | {
501 |
502 | $url = $this->getSetting('ext_ims_lis_basic_outcome_url') . $this->getSetting('lis_outcome_service_url');
503 |
504 | return !empty($url);
505 |
506 | }
507 |
508 | /**
509 | * Check if the Memberships extension service is supported.
510 | *
511 | * @return boolean True if this resource link supports the Memberships extension service
512 | */
513 | public function hasMembershipsService()
514 | {
515 |
516 | $url = $this->getSetting('ext_ims_lis_memberships_url');
517 |
518 | return !empty($url);
519 |
520 | }
521 |
522 | /**
523 | * Check if the Setting extension service is supported.
524 | *
525 | * @return boolean True if this resource link supports the Setting extension service
526 | */
527 | public function hasSettingService()
528 | {
529 |
530 | $url = $this->getSetting('ext_ims_lti_tool_setting_url');
531 |
532 | return !empty($url);
533 |
534 | }
535 |
536 | /**
537 | * Perform an Outcomes service request.
538 | *
539 | * @param int $action The action type constant
540 | * @param Outcome $ltiOutcome Outcome object
541 | * @param User $user User object
542 | *
543 | * @return boolean True if the request was successfully processed
544 | */
545 | public function doOutcomesService($action, $ltiOutcome, $user)
546 | {
547 |
548 | $response = false;
549 | $this->extResponse = null;
550 |
551 | // Lookup service details from the source resource link appropriate to the user (in case the destination is being shared)
552 | $sourceResourceLink = $user->getResourceLink();
553 | $sourcedId = $user->ltiResultSourcedId;
554 |
555 | // Use LTI 1.1 service in preference to extension service if it is available
556 | $urlLTI11 = $sourceResourceLink->getSetting('lis_outcome_service_url');
557 | $urlExt = $sourceResourceLink->getSetting('ext_ims_lis_basic_outcome_url');
558 | if ($urlExt || $urlLTI11) {
559 | switch ($action) {
560 | case self::EXT_READ:
561 | if ($urlLTI11 && ($ltiOutcome->type === self::EXT_TYPE_DECIMAL)) {
562 | $do = 'readResult';
563 | } else if ($urlExt) {
564 | $urlLTI11 = null;
565 | $do = 'basic-lis-readresult';
566 | }
567 | break;
568 | case self::EXT_WRITE:
569 | if ($urlLTI11 && $this->checkValueType($ltiOutcome, array(self::EXT_TYPE_DECIMAL))) {
570 | $do = 'replaceResult';
571 | } else if ($this->checkValueType($ltiOutcome)) {
572 | $urlLTI11 = null;
573 | $do = 'basic-lis-updateresult';
574 | }
575 | break;
576 | case self::EXT_DELETE:
577 | if ($urlLTI11 && ($ltiOutcome->type === self::EXT_TYPE_DECIMAL)) {
578 | $do = 'deleteResult';
579 | } else if ($urlExt) {
580 | $urlLTI11 = null;
581 | $do = 'basic-lis-deleteresult';
582 | }
583 | break;
584 | }
585 | }
586 | if (isset($do)) {
587 | $value = $ltiOutcome->getValue();
588 | if (is_null($value)) {
589 | $value = '';
590 | }
591 | if ($urlLTI11) {
592 | $xml = '';
593 | if ($action === self::EXT_WRITE) {
594 | $xml = <<
597 |
598 | {$ltiOutcome->language}
599 | {$value}
600 |
601 |
602 | EOF;
603 | }
604 | $sourcedId = htmlentities($sourcedId);
605 | $xml = <<
607 |
608 | {$sourcedId}
609 | {$xml}
610 |
611 | EOF;
612 | if ($this->doLTI11Service($do, $urlLTI11, $xml)) {
613 | switch ($action) {
614 | case self::EXT_READ:
615 | if (!isset($this->extNodes['imsx_POXBody']["{$do}Response"]['result']['resultScore']['textString'])) {
616 | break;
617 | } else {
618 | $ltiOutcome->setValue($this->extNodes['imsx_POXBody']["{$do}Response"]['result']['resultScore']['textString']);
619 | }
620 | case self::EXT_WRITE:
621 | case self::EXT_DELETE:
622 | $response = true;
623 | break;
624 | }
625 | }
626 | } else {
627 | $params = array();
628 | $params['sourcedid'] = $sourcedId;
629 | $params['result_resultscore_textstring'] = $value;
630 | if (!empty($ltiOutcome->language)) {
631 | $params['result_resultscore_language'] = $ltiOutcome->language;
632 | }
633 | if (!empty($ltiOutcome->status)) {
634 | $params['result_statusofresult'] = $ltiOutcome->status;
635 | }
636 | if (!empty($ltiOutcome->date)) {
637 | $params['result_date'] = $ltiOutcome->date;
638 | }
639 | if (!empty($ltiOutcome->type)) {
640 | $params['result_resultvaluesourcedid'] = $ltiOutcome->type;
641 | }
642 | if (!empty($ltiOutcome->data_source)) {
643 | $params['result_datasource'] = $ltiOutcome->data_source;
644 | }
645 | if ($this->doService($do, $urlExt, $params)) {
646 | switch ($action) {
647 | case self::EXT_READ:
648 | if (isset($this->extNodes['result']['resultscore']['textstring'])) {
649 | $response = $this->extNodes['result']['resultscore']['textstring'];
650 | }
651 | break;
652 | case self::EXT_WRITE:
653 | case self::EXT_DELETE:
654 | $response = true;
655 | break;
656 | }
657 | }
658 | }
659 | if (is_array($response) && (count($response) <= 0)) {
660 | $response = '';
661 | }
662 | }
663 |
664 | return $response;
665 |
666 | }
667 |
668 | /**
669 | * Perform a Memberships service request.
670 | *
671 | * The user table is updated with the new list of user objects.
672 | *
673 | * @param boolean $withGroups True is group information is to be requested as well
674 | *
675 | * @return mixed Array of User objects or False if the request was not successful
676 | */
677 | public function doMembershipsService($withGroups = false)
678 | {
679 |
680 | $users = array();
681 | $oldUsers = $this->getUserResultSourcedIDs(true, ToolProvider::ID_SCOPE_RESOURCE);
682 | $this->extResponse = null;
683 | $url = $this->getSetting('ext_ims_lis_memberships_url');
684 | $params = array();
685 | $params['id'] = $this->getSetting('ext_ims_lis_memberships_id');
686 | $ok = false;
687 | if ($withGroups) {
688 | $ok = $this->doService('basic-lis-readmembershipsforcontextwithgroups', $url, $params);
689 | }
690 | if ($ok) {
691 | $this->groupSets = array();
692 | $this->groups = array();
693 | } else {
694 | $ok = $this->doService('basic-lis-readmembershipsforcontext', $url, $params);
695 | }
696 |
697 | if ($ok) {
698 | if (!isset($this->extNodes['memberships']['member'])) {
699 | $members = array();
700 | } else if (!isset($this->extNodes['memberships']['member'][0])) {
701 | $members = array();
702 | $members[0] = $this->extNodes['memberships']['member'];
703 | } else {
704 | $members = $this->extNodes['memberships']['member'];
705 | }
706 |
707 | for ($i = 0; $i < count($members); $i++) {
708 |
709 | $user = User::fromResourceLink($this, $members[$i]['user_id']);
710 |
711 | // Set the user name
712 | $firstname = (isset($members[$i]['person_name_given'])) ? $members[$i]['person_name_given'] : '';
713 | $lastname = (isset($members[$i]['person_name_family'])) ? $members[$i]['person_name_family'] : '';
714 | $fullname = (isset($members[$i]['person_name_full'])) ? $members[$i]['person_name_full'] : '';
715 | $user->setNames($firstname, $lastname, $fullname);
716 |
717 | // Set the user email
718 | $email = (isset($members[$i]['person_contact_email_primary'])) ? $members[$i]['person_contact_email_primary'] : '';
719 | $user->setEmail($email, $this->getConsumer()->defaultEmail);
720 |
721 | /// Set the user roles
722 | if (isset($members[$i]['roles'])) {
723 | $user->roles = ToolProvider::parseRoles($members[$i]['roles']);
724 | }
725 |
726 | // Set the user groups
727 | if (!isset($members[$i]['groups']['group'])) {
728 | $groups = array();
729 | } else if (!isset($members[$i]['groups']['group'][0])) {
730 | $groups = array();
731 | $groups[0] = $members[$i]['groups']['group'];
732 | } else {
733 | $groups = $members[$i]['groups']['group'];
734 | }
735 | for ($j = 0; $j < count($groups); $j++) {
736 | $group = $groups[$j];
737 | if (isset($group['set'])) {
738 | $set_id = $group['set']['id'];
739 | if (!isset($this->groupSets[$set_id])) {
740 | $this->groupSets[$set_id] = array('title' => $group['set']['title'], 'groups' => array(),
741 | 'num_members' => 0, 'num_staff' => 0, 'num_learners' => 0);
742 | }
743 | $this->groupSets[$set_id]['num_members']++;
744 | if ($user->isStaff()) {
745 | $this->groupSets[$set_id]['num_staff']++;
746 | }
747 | if ($user->isLearner()) {
748 | $this->groupSets[$set_id]['num_learners']++;
749 | }
750 | if (!in_array($group['id'], $this->groupSets[$set_id]['groups'])) {
751 | $this->groupSets[$set_id]['groups'][] = $group['id'];
752 | }
753 | $this->groups[$group['id']] = array('title' => $group['title'], 'set' => $set_id);
754 | } else {
755 | $this->groups[$group['id']] = array('title' => $group['title']);
756 | }
757 | $user->groups[] = $group['id'];
758 | }
759 |
760 | // If a result sourcedid is provided save the user
761 | if (isset($members[$i]['lis_result_sourcedid'])) {
762 | $user->ltiResultSourcedId = $members[$i]['lis_result_sourcedid'];
763 | $user->save();
764 | }
765 | $users[] = $user;
766 |
767 | // Remove old user (if it exists)
768 | unset($oldUsers[$user->getId(ToolProvider::ID_SCOPE_RESOURCE)]);
769 | }
770 |
771 | // Delete any old users which were not in the latest list from the tool consumer
772 | foreach ($oldUsers as $id => $user) {
773 | $user->delete();
774 | }
775 | } else {
776 | $users = false;
777 | }
778 |
779 | return $users;
780 |
781 | }
782 |
783 | /**
784 | * Perform a Setting service request.
785 | *
786 | * @param int $action The action type constant
787 | * @param string $value The setting value (optional, default is null)
788 | *
789 | * @return mixed The setting value for a read action, true if a write or delete action was successful, otherwise false
790 | */
791 | public function doSettingService($action, $value = null)
792 | {
793 |
794 | $response = false;
795 | $this->extResponse = null;
796 | switch ($action) {
797 | case self::EXT_READ:
798 | $do = 'basic-lti-loadsetting';
799 | break;
800 | case self::EXT_WRITE:
801 | $do = 'basic-lti-savesetting';
802 | break;
803 | case self::EXT_DELETE:
804 | $do = 'basic-lti-deletesetting';
805 | break;
806 | }
807 | if (isset($do)) {
808 |
809 | $url = $this->getSetting('ext_ims_lti_tool_setting_url');
810 | $params = array();
811 | $params['id'] = $this->getSetting('ext_ims_lti_tool_setting_id');
812 | if (is_null($value)) {
813 | $value = '';
814 | }
815 | $params['setting'] = $value;
816 |
817 | if ($this->doService($do, $url, $params)) {
818 | switch ($action) {
819 | case self::EXT_READ:
820 | if (isset($this->extNodes['setting']['value'])) {
821 | $response = $this->extNodes['setting']['value'];
822 | if (is_array($response)) {
823 | $response = '';
824 | }
825 | }
826 | break;
827 | case self::EXT_WRITE:
828 | $this->setSetting('ext_ims_lti_tool_setting', $value);
829 | $this->saveSettings();
830 | $response = true;
831 | break;
832 | case self::EXT_DELETE:
833 | $response = true;
834 | break;
835 | }
836 | }
837 | }
838 |
839 | return $response;
840 |
841 | }
842 |
843 | /**
844 | * Check if the Tool Settings service is supported.
845 | *
846 | * @return boolean True if this resource link supports the Tool Settings service
847 | */
848 | public function hasToolSettingsService()
849 | {
850 |
851 | $url = $this->getSetting('custom_link_setting_url');
852 |
853 | return !empty($url);
854 |
855 | }
856 |
857 | /**
858 | * Get Tool Settings.
859 | *
860 | * @param int $mode Mode for request (optional, default is current level only)
861 | * @param boolean $simple True if all the simple media type is to be used (optional, default is true)
862 | *
863 | * @return mixed The array of settings if successful, otherwise false
864 | */
865 | public function getToolSettings($mode = Service\ToolSettings::MODE_CURRENT_LEVEL, $simple = true)
866 | {
867 |
868 | $url = $this->getSetting('custom_link_setting_url');
869 | $service = new Service\ToolSettings($this, $url, $simple);
870 | $response = $service->get($mode);
871 |
872 | return $response;
873 |
874 | }
875 |
876 | /**
877 | * Perform a Tool Settings service request.
878 | *
879 | * @param array $settings An associative array of settings (optional, default is none)
880 | *
881 | * @return boolean True if action was successful, otherwise false
882 | */
883 | public function setToolSettings($settings = array())
884 | {
885 |
886 | $url = $this->getSetting('custom_link_setting_url');
887 | $service = new Service\ToolSettings($this, $url);
888 | $response = $service->set($settings);
889 |
890 | return $response;
891 |
892 | }
893 |
894 | /**
895 | * Check if the Membership service is supported.
896 | *
897 | * @return boolean True if this resource link supports the Membership service
898 | */
899 | public function hasMembershipService()
900 | {
901 |
902 | $has = !empty($this->contextId);
903 | if ($has) {
904 | $has = !empty($this->getContext()->getSetting('custom_context_memberships_url'));
905 | }
906 |
907 | return $has;
908 |
909 | }
910 |
911 | /**
912 | * Get Memberships.
913 | *
914 | * @return mixed The array of User objects if successful, otherwise false
915 | */
916 | public function getMembership()
917 | {
918 |
919 | $response = false;
920 | if (!empty($this->contextId)) {
921 | $url = $this->getContext()->getSetting('custom_context_memberships_url');
922 | if (!empty($url)) {
923 | $service = new Service\Membership($this, $url);
924 | $response = $service->get();
925 | }
926 | }
927 |
928 | return $response;
929 |
930 | }
931 |
932 | /**
933 | * Obtain an array of User objects for users with a result sourcedId.
934 | *
935 | * The array may include users from other resource links which are sharing this resource link.
936 | * It may also be optionally indexed by the user ID of a specified scope.
937 | *
938 | * @param boolean $localOnly True if only users from this resource link are to be returned, not users from shared resource links (optional, default is false)
939 | * @param int $idScope Scope to use for ID values (optional, default is null for consumer default)
940 | *
941 | * @return array Array of User objects
942 | */
943 | public function getUserResultSourcedIDs($localOnly = false, $idScope = null)
944 | {
945 |
946 | return $this->getDataConnector()->getUserResultSourcedIDsResourceLink($this, $localOnly, $idScope);
947 |
948 | }
949 |
950 | /**
951 | * Get an array of ResourceLinkShare objects for each resource link which is sharing this context.
952 | *
953 | * @return array Array of ResourceLinkShare objects
954 | */
955 | public function getShares()
956 | {
957 |
958 | return $this->getDataConnector()->getSharesResourceLink($this);
959 |
960 | }
961 |
962 | /**
963 | * Class constructor from consumer.
964 | *
965 | * @param ToolConsumer $consumer Consumer object
966 | * @param string $ltiResourceLinkId Resource link ID value
967 | * @param string $tempId Temporary Resource link ID value (optional, default is null)
968 | * @return ResourceLink
969 | */
970 | public static function fromConsumer($consumer, $ltiResourceLinkId, $tempId = null)
971 | {
972 |
973 | $resourceLink = new ResourceLink();
974 | $resourceLink->consumer = $consumer;
975 | $resourceLink->dataConnector = $consumer->getDataConnector();
976 | $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
977 | if (!empty($ltiResourceLinkId)) {
978 | $resourceLink->load();
979 | if (is_null($resourceLink->id) && !empty($tempId)) {
980 | $resourceLink->ltiResourceLinkId = $tempId;
981 | $resourceLink->load();
982 | $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
983 | }
984 | }
985 |
986 | return $resourceLink;
987 |
988 | }
989 |
990 | /**
991 | * Class constructor from context.
992 | *
993 | * @param Context $context Context object
994 | * @param string $ltiResourceLinkId Resource link ID value
995 | * @param string $tempId Temporary Resource link ID value (optional, default is null)
996 | * @return ResourceLink
997 | */
998 | public static function fromContext($context, $ltiResourceLinkId, $tempId = null)
999 | {
1000 |
1001 | $resourceLink = new ResourceLink();
1002 | $resourceLink->setContextId($context->getRecordId());
1003 | $resourceLink->context = $context;
1004 | $resourceLink->dataConnector = $context->getDataConnector();
1005 | $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
1006 | if (!empty($ltiResourceLinkId)) {
1007 | $resourceLink->load();
1008 | if (is_null($resourceLink->id) && !empty($tempId)) {
1009 | $resourceLink->ltiResourceLinkId = $tempId;
1010 | $resourceLink->load();
1011 | $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
1012 | }
1013 | }
1014 |
1015 | return $resourceLink;
1016 |
1017 | }
1018 |
1019 | /**
1020 | * Load the resource link from the database.
1021 | *
1022 | * @param int $id Record ID of resource link
1023 | * @param DataConnector $dataConnector Database connection object
1024 | *
1025 | * @return ResourceLink ResourceLink object
1026 | */
1027 | public static function fromRecordId($id, $dataConnector)
1028 | {
1029 |
1030 | $resourceLink = new ResourceLink();
1031 | $resourceLink->dataConnector = $dataConnector;
1032 | $resourceLink->load($id);
1033 |
1034 | return $resourceLink;
1035 |
1036 | }
1037 |
1038 | ###
1039 | ### PRIVATE METHODS
1040 | ###
1041 |
1042 | /**
1043 | * Load the resource link from the database.
1044 | *
1045 | * @param int $id Record ID of resource link (optional, default is null)
1046 | *
1047 | * @return boolean True if resource link was successfully loaded
1048 | */
1049 | private function load($id = null)
1050 | {
1051 |
1052 | $this->initialize();
1053 | $this->id = $id;
1054 |
1055 | return $this->getDataConnector()->loadResourceLink($this);
1056 |
1057 | }
1058 |
1059 | /**
1060 | * Convert data type of value to a supported type if possible.
1061 | *
1062 | * @param Outcome $ltiOutcome Outcome object
1063 | * @param string[] $supportedTypes Array of outcome types to be supported (optional, default is null to use supported types reported in the last launch for this resource link)
1064 | *
1065 | * @return boolean True if the type/value are valid and supported
1066 | */
1067 | private function checkValueType($ltiOutcome, $supportedTypes = null)
1068 | {
1069 |
1070 | if (empty($supportedTypes)) {
1071 | $supportedTypes = explode(',', str_replace(' ', '', strtolower($this->getSetting('ext_ims_lis_resultvalue_sourcedids', self::EXT_TYPE_DECIMAL))));
1072 | }
1073 | $type = $ltiOutcome->type;
1074 | $value = $ltiOutcome->getValue();
1075 | // Check whether the type is supported or there is no value
1076 | $ok = in_array($type, $supportedTypes) || (strlen($value) <= 0);
1077 | if (!$ok) {
1078 | // Convert numeric values to decimal
1079 | if ($type === self::EXT_TYPE_PERCENTAGE) {
1080 | if (substr($value, -1) === '%') {
1081 | $value = substr($value, 0, -1);
1082 | }
1083 | $ok = is_numeric($value) && ($value >= 0) && ($value <= 100);
1084 | if ($ok) {
1085 | $ltiOutcome->setValue($value / 100);
1086 | $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1087 | }
1088 | } else if ($type === self::EXT_TYPE_RATIO) {
1089 | $parts = explode('/', $value, 2);
1090 | $ok = (count($parts) === 2) && is_numeric($parts[0]) && is_numeric($parts[1]) && ($parts[0] >= 0) && ($parts[1] > 0);
1091 | if ($ok) {
1092 | $ltiOutcome->setValue($parts[0] / $parts[1]);
1093 | $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1094 | }
1095 | // Convert letter_af to letter_af_plus or text
1096 | } else if ($type === self::EXT_TYPE_LETTER_AF) {
1097 | if (in_array(self::EXT_TYPE_LETTER_AF_PLUS, $supportedTypes)) {
1098 | $ok = true;
1099 | $ltiOutcome->type = self::EXT_TYPE_LETTER_AF_PLUS;
1100 | } else if (in_array(self::EXT_TYPE_TEXT, $supportedTypes)) {
1101 | $ok = true;
1102 | $ltiOutcome->type = self::EXT_TYPE_TEXT;
1103 | }
1104 | // Convert letter_af_plus to letter_af or text
1105 | } else if ($type === self::EXT_TYPE_LETTER_AF_PLUS) {
1106 | if (in_array(self::EXT_TYPE_LETTER_AF, $supportedTypes) && (strlen($value) === 1)) {
1107 | $ok = true;
1108 | $ltiOutcome->type = self::EXT_TYPE_LETTER_AF;
1109 | } else if (in_array(self::EXT_TYPE_TEXT, $supportedTypes)) {
1110 | $ok = true;
1111 | $ltiOutcome->type = self::EXT_TYPE_TEXT;
1112 | }
1113 | // Convert text to decimal
1114 | } else if ($type === self::EXT_TYPE_TEXT) {
1115 | $ok = is_numeric($value) && ($value >= 0) && ($value <=1);
1116 | if ($ok) {
1117 | $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1118 | } else if (substr($value, -1) === '%') {
1119 | $value = substr($value, 0, -1);
1120 | $ok = is_numeric($value) && ($value >= 0) && ($value <=100);
1121 | if ($ok) {
1122 | if (in_array(self::EXT_TYPE_PERCENTAGE, $supportedTypes)) {
1123 | $ltiOutcome->type = self::EXT_TYPE_PERCENTAGE;
1124 | } else {
1125 | $ltiOutcome->setValue($value / 100);
1126 | $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1127 | }
1128 | }
1129 | }
1130 | }
1131 | }
1132 |
1133 | return $ok;
1134 |
1135 | }
1136 |
1137 | /**
1138 | * Send a service request to the tool consumer.
1139 | *
1140 | * @param string $type Message type value
1141 | * @param string $url URL to send request to
1142 | * @param array $params Associative array of parameter values to be passed
1143 | *
1144 | * @return boolean True if the request successfully obtained a response
1145 | */
1146 | private function doService($type, $url, $params)
1147 | {
1148 |
1149 | $ok = false;
1150 | $this->extRequest = null;
1151 | $this->extRequestHeaders = '';
1152 | $this->extResponse = null;
1153 | $this->extResponseHeaders = '';
1154 | if (!empty($url)) {
1155 | $params = $this->getConsumer()->signParameters($url, $type, $this->getConsumer()->ltiVersion, $params);
1156 | // Connect to tool consumer
1157 | $http = new HTTPMessage($url, 'POST', $params);
1158 | // Parse XML response
1159 | if ($http->send()) {
1160 | $this->extResponse = $http->response;
1161 | $this->extResponseHeaders = $http->responseHeaders;
1162 | try {
1163 | $this->extDoc = new DOMDocument();
1164 | $this->extDoc->loadXML($http->response);
1165 | $this->extNodes = $this->domnodeToArray($this->extDoc->documentElement);
1166 | if (isset($this->extNodes['statusinfo']['codemajor']) && ($this->extNodes['statusinfo']['codemajor'] === 'Success')) {
1167 | $ok = true;
1168 | }
1169 | } catch (\Exception $e) {
1170 | }
1171 | }
1172 | $this->extRequest = $http->request;
1173 | $this->extRequestHeaders = $http->requestHeaders;
1174 | }
1175 |
1176 | return $ok;
1177 |
1178 | }
1179 |
1180 | /**
1181 | * Send a service request to the tool consumer.
1182 | *
1183 | * @param string $type Message type value
1184 | * @param string $url URL to send request to
1185 | * @param string $xml XML of message request
1186 | *
1187 | * @return boolean True if the request successfully obtained a response
1188 | */
1189 | private function doLTI11Service($type, $url, $xml)
1190 | {
1191 |
1192 | $ok = false;
1193 | $this->extRequest = null;
1194 | $this->extRequestHeaders = '';
1195 | $this->extResponse = null;
1196 | $this->extResponseHeaders = '';
1197 | if (!empty($url)) {
1198 | $id = uniqid();
1199 | $xmlRequest = <<< EOD
1200 |
1201 |
1202 |
1203 |
1204 | V1.0
1205 | {$id}
1206 |
1207 |
1208 |
1209 | <{$type}Request>
1210 | {$xml}
1211 | {$type}Request>
1212 |
1213 |
1214 | EOD;
1215 | // Calculate body hash
1216 | $hash = base64_encode(sha1($xmlRequest, true));
1217 | $params = array('oauth_body_hash' => $hash);
1218 |
1219 | // Add OAuth signature
1220 | $hmacMethod = new OAuth\OAuthSignatureMethod_HMAC_SHA1();
1221 | $consumer = new OAuth\OAuthConsumer($this->getConsumer()->getKey(), $this->getConsumer()->secret, null);
1222 | $req = OAuth\OAuthRequest::from_consumer_and_token($consumer, null, 'POST', $url, $params);
1223 | $req->sign_request($hmacMethod, $consumer, null);
1224 | $params = $req->get_parameters();
1225 | $header = $req->to_header();
1226 | $header .= "\nContent-Type: application/xml";
1227 | // Connect to tool consumer
1228 | $http = new HTTPMessage($url, 'POST', $xmlRequest, $header);
1229 | // Parse XML response
1230 | if ($http->send()) {
1231 | $this->extResponse = $http->response;
1232 | $this->extResponseHeaders = $http->responseHeaders;
1233 | try {
1234 | $this->extDoc = new DOMDocument();
1235 | $this->extDoc->loadXML($http->response);
1236 | $this->extNodes = $this->domnodeToArray($this->extDoc->documentElement);
1237 | if (isset($this->extNodes['imsx_POXHeader']['imsx_POXResponseHeaderInfo']['imsx_statusInfo']['imsx_codeMajor']) &&
1238 | ($this->extNodes['imsx_POXHeader']['imsx_POXResponseHeaderInfo']['imsx_statusInfo']['imsx_codeMajor'] === 'success')) {
1239 | $ok = true;
1240 | }
1241 | } catch (\Exception $e) {
1242 | }
1243 | }
1244 | $this->extRequest = $http->request;
1245 | $this->extRequestHeaders = $http->requestHeaders;
1246 | }
1247 |
1248 | return $ok;
1249 |
1250 | }
1251 |
1252 | /**
1253 | * Convert DOM nodes to array.
1254 | *
1255 | * @param DOMElement $node XML element
1256 | *
1257 | * @return array Array of XML document elements
1258 | */
1259 | private function domnodeToArray($node)
1260 | {
1261 |
1262 | $output = '';
1263 | switch ($node->nodeType) {
1264 | case XML_CDATA_SECTION_NODE:
1265 | case XML_TEXT_NODE:
1266 | $output = trim($node->textContent);
1267 | break;
1268 | case XML_ELEMENT_NODE:
1269 | for ($i = 0; $i < $node->childNodes->length; $i++) {
1270 | $child = $node->childNodes->item($i);
1271 | $v = $this->domnodeToArray($child);
1272 | if (isset($child->tagName)) {
1273 | $t = $child->tagName;
1274 | if (!isset($output[$t])) {
1275 | $output[$t] = array();
1276 | }
1277 | $output[$t][] = $v;
1278 | } else {
1279 | $s = (string) $v;
1280 | if (strlen($s) > 0) {
1281 | $output = $s;
1282 | }
1283 | }
1284 | }
1285 | if (is_array($output)) {
1286 | if ($node->attributes->length) {
1287 | $a = array();
1288 | foreach ($node->attributes as $attrName => $attrNode) {
1289 | $a[$attrName] = (string) $attrNode->value;
1290 | }
1291 | $output['@attributes'] = $a;
1292 | }
1293 | foreach ($output as $t => $v) {
1294 | if (is_array($v) && count($v)==1 && $t!='@attributes') {
1295 | $output[$t] = $v[0];
1296 | }
1297 | }
1298 | }
1299 | break;
1300 | }
1301 |
1302 | return $output;
1303 |
1304 | }
1305 |
1306 | }
1307 |
--------------------------------------------------------------------------------
/src/ToolProvider/ResourceLinkShare.php:
--------------------------------------------------------------------------------
1 |
9 | * @copyright IMS Global Learning Consortium Inc
10 | * @date 2016
11 | * @version 3.0.0
12 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
13 | */
14 | class ResourceLinkShare
15 | {
16 |
17 | /**
18 | * Consumer key value.
19 | *
20 | * @var string $consumerKey
21 | */
22 | public $consumerKey = null;
23 | /**
24 | * Resource link ID value.
25 | *
26 | * @var string $resourceLinkId
27 | */
28 | public $resourceLinkId = null;
29 | /**
30 | * Title of sharing context.
31 | *
32 | * @var string $title
33 | */
34 | public $title = null;
35 | /**
36 | * Whether sharing request is to be automatically approved on first use.
37 | *
38 | * @var boolean $approved
39 | */
40 | public $approved = null;
41 |
42 | /**
43 | * Class constructor.
44 | */
45 | public function __construct()
46 | {
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/ToolProvider/ResourceLinkShareKey.php:
--------------------------------------------------------------------------------
1 |
11 | * @copyright IMS Global Learning Consortium Inc
12 | * @date 2016
13 | * @version 3.0.2
14 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
15 | */
16 | class ResourceLinkShareKey
17 | {
18 |
19 | /**
20 | * Maximum permitted life for a share key value.
21 | */
22 | const MAX_SHARE_KEY_LIFE = 168; // in hours (1 week)
23 | /**
24 | * Default life for a share key value.
25 | */
26 | const DEFAULT_SHARE_KEY_LIFE = 24; // in hours
27 | /**
28 | * Minimum length for a share key value.
29 | */
30 | const MIN_SHARE_KEY_LENGTH = 5;
31 | /**
32 | * Maximum length for a share key value.
33 | */
34 | const MAX_SHARE_KEY_LENGTH = 32;
35 |
36 | /**
37 | * ID for resource link being shared.
38 | *
39 | * @var string $resourceLinkId
40 | */
41 | public $resourceLinkId = null;
42 | /**
43 | * Length of share key.
44 | *
45 | * @var int $length
46 | */
47 | public $length = null;
48 | /**
49 | * Life of share key.
50 | *
51 | * @var int $life
52 | */
53 | public $life = null; // in hours
54 | /**
55 | * Whether the sharing arrangement should be automatically approved when first used.
56 | *
57 | * @var boolean $autoApprove
58 | */
59 | public $autoApprove = false;
60 | /**
61 | * Date/time when the share key expires.
62 | *
63 | * @var int $expires
64 | */
65 | public $expires = null;
66 |
67 | /**
68 | * Share key value.
69 | *
70 | * @var string $id
71 | */
72 | private $id = null;
73 | /**
74 | * Data connector.
75 | *
76 | * @var DataConnector $dataConnector
77 | */
78 | private $dataConnector = null;
79 |
80 | /**
81 | * Class constructor.
82 | *
83 | * @param ResourceLink $resourceLink Resource_Link object
84 | * @param string $id Value of share key (optional, default is null)
85 | */
86 | public function __construct($resourceLink, $id = null)
87 | {
88 |
89 | $this->initialize();
90 | $this->dataConnector = $resourceLink->getDataConnector();
91 | $this->resourceLinkId = $resourceLink->getRecordId();
92 | $this->id = $id;
93 | if (!empty($id)) {
94 | $this->load();
95 | }
96 |
97 | }
98 |
99 | /**
100 | * Initialise the resource link share key.
101 | */
102 | public function initialize()
103 | {
104 |
105 | $this->length = null;
106 | $this->life = null;
107 | $this->autoApprove = false;
108 | $this->expires = null;
109 |
110 | }
111 |
112 | /**
113 | * Initialise the resource link share key.
114 | *
115 | * Pseudonym for initialize().
116 | */
117 | public function initialise()
118 | {
119 |
120 | $this->initialize();
121 |
122 | }
123 |
124 | /**
125 | * Save the resource link share key to the database.
126 | *
127 | * @return boolean True if the share key was successfully saved
128 | */
129 | public function save()
130 | {
131 |
132 | if (empty($this->life)) {
133 | $this->life = self::DEFAULT_SHARE_KEY_LIFE;
134 | } else {
135 | $this->life = max(min($this->life, self::MAX_SHARE_KEY_LIFE), 0);
136 | }
137 | $this->expires = time() + ($this->life * 60 * 60);
138 | if (empty($this->id)) {
139 | if (empty($this->length) || !is_numeric($this->length)) {
140 | $this->length = self::MAX_SHARE_KEY_LENGTH;
141 | } else {
142 | $this->length = max(min($this->length, self::MAX_SHARE_KEY_LENGTH), self::MIN_SHARE_KEY_LENGTH);
143 | }
144 | $this->id = DataConnector::getRandomString($this->length);
145 | }
146 |
147 | return $this->dataConnector->saveResourceLinkShareKey($this);
148 |
149 | }
150 |
151 | /**
152 | * Delete the resource link share key from the database.
153 | *
154 | * @return boolean True if the share key was successfully deleted
155 | */
156 | public function delete()
157 | {
158 |
159 | return $this->dataConnector->deleteResourceLinkShareKey($this);
160 |
161 | }
162 |
163 | /**
164 | * Get share key value.
165 | *
166 | * @return string Share key value
167 | */
168 | public function getId()
169 | {
170 |
171 | return $this->id;
172 |
173 | }
174 |
175 | ###
176 | ### PRIVATE METHOD
177 | ###
178 |
179 | /**
180 | * Load the resource link share key from the database.
181 | */
182 | private function load()
183 | {
184 |
185 | $this->initialize();
186 | $this->dataConnector->loadResourceLinkShareKey($this);
187 | if (!is_null($this->id)) {
188 | $this->length = strlen($this->id);
189 | }
190 | if (!is_null($this->expires)) {
191 | $this->life = ($this->expires - time()) / 60 / 60;
192 | }
193 |
194 | }
195 |
196 | }
197 |
--------------------------------------------------------------------------------
/src/ToolProvider/Service/Membership.php:
--------------------------------------------------------------------------------
1 |
11 | * @copyright IMS Global Learning Consortium Inc
12 | * @date 2016
13 | * @version 3.0.0
14 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
15 | */
16 | class Membership extends Service
17 | {
18 |
19 | /**
20 | * The object to which the settings apply (ResourceLink, Context or ToolConsumer).
21 | *
22 | * @var object $source
23 | */
24 | private $source;
25 |
26 | /**
27 | * Class constructor.
28 | *
29 | * @param object $source The object to which the memberships apply (ResourceLink or Context)
30 | * @param string $endpoint Service endpoint
31 | */
32 | public function __construct($source, $endpoint)
33 | {
34 |
35 | $consumer = $source->getConsumer();
36 | parent::__construct($consumer, $endpoint, 'application/vnd.ims.lis.v2.membershipcontainer+json');
37 | $this->source = $source;
38 |
39 | }
40 |
41 | /**
42 | * Get the memberships.
43 | *
44 | * @param string $role Role for which memberships are to be requested (optional, default is all roles)
45 | * @param int $limit Limit on the number of memberships to be returned (optional, default is all)
46 | *
47 | * @return mixed The array of User objects if successful, otherwise false
48 | */
49 | public function get($role = null, $limit = 0) {
50 |
51 | $isLink = is_a($this->source, 'IMSGlobal\LTI\ToolProvider\ResourceLink');
52 | $parameters = array();
53 | if (!empty($role)) {
54 | $parameters['role'] = $role;
55 | }
56 | if ($limit > 0) {
57 | $parameters['limit'] = strval($limit);
58 | }
59 | if ($isLink) {
60 | $parameters['rlid'] = $this->source->getId();
61 | }
62 | $http = $this->send('GET', $parameters);
63 | if (!$http->ok) {
64 | $users = false;
65 | } else {
66 | $users = array();
67 | if ($isLink) {
68 | $oldUsers = $this->source->getUserResultSourcedIDs(true, ToolProvider\ToolProvider::ID_SCOPE_RESOURCE);
69 | }
70 | foreach ($http->responseJson->pageOf->membershipSubject->membership as $membership) {
71 | $member = $membership->member;
72 | if ($isLink) {
73 | $user = ToolProvider\User::fromResourceLink($this->source, $member->userId);
74 | } else {
75 | $user = new ToolProvider\User();
76 | $user->ltiUserId = $member->userId;
77 | }
78 |
79 | // Set the user name
80 | $firstname = (isset($member->givenName)) ? $member->givenName : '';
81 | $lastname = (isset($member->familyName)) ? $member->familyName : '';
82 | $fullname = (isset($member->name)) ? $member->name : '';
83 | $user->setNames($firstname, $lastname, $fullname);
84 |
85 | // Set the user email
86 | $email = (isset($member->email)) ? $member->email : '';
87 | $user->setEmail($email, $this->source->getConsumer()->defaultEmail);
88 |
89 | // Set the user roles
90 | if (isset($membership->role)) {
91 | $user->roles = ToolProvider\ToolProvider::parseRoles($membership->role);
92 | }
93 |
94 | // If a result sourcedid is provided save the user
95 | if ($isLink) {
96 | if (isset($member->message)) {
97 | foreach ($member->message as $message) {
98 | if (isset($message->message_type) && ($message->message_type === 'basic-lti-launch-request')) {
99 | if (isset($message->lis_result_sourcedid)) {
100 | $user->ltiResultSourcedId = $message->lis_result_sourcedid;
101 | $user->save();
102 | }
103 | break;
104 | }
105 | }
106 | }
107 | }
108 | $users[] = $user;
109 |
110 | // Remove old user (if it exists)
111 | if ($isLink) {
112 | unset($oldUsers[$user->getId(ToolProvider\ToolProvider::ID_SCOPE_RESOURCE)]);
113 | }
114 | }
115 |
116 | // Delete any old users which were not in the latest list from the tool consumer
117 | if ($isLink) {
118 | foreach ($oldUsers as $id => $user) {
119 | $user->delete();
120 | }
121 | }
122 | }
123 |
124 | return $users;
125 |
126 | }
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/src/ToolProvider/Service/Service.php:
--------------------------------------------------------------------------------
1 |
12 | * @copyright IMS Global Learning Consortium Inc
13 | * @date 2016
14 | * @version 3.0.0
15 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
16 | */
17 | class Service
18 | {
19 |
20 | /**
21 | * Whether service request should be sent unsigned.
22 | *
23 | * @var boolean $unsigned
24 | */
25 | public $unsigned = false;
26 |
27 | /**
28 | * Service endpoint.
29 | *
30 | * @var string $endpoint
31 | */
32 | protected $endpoint;
33 | /**
34 | * Tool Consumer for this service request.
35 | *
36 | * @var ToolConsumer $consumer
37 | */
38 | private $consumer;
39 | /**
40 | * Media type of message body.
41 | *
42 | * @var string $mediaType
43 | */
44 | private $mediaType;
45 |
46 | /**
47 | * Class constructor.
48 | *
49 | * @param ToolConsumer $consumer Tool consumer object for this service request
50 | * @param string $endpoint Service endpoint
51 | * @param string $mediaType Media type of message body
52 | */
53 | public function __construct($consumer, $endpoint, $mediaType)
54 | {
55 |
56 | $this->consumer = $consumer;
57 | $this->endpoint = $endpoint;
58 | $this->mediaType = $mediaType;
59 |
60 | }
61 |
62 | /**
63 | * Send a service request.
64 | *
65 | * @param string $method The action type constant (optional, default is GET)
66 | * @param array $parameters Query parameters to add to endpoint (optional, default is none)
67 | * @param string $body Body of request (optional, default is null)
68 | *
69 | * @return HTTPMessage HTTP object containing request and response details
70 | */
71 | public function send($method, $parameters = array(), $body = null)
72 | {
73 |
74 | $url = $this->endpoint;
75 | if (!empty($parameters)) {
76 | if (strpos($url, '?') === false) {
77 | $sep = '?';
78 | } else {
79 | $sep = '&';
80 | }
81 | foreach ($parameters as $name => $value) {
82 | $url .= $sep . urlencode($name) . '=' . urlencode($value);
83 | $sep = '&';
84 | }
85 | }
86 | if (!$this->unsigned) {
87 | $header = ToolProvider\ToolConsumer::addSignature($url, $this->consumer->getKey(), $this->consumer->secret, $body, $method, $this->mediaType);
88 | } else {
89 | $header = null;
90 | }
91 |
92 | // Connect to tool consumer
93 | $http = new HTTPMessage($url, $method, $body, $header);
94 | // Parse JSON response
95 | if ($http->send() && !empty($http->response)) {
96 | $http->responseJson = json_decode($http->response);
97 | $http->ok = !is_null($http->responseJson);
98 | }
99 |
100 | return $http;
101 |
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/src/ToolProvider/Service/ToolSettings.php:
--------------------------------------------------------------------------------
1 |
9 | * @copyright IMS Global Learning Consortium Inc
10 | * @date 2016
11 | * @version 3.0.0
12 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
13 | */
14 | class ToolSettings extends Service
15 | {
16 |
17 | /**
18 | * Settings at current level mode.
19 | */
20 | const MODE_CURRENT_LEVEL = 1;
21 | /**
22 | * Settings at all levels mode.
23 | */
24 | const MODE_ALL_LEVELS = 2;
25 | /**
26 | * Settings with distinct names at all levels mode.
27 | */
28 | const MODE_DISTINCT_NAMES = 3;
29 |
30 | /**
31 | * Names of LTI parameters to be retained in the consumer settings property.
32 | *
33 | * @var array $LEVEL_NAMES
34 | */
35 | private static $LEVEL_NAMES = array('ToolProxy' => 'system',
36 | 'ToolProxyBinding' => 'context',
37 | 'LtiLink' => 'link');
38 |
39 | /**
40 | * The object to which the settings apply (ResourceLink, Context or ToolConsumer).
41 | *
42 | * @var object $source
43 | */
44 | private $source;
45 | /**
46 | * Whether to use the simple JSON format.
47 | *
48 | * @var boolean $simple
49 | */
50 | private $simple;
51 |
52 | /**
53 | * Class constructor.
54 | *
55 | * @param object $source The object to which the settings apply (ResourceLink, Context or ToolConsumer)
56 | * @param string $endpoint Service endpoint
57 | * @param boolean $simple True if the simple media type is to be used (optional, default is true)
58 | */
59 | public function __construct($source, $endpoint, $simple = true)
60 | {
61 |
62 | if (is_a($source, 'IMSGlobal\LTI\ToolProvider\ToolConsumer')) {
63 | $consumer = $source;
64 | } else {
65 | $consumer = $source->getConsumer();
66 | }
67 | if ($simple) {
68 | $mediaType = 'application/vnd.ims.lti.v2.toolsettings.simple+json';
69 | } else {
70 | $mediaType = 'application/vnd.ims.lti.v2.toolsettings+json';
71 | }
72 | parent::__construct($consumer, $endpoint, $mediaType);
73 | $this->source = $source;
74 | $this->simple = $simple;
75 |
76 | }
77 |
78 | /**
79 | * Get the tool settings.
80 | *
81 | * @param int $mode Mode for request (optional, default is current level only)
82 | *
83 | * @return mixed The array of settings if successful, otherwise false
84 | */
85 | public function get($mode = self::MODE_CURRENT_LEVEL) {
86 |
87 | $parameter = array();
88 | if ($mode === self::MODE_ALL_LEVELS) {
89 | $parameter['bubble'] = 'all';
90 | } else if ($mode === self::MODE_DISTINCT_NAMES) {
91 | $parameter['bubble'] = 'distinct';
92 | }
93 | $http = $this->send('GET', $parameter);
94 | if (!$http->ok) {
95 | $response = false;
96 | } else if ($this->simple) {
97 | $response = json_decode($http->response, true);
98 | } else if (isset($http->responseJson->{'@graph'})) {
99 | $response = array();
100 | foreach ($http->responseJson->{'@graph'} as $level) {
101 | $settings = json_decode(json_encode($level->custom), true);
102 | unset($settings['@id']);
103 | $response[self::$LEVEL_NAMES[$level->{'@type'}]] = $settings;
104 | }
105 | }
106 |
107 | return $response;
108 |
109 | }
110 |
111 | /**
112 | * Set the tool settings.
113 | *
114 | * @param array $settings An associative array of settings (optional, default is null)
115 | *
116 | * @return HTTPMessage HTTP object containing request and response details
117 | */
118 | public function set($settings) {
119 |
120 | if (!$this->simple) {
121 | if (is_a($this->source, 'ToolConsumer')) {
122 | $type = 'ToolProxy';
123 | } else if (is_a($this->source, 'ToolConsumer')) {
124 | $type = 'ToolProxyBinding';
125 | } else {
126 | $type = 'LtiLink';
127 | }
128 | $obj = new \stdClass();
129 | $obj->{'@context'} = 'http://purl.imsglobal.org/ctx/lti/v2/ToolSettings';
130 | $obj->{'@graph'} = array();
131 | $level = new \stdClass();
132 | $level->{'@type'} = $type;
133 | $level->{'@id'} = $this->endpoint;
134 | $level->{'custom'} = $settings;
135 | $obj->{'@graph'}[] = $level;
136 | $body = json_encode($obj);
137 | } else {
138 | $body = json_encode($settings);
139 | }
140 |
141 | $response = parent::send('PUT', null, $body);
142 |
143 | return $response->ok;
144 |
145 | }
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/src/ToolProvider/ToolConsumer.php:
--------------------------------------------------------------------------------
1 |
14 | * @copyright IMS Global Learning Consortium Inc
15 | * @date 2016
16 | * @version 3.0.2
17 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
18 | */
19 | class ToolConsumer
20 | {
21 |
22 | /**
23 | * Local name of tool consumer.
24 | *
25 | * @var string $name
26 | */
27 | public $name = null;
28 | /**
29 | * Shared secret.
30 | *
31 | * @var string $secret
32 | */
33 | public $secret = null;
34 | /**
35 | * LTI version (as reported by last tool consumer connection).
36 | *
37 | * @var string $ltiVersion
38 | */
39 | public $ltiVersion = null;
40 | /**
41 | * Name of tool consumer (as reported by last tool consumer connection).
42 | *
43 | * @var string $consumerName
44 | */
45 | public $consumerName = null;
46 | /**
47 | * Tool consumer version (as reported by last tool consumer connection).
48 | *
49 | * @var string $consumerVersion
50 | */
51 | public $consumerVersion = null;
52 | /**
53 | * Tool consumer GUID (as reported by first tool consumer connection).
54 | *
55 | * @var string $consumerGuid
56 | */
57 | public $consumerGuid = null;
58 | /**
59 | * Optional CSS path (as reported by last tool consumer connection).
60 | *
61 | * @var string $cssPath
62 | */
63 | public $cssPath = null;
64 | /**
65 | * Whether the tool consumer instance is protected by matching the consumer_guid value in incoming requests.
66 | *
67 | * @var boolean $protected
68 | */
69 | public $protected = false;
70 | /**
71 | * Whether the tool consumer instance is enabled to accept incoming connection requests.
72 | *
73 | * @var boolean $enabled
74 | */
75 | public $enabled = false;
76 | /**
77 | * Date/time from which the the tool consumer instance is enabled to accept incoming connection requests.
78 | *
79 | * @var int $enableFrom
80 | */
81 | public $enableFrom = null;
82 | /**
83 | * Date/time until which the tool consumer instance is enabled to accept incoming connection requests.
84 | *
85 | * @var int $enableUntil
86 | */
87 | public $enableUntil = null;
88 | /**
89 | * Date of last connection from this tool consumer.
90 | *
91 | * @var int $lastAccess
92 | */
93 | public $lastAccess = null;
94 | /**
95 | * Default scope to use when generating an Id value for a user.
96 | *
97 | * @var int $idScope
98 | */
99 | public $idScope = ToolProvider::ID_SCOPE_ID_ONLY;
100 | /**
101 | * Default email address (or email domain) to use when no email address is provided for a user.
102 | *
103 | * @var string $defaultEmail
104 | */
105 | public $defaultEmail = '';
106 | /**
107 | * Setting values (LTI parameters, custom parameters and local parameters).
108 | *
109 | * @var array $settings
110 | */
111 | public $settings = null;
112 | /**
113 | * Date/time when the object was created.
114 | *
115 | * @var int $created
116 | */
117 | public $created = null;
118 | /**
119 | * Date/time when the object was last updated.
120 | *
121 | * @var int $updated
122 | */
123 | public $updated = null;
124 |
125 | /**
126 | * Consumer ID value.
127 | *
128 | * @var int $id
129 | */
130 | private $id = null;
131 | /**
132 | * Consumer key value.
133 | *
134 | * @var string $key
135 | */
136 | private $key = null;
137 | /**
138 | * Whether the settings value have changed since last saved.
139 | *
140 | * @var boolean $settingsChanged
141 | */
142 | private $settingsChanged = false;
143 | /**
144 | * Data connector object or string.
145 | *
146 | * @var mixed $dataConnector
147 | */
148 | private $dataConnector = null;
149 |
150 | /**
151 | * Class constructor.
152 | *
153 | * @param string $key Consumer key
154 | * @param DataConnector $dataConnector A data connector object
155 | * @param boolean $autoEnable true if the tool consumers is to be enabled automatically (optional, default is false)
156 | */
157 | public function __construct($key = null, $dataConnector = null, $autoEnable = false)
158 | {
159 |
160 | $this->initialize();
161 | if (empty($dataConnector)) {
162 | $dataConnector = DataConnector::getDataConnector();
163 | }
164 | $this->dataConnector = $dataConnector;
165 | if (!empty($key)) {
166 | $this->load($key, $autoEnable);
167 | } else {
168 | $this->secret = DataConnector::getRandomString(32);
169 | }
170 |
171 | }
172 |
173 | /**
174 | * Initialise the tool consumer.
175 | */
176 | public function initialize()
177 | {
178 |
179 | $this->id = null;
180 | $this->key = null;
181 | $this->name = null;
182 | $this->secret = null;
183 | $this->ltiVersion = null;
184 | $this->consumerName = null;
185 | $this->consumerVersion = null;
186 | $this->consumerGuid = null;
187 | $this->profile = null;
188 | $this->toolProxy = null;
189 | $this->settings = array();
190 | $this->protected = false;
191 | $this->enabled = false;
192 | $this->enableFrom = null;
193 | $this->enableUntil = null;
194 | $this->lastAccess = null;
195 | $this->idScope = ToolProvider::ID_SCOPE_ID_ONLY;
196 | $this->defaultEmail = '';
197 | $this->created = null;
198 | $this->updated = null;
199 |
200 | }
201 |
202 | /**
203 | * Initialise the tool consumer.
204 | *
205 | * Pseudonym for initialize().
206 | */
207 | public function initialise()
208 | {
209 |
210 | $this->initialize();
211 |
212 | }
213 |
214 | /**
215 | * Save the tool consumer to the database.
216 | *
217 | * @return boolean True if the object was successfully saved
218 | */
219 | public function save()
220 | {
221 |
222 | $ok = $this->dataConnector->saveToolConsumer($this);
223 | if ($ok) {
224 | $this->settingsChanged = false;
225 | }
226 |
227 | return $ok;
228 |
229 | }
230 |
231 | /**
232 | * Delete the tool consumer from the database.
233 | *
234 | * @return boolean True if the object was successfully deleted
235 | */
236 | public function delete()
237 | {
238 |
239 | return $this->dataConnector->deleteToolConsumer($this);
240 |
241 | }
242 |
243 | /**
244 | * Get the tool consumer record ID.
245 | *
246 | * @return int Consumer record ID value
247 | */
248 | public function getRecordId()
249 | {
250 |
251 | return $this->id;
252 |
253 | }
254 |
255 | /**
256 | * Sets the tool consumer record ID.
257 | *
258 | * @param int $id Consumer record ID value
259 | */
260 | public function setRecordId($id)
261 | {
262 |
263 | $this->id = $id;
264 |
265 | }
266 |
267 | /**
268 | * Get the tool consumer key.
269 | *
270 | * @return string Consumer key value
271 | */
272 | public function getKey()
273 | {
274 |
275 | return $this->key;
276 |
277 | }
278 |
279 | /**
280 | * Set the tool consumer key.
281 | *
282 | * @param string $key Consumer key value
283 | */
284 | public function setKey($key)
285 | {
286 |
287 | $this->key = $key;
288 |
289 | }
290 |
291 | /**
292 | * Get the data connector.
293 | *
294 | * @return mixed Data connector object or string
295 | */
296 | public function getDataConnector()
297 | {
298 |
299 | return $this->dataConnector;
300 |
301 | }
302 |
303 | /**
304 | * Is the consumer key available to accept launch requests?
305 | *
306 | * @return boolean True if the consumer key is enabled and within any date constraints
307 | */
308 | public function getIsAvailable()
309 | {
310 |
311 | $ok = $this->enabled;
312 |
313 | $now = time();
314 | if ($ok && !is_null($this->enableFrom)) {
315 | $ok = $this->enableFrom <= $now;
316 | }
317 | if ($ok && !is_null($this->enableUntil)) {
318 | $ok = $this->enableUntil > $now;
319 | }
320 |
321 | return $ok;
322 |
323 | }
324 |
325 | /**
326 | * Get a setting value.
327 | *
328 | * @param string $name Name of setting
329 | * @param string $default Value to return if the setting does not exist (optional, default is an empty string)
330 | *
331 | * @return string Setting value
332 | */
333 | public function getSetting($name, $default = '')
334 | {
335 |
336 | if (array_key_exists($name, $this->settings)) {
337 | $value = $this->settings[$name];
338 | } else {
339 | $value = $default;
340 | }
341 |
342 | return $value;
343 |
344 | }
345 |
346 | /**
347 | * Set a setting value.
348 | *
349 | * @param string $name Name of setting
350 | * @param string $value Value to set, use an empty value to delete a setting (optional, default is null)
351 | */
352 | public function setSetting($name, $value = null)
353 | {
354 |
355 | $old_value = $this->getSetting($name);
356 | if ($value !== $old_value) {
357 | if (!empty($value)) {
358 | $this->settings[$name] = $value;
359 | } else {
360 | unset($this->settings[$name]);
361 | }
362 | $this->settingsChanged = true;
363 | }
364 |
365 | }
366 |
367 | /**
368 | * Get an array of all setting values.
369 | *
370 | * @return array Associative array of setting values
371 | */
372 | public function getSettings()
373 | {
374 |
375 | return $this->settings;
376 |
377 | }
378 |
379 | /**
380 | * Set an array of all setting values.
381 | *
382 | * @param array $settings Associative array of setting values
383 | */
384 | public function setSettings($settings)
385 | {
386 |
387 | $this->settings = $settings;
388 |
389 | }
390 |
391 | /**
392 | * Save setting values.
393 | *
394 | * @return boolean True if the settings were successfully saved
395 | */
396 | public function saveSettings()
397 | {
398 |
399 | if ($this->settingsChanged) {
400 | $ok = $this->save();
401 | } else {
402 | $ok = true;
403 | }
404 |
405 | return $ok;
406 |
407 | }
408 |
409 | /**
410 | * Check if the Tool Settings service is supported.
411 | *
412 | * @return boolean True if this tool consumer supports the Tool Settings service
413 | */
414 | public function hasToolSettingsService()
415 | {
416 |
417 | $url = $this->getSetting('custom_system_setting_url');
418 |
419 | return !empty($url);
420 |
421 | }
422 |
423 | /**
424 | * Get Tool Settings.
425 | *
426 | * @param boolean $simple True if all the simple media type is to be used (optional, default is true)
427 | *
428 | * @return mixed The array of settings if successful, otherwise false
429 | */
430 | public function getToolSettings($simple = true)
431 | {
432 |
433 | $url = $this->getSetting('custom_system_setting_url');
434 | $service = new Service\ToolSettings($this, $url, $simple);
435 | $response = $service->get();
436 |
437 | return $response;
438 |
439 | }
440 |
441 | /**
442 | * Perform a Tool Settings service request.
443 | *
444 | * @param array $settings An associative array of settings (optional, default is none)
445 | *
446 | * @return boolean True if action was successful, otherwise false
447 | */
448 | public function setToolSettings($settings = array())
449 | {
450 |
451 | $url = $this->getSetting('custom_system_setting_url');
452 | $service = new Service\ToolSettings($this, $url);
453 | $response = $service->set($settings);
454 |
455 | return $response;
456 |
457 | }
458 |
459 | /**
460 | * Add the OAuth signature to an LTI message.
461 | *
462 | * @param string $url URL for message request
463 | * @param string $type LTI message type
464 | * @param string $version LTI version
465 | * @param array $params Message parameters
466 | *
467 | * @return array Array of signed message parameters
468 | */
469 | public function signParameters($url, $type, $version, $params)
470 | {
471 |
472 | if (!empty($url)) {
473 | // Check for query parameters which need to be included in the signature
474 | $queryParams = array();
475 | $queryString = parse_url($url, PHP_URL_QUERY);
476 | if (!is_null($queryString)) {
477 | $queryItems = explode('&', $queryString);
478 | foreach ($queryItems as $item) {
479 | if (strpos($item, '=') !== false) {
480 | list($name, $value) = explode('=', $item);
481 | $queryParams[urldecode($name)] = urldecode($value);
482 | } else {
483 | $queryParams[urldecode($item)] = '';
484 | }
485 | }
486 | }
487 | $params = $params + $queryParams;
488 | // Add standard parameters
489 | $params['lti_version'] = $version;
490 | $params['lti_message_type'] = $type;
491 | $params['oauth_callback'] = 'about:blank';
492 | // Add OAuth signature
493 | $hmacMethod = new OAuth\OAuthSignatureMethod_HMAC_SHA1();
494 | $consumer = new OAuth\OAuthConsumer($this->getKey(), $this->secret, null);
495 | $req = OAuth\OAuthRequest::from_consumer_and_token($consumer, null, 'POST', $url, $params);
496 | $req->sign_request($hmacMethod, $consumer, null);
497 | $params = $req->get_parameters();
498 | // Remove parameters being passed on the query string
499 | foreach (array_keys($queryParams) as $name) {
500 | unset($params[$name]);
501 | }
502 | }
503 |
504 | return $params;
505 |
506 | }
507 |
508 | /**
509 | * Add the OAuth signature to an array of message parameters or to a header string.
510 | *
511 | * @return mixed Array of signed message parameters or header string
512 | */
513 | public static function addSignature($endpoint, $consumerKey, $consumerSecret, $data, $method = 'POST', $type = null)
514 | {
515 |
516 | $params = array();
517 | if (is_array($data)) {
518 | $params = $data;
519 | }
520 | // Check for query parameters which need to be included in the signature
521 | $queryParams = array();
522 | $queryString = parse_url($endpoint, PHP_URL_QUERY);
523 | if (!is_null($queryString)) {
524 | $queryItems = explode('&', $queryString);
525 | foreach ($queryItems as $item) {
526 | if (strpos($item, '=') !== false) {
527 | list($name, $value) = explode('=', $item);
528 | $queryParams[urldecode($name)] = urldecode($value);
529 | } else {
530 | $queryParams[urldecode($item)] = '';
531 | }
532 | }
533 | $params = $params + $queryParams;
534 | }
535 |
536 | if (!is_array($data)) {
537 | // Calculate body hash
538 | $hash = base64_encode(sha1($data, true));
539 | $params['oauth_body_hash'] = $hash;
540 | }
541 |
542 | // Add OAuth signature
543 | $hmacMethod = new OAuth\OAuthSignatureMethod_HMAC_SHA1();
544 | $oauthConsumer = new OAuth\OAuthConsumer($consumerKey, $consumerSecret, null);
545 | $oauthReq = OAuth\OAuthRequest::from_consumer_and_token($oauthConsumer, null, $method, $endpoint, $params);
546 | $oauthReq->sign_request($hmacMethod, $oauthConsumer, null);
547 | $params = $oauthReq->get_parameters();
548 | // Remove parameters being passed on the query string
549 | foreach (array_keys($queryParams) as $name) {
550 | unset($params[$name]);
551 | }
552 |
553 | if (!is_array($data)) {
554 | $header = $oauthReq->to_header();
555 | if (empty($data)) {
556 | if (!empty($type)) {
557 | $header .= "\nAccept: {$type}";
558 | }
559 | } else if (isset($type)) {
560 | $header .= "\nContent-Type: {$type}";
561 | $header .= "\nContent-Length: " . strlen($data);
562 | }
563 | return $header;
564 | } else {
565 | return $params;
566 | }
567 |
568 | }
569 |
570 | /**
571 | * Perform a service request
572 | *
573 | * @param object $service Service object to be executed
574 | * @param string $method HTTP action
575 | * @param string $format Media type
576 | * @param mixed $data Array of parameters or body string
577 | *
578 | * @return HTTPMessage HTTP object containing request and response details
579 | */
580 | public function doServiceRequest($service, $method, $format, $data)
581 | {
582 |
583 | $header = ToolConsumer::addSignature($service->endpoint, $this->getKey(), $this->secret, $data, $method, $format);
584 |
585 | // Connect to tool consumer
586 | $http = new HTTPMessage($service->endpoint, $method, $data, $header);
587 | // Parse JSON response
588 | if ($http->send() && !empty($http->response)) {
589 | $http->responseJson = json_decode($http->response);
590 | $http->ok = !is_null($http->responseJson);
591 | }
592 |
593 | return $http;
594 |
595 | }
596 |
597 | /**
598 | * Load the tool consumer from the database by its record ID.
599 | *
600 | * @param string $id The consumer key record ID
601 | * @param DataConnector $dataConnector Database connection object
602 | *
603 | * @return object ToolConsumer The tool consumer object
604 | */
605 | public static function fromRecordId($id, $dataConnector)
606 | {
607 |
608 | $toolConsumer = new ToolConsumer(null, $dataConnector);
609 |
610 | $toolConsumer->initialize();
611 | $toolConsumer->setRecordId($id);
612 | if (!$dataConnector->loadToolConsumer($toolConsumer)) {
613 | $toolConsumer->initialize();
614 | }
615 |
616 | return $toolConsumer;
617 |
618 | }
619 |
620 |
621 | ###
622 | ### PRIVATE METHOD
623 | ###
624 |
625 | /**
626 | * Load the tool consumer from the database.
627 | *
628 | * @param string $key The consumer key value
629 | * @param boolean $autoEnable True if the consumer should be enabled (optional, default if false)
630 | *
631 | * @return boolean True if the consumer was successfully loaded
632 | */
633 | private function load($key, $autoEnable = false)
634 | {
635 |
636 | $this->key = $key;
637 | $ok = $this->dataConnector->loadToolConsumer($this);
638 | if (!$ok) {
639 | $this->enabled = $autoEnable;
640 | }
641 |
642 | return $ok;
643 |
644 | }
645 |
646 | }
647 |
--------------------------------------------------------------------------------
/src/ToolProvider/ToolProxy.php:
--------------------------------------------------------------------------------
1 |
12 | * @copyright IMS Global Learning Consortium Inc
13 | * @date 2016
14 | * @version 3.0.2
15 | * @license GNU Lesser General Public License, version 3 ()
16 | */
17 | class ToolProxy
18 | {
19 |
20 | /**
21 | * Local id of tool consumer.
22 | *
23 | * @var string $id
24 | */
25 | public $id = null;
26 |
27 | /**
28 | * Tool Consumer for this tool proxy.
29 | *
30 | * @var ToolConsumer $consumer
31 | */
32 | private $consumer = null;
33 | /**
34 | * Tool Consumer ID for this tool proxy.
35 | *
36 | * @var int $consumerId
37 | */
38 | private $consumerId = null;
39 | /**
40 | * Consumer ID value.
41 | *
42 | * @var int $id
43 | */
44 | private $recordId = null;
45 | /**
46 | * Data connector object.
47 | *
48 | * @var DataConnector $dataConnector
49 | */
50 | private $dataConnector = null;
51 | /**
52 | * Tool Proxy document.
53 | *
54 | * @var MediaType\ToolProxy $toolProxy
55 | */
56 | private $toolProxy = null;
57 |
58 | /**
59 | * Class constructor.
60 | *
61 | * @param DataConnector $dataConnector Data connector
62 | * @param string $id Tool Proxy ID (optional, default is null)
63 | */
64 | public function __construct($dataConnector, $id = null)
65 | {
66 |
67 | $this->initialize();
68 | $this->dataConnector = $dataConnector;
69 | if (!empty($id)) {
70 | $this->load($id);
71 | } else {
72 | $this->recordId = DataConnector::getRandomString(32);
73 | }
74 |
75 | }
76 |
77 | /**
78 | * Initialise the tool proxy.
79 | */
80 | public function initialize()
81 | {
82 |
83 | $this->id = null;
84 | $this->recordId = null;
85 | $this->toolProxy = null;
86 | $this->created = null;
87 | $this->updated = null;
88 |
89 | }
90 |
91 | /**
92 | * Initialise the tool proxy.
93 | *
94 | * Pseudonym for initialize().
95 | */
96 | public function initialise()
97 | {
98 |
99 | $this->initialize();
100 |
101 | }
102 |
103 | /**
104 | * Get the tool proxy record ID.
105 | *
106 | * @return int Tool Proxy record ID value
107 | */
108 | public function getRecordId()
109 | {
110 |
111 | return $this->recordId;
112 |
113 | }
114 |
115 | /**
116 | * Sets the tool proxy record ID.
117 | *
118 | * @param int $recordId Tool Proxy record ID value
119 | */
120 | public function setRecordId($recordId)
121 | {
122 |
123 | $this->recordId = $recordId;
124 |
125 | }
126 |
127 | /**
128 | * Get tool consumer.
129 | *
130 | * @return ToolConsumer Tool consumer object for this context.
131 | */
132 | public function getConsumer()
133 | {
134 |
135 | if (is_null($this->consumer)) {
136 | $this->consumer = ToolConsumer::fromRecordId($this->consumerId, $this->getDataConnector());
137 | }
138 |
139 | return $this->consumer;
140 |
141 | }
142 |
143 | /**
144 | * Set tool consumer ID.
145 | *
146 | * @param int $consumerId Tool Consumer ID for this resource link.
147 | */
148 | public function setConsumerId($consumerId)
149 | {
150 |
151 | $this->consumer = null;
152 | $this->consumerId = $consumerId;
153 |
154 | }
155 |
156 | /**
157 | * Get the data connector.
158 | *
159 | * @return DataConnector Data connector object
160 | */
161 | public function getDataConnector()
162 | {
163 |
164 | return $this->dataConnector;
165 |
166 | }
167 |
168 |
169 | ###
170 | ### PRIVATE METHOD
171 | ###
172 |
173 | /**
174 | * Load the tool proxy from the database.
175 | *
176 | * @param string $id The tool proxy id value
177 | *
178 | * @return boolean True if the tool proxy was successfully loaded
179 | */
180 | private function load($id)
181 | {
182 |
183 | $this->initialize();
184 | $this->id = $id;
185 | $ok = $this->dataConnector->loadToolProxy($this);
186 | if (!$ok) {
187 | $this->enabled = $autoEnable;
188 | }
189 |
190 | return $ok;
191 |
192 | }
193 |
194 | }
195 |
--------------------------------------------------------------------------------
/src/ToolProvider/User.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright IMS Global Learning Consortium Inc
11 | * @date 2016
12 | * @version 3.0.2
13 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
14 | */
15 | class User
16 | {
17 |
18 | /**
19 | * User's first name.
20 | *
21 | * @var string $firstname
22 | */
23 | public $firstname = '';
24 | /**
25 | * User's last name (surname or family name).
26 | *
27 | * @var string $lastname
28 | */
29 | public $lastname = '';
30 | /**
31 | * User's fullname.
32 | *
33 | * @var string $fullname
34 | */
35 | public $fullname = '';
36 | /**
37 | * User's email address.
38 | *
39 | * @var string $email
40 | */
41 | public $email = '';
42 | /**
43 | * User's image URI.
44 | *
45 | * @var string $image
46 | */
47 | public $image = '';
48 | /**
49 | * Roles for user.
50 | *
51 | * @var array $roles
52 | */
53 | public $roles = array();
54 | /**
55 | * Groups for user.
56 | *
57 | * @var array $groups
58 | */
59 | public $groups = array();
60 | /**
61 | * User's result sourcedid.
62 | *
63 | * @var string $ltiResultSourcedId
64 | */
65 | public $ltiResultSourcedId = null;
66 | /**
67 | * Date/time the record was created.
68 | *
69 | * @var object $created
70 | */
71 | public $created = null;
72 | /**
73 | * Date/time the record was last updated.
74 | *
75 | * @var object $updated
76 | */
77 | public $updated = null;
78 |
79 | /**
80 | * Resource link object.
81 | *
82 | * @var ResourceLink $resourceLink
83 | */
84 | private $resourceLink = null;
85 | /**
86 | * Resource link record ID.
87 | *
88 | * @var int $resourceLinkId
89 | */
90 | private $resourceLinkId = null;
91 | /**
92 | * User record ID value.
93 | *
94 | * @var string $id
95 | */
96 | private $id = null;
97 | /**
98 | * user ID as supplied in the last connection request.
99 | *
100 | * @var string $ltiUserId
101 | */
102 | public $ltiUserId = null;
103 | /**
104 | * Data connector object or string.
105 | *
106 | * @var mixed $dataConnector
107 | */
108 | private $dataConnector = null;
109 |
110 | /**
111 | * Class constructor.
112 | */
113 | public function __construct()
114 | {
115 |
116 | $this->initialize();
117 |
118 | }
119 |
120 | /**
121 | * Initialise the user.
122 | */
123 | public function initialize()
124 | {
125 |
126 | $this->firstname = '';
127 | $this->lastname = '';
128 | $this->fullname = '';
129 | $this->email = '';
130 | $this->image = '';
131 | $this->roles = array();
132 | $this->groups = array();
133 | $this->ltiResultSourcedId = null;
134 | $this->created = null;
135 | $this->updated = null;
136 |
137 | }
138 |
139 | /**
140 | * Initialise the user.
141 | *
142 | * Pseudonym for initialize().
143 | */
144 | public function initialise()
145 | {
146 |
147 | $this->initialize();
148 |
149 | }
150 |
151 | /**
152 | * Save the user to the database.
153 | *
154 | * @return boolean True if the user object was successfully saved
155 | */
156 | public function save()
157 | {
158 |
159 | if (!empty($this->ltiResultSourcedId) && !is_null($this->resourceLinkId)) {
160 | $ok = $this->getDataConnector()->saveUser($this);
161 | } else {
162 | $ok = true;
163 | }
164 |
165 | return $ok;
166 |
167 | }
168 |
169 | /**
170 | * Delete the user from the database.
171 | *
172 | * @return boolean True if the user object was successfully deleted
173 | */
174 | public function delete()
175 | {
176 |
177 | $ok = $this->getDataConnector()->deleteUser($this);
178 |
179 | return $ok;
180 |
181 | }
182 |
183 | /**
184 | * Get resource link.
185 | *
186 | * @return ResourceLink Resource link object
187 | */
188 | public function getResourceLink()
189 | {
190 |
191 | if (is_null($this->resourceLink) && !is_null($this->resourceLinkId)) {
192 | $this->resourceLink = ResourceLink::fromRecordId($this->resourceLinkId, $this->getDataConnector());
193 | }
194 |
195 | return $this->resourceLink;
196 |
197 | }
198 |
199 | /**
200 | * Get record ID of user.
201 | *
202 | * @return int Record ID of user
203 | */
204 | public function getRecordId()
205 | {
206 |
207 | return $this->id;
208 |
209 | }
210 |
211 | /**
212 | * Set record ID of user.
213 | *
214 | * @param int $id Record ID of user
215 | */
216 | public function setRecordId($id)
217 | {
218 |
219 | $this->id = $id;
220 |
221 | }
222 |
223 | /**
224 | * Set resource link ID of user.
225 | *
226 | * @param int $resourceLinkId Resource link ID of user
227 | */
228 | public function setResourceLinkId($resourceLinkId)
229 | {
230 |
231 | $this->resourceLinkId = $resourceLinkId;
232 |
233 | }
234 |
235 | /**
236 | * Get the data connector.
237 | *
238 | * @return mixed Data connector object or string
239 | */
240 | public function getDataConnector()
241 | {
242 |
243 | return $this->dataConnector;
244 |
245 | }
246 |
247 | /**
248 | * Get the user ID (which may be a compound of the tool consumer and resource link IDs).
249 | *
250 | * @param int $idScope Scope to use for user ID (optional, default is null for consumer default setting)
251 | *
252 | * @return string User ID value
253 | */
254 | public function getId($idScope = null)
255 | {
256 |
257 | if (empty($idScope)) {
258 | if (!is_null($this->resourceLink)) {
259 | $idScope = $this->resourceLink->getConsumer()->idScope;
260 | } else {
261 | $idScope = ToolProvider::ID_SCOPE_ID_ONLY;
262 | }
263 | }
264 | switch ($idScope) {
265 | case ToolProvider::ID_SCOPE_GLOBAL:
266 | $id = $this->getResourceLink()->getKey() . ToolProvider::ID_SCOPE_SEPARATOR . $this->ltiUserId;
267 | break;
268 | case ToolProvider::ID_SCOPE_CONTEXT:
269 | $id = $this->getResourceLink()->getKey();
270 | if ($this->resourceLink->ltiContextId) {
271 | $id .= ToolProvider::ID_SCOPE_SEPARATOR . $this->resourceLink->ltiContextId;
272 | }
273 | $id .= ToolProvider::ID_SCOPE_SEPARATOR . $this->ltiUserId;
274 | break;
275 | case ToolProvider::ID_SCOPE_RESOURCE:
276 | $id = $this->getResourceLink()->getKey();
277 | if ($this->resourceLink->ltiResourceLinkId) {
278 | $id .= ToolProvider::ID_SCOPE_SEPARATOR . $this->resourceLink->ltiResourceLinkId;
279 | }
280 | $id .= ToolProvider::ID_SCOPE_SEPARATOR . $this->ltiUserId;
281 | break;
282 | default:
283 | $id = $this->ltiUserId;
284 | break;
285 | }
286 |
287 | return $id;
288 |
289 | }
290 |
291 | /**
292 | * Set the user's name.
293 | *
294 | * @param string $firstname User's first name.
295 | * @param string $lastname User's last name.
296 | * @param string $fullname User's full name.
297 | */
298 | public function setNames($firstname, $lastname, $fullname)
299 | {
300 |
301 | $names = array(0 => '', 1 => '');
302 | if (!empty($fullname)) {
303 | $this->fullname = trim($fullname);
304 | $names = preg_split("/[\s]+/", $this->fullname, 2);
305 | }
306 | if (!empty($firstname)) {
307 | $this->firstname = trim($firstname);
308 | $names[0] = $this->firstname;
309 | } else if (!empty($names[0])) {
310 | $this->firstname = $names[0];
311 | } else {
312 | $this->firstname = 'User';
313 | }
314 | if (!empty($lastname)) {
315 | $this->lastname = trim($lastname);
316 | $names[1] = $this->lastname;
317 | } else if (!empty($names[1])) {
318 | $this->lastname = $names[1];
319 | } else {
320 | $this->lastname = $this->ltiUserId;
321 | }
322 | if (empty($this->fullname)) {
323 | $this->fullname = "{$this->firstname} {$this->lastname}";
324 | }
325 |
326 | }
327 |
328 | /**
329 | * Set the user's email address.
330 | *
331 | * @param string $email Email address value
332 | * @param string $defaultEmail Value to use if no email is provided (optional, default is none)
333 | */
334 | public function setEmail($email, $defaultEmail = null)
335 | {
336 |
337 | if (!empty($email)) {
338 | $this->email = $email;
339 | } else if (!empty($defaultEmail)) {
340 | $this->email = $defaultEmail;
341 | if (substr($this->email, 0, 1) === '@') {
342 | $this->email = $this->getId() . $this->email;
343 | }
344 | } else {
345 | $this->email = '';
346 | }
347 |
348 | }
349 |
350 | /**
351 | * Check if the user is an administrator (at any of the system, institution or context levels).
352 | *
353 | * @return boolean True if the user has a role of administrator
354 | */
355 | public function isAdmin()
356 | {
357 |
358 | return $this->hasRole('Administrator') || $this->hasRole('urn:lti:sysrole:ims/lis/SysAdmin') ||
359 | $this->hasRole('urn:lti:sysrole:ims/lis/Administrator') || $this->hasRole('urn:lti:instrole:ims/lis/Administrator');
360 |
361 | }
362 |
363 | /**
364 | * Check if the user is staff.
365 | *
366 | * @return boolean True if the user has a role of instructor, contentdeveloper or teachingassistant
367 | */
368 | public function isStaff()
369 | {
370 |
371 | return ($this->hasRole('Instructor') || $this->hasRole('ContentDeveloper') || $this->hasRole('TeachingAssistant'));
372 |
373 | }
374 |
375 | /**
376 | * Check if the user is a learner.
377 | *
378 | * @return boolean True if the user has a role of learner
379 | */
380 | public function isLearner()
381 | {
382 |
383 | return $this->hasRole('Learner');
384 |
385 | }
386 |
387 | /**
388 | * Load the user from the database.
389 | *
390 | * @param int $id Record ID of user
391 | * @param DataConnector $dataConnector Database connection object
392 | *
393 | * @return User User object
394 | */
395 | public static function fromRecordId($id, $dataConnector)
396 | {
397 |
398 | $user = new User();
399 | $user->dataConnector = $dataConnector;
400 | $user->load($id);
401 |
402 | return $user;
403 |
404 | }
405 |
406 | /**
407 | * Class constructor from resource link.
408 | *
409 | * @param ResourceLink $resourceLink Resource_Link object
410 | * @param string $ltiUserId User ID value
411 | * @return User
412 | */
413 | public static function fromResourceLink($resourceLink, $ltiUserId)
414 | {
415 |
416 | $user = new User();
417 | $user->resourceLink = $resourceLink;
418 | if (!is_null($resourceLink)) {
419 | $user->resourceLinkId = $resourceLink->getRecordId();
420 | $user->dataConnector = $resourceLink->getDataConnector();
421 | }
422 | $user->ltiUserId = $ltiUserId;
423 | if (!empty($ltiUserId)) {
424 | $user->load();
425 | }
426 |
427 | return $user;
428 |
429 | }
430 |
431 | ###
432 | ### PRIVATE METHODS
433 | ###
434 |
435 | /**
436 | * Check whether the user has a specified role name.
437 | *
438 | * @param string $role Name of role
439 | *
440 | * @return boolean True if the user has the specified role
441 | */
442 | private function hasRole($role) {
443 |
444 | if (substr($role, 0, 4) !== 'urn:')
445 | {
446 | $role = 'urn:lti:role:ims/lis/' . $role;
447 | }
448 |
449 | return in_array($role, $this->roles);
450 |
451 | }
452 |
453 | /**
454 | * Load the user from the database.
455 | *
456 | * @param int $id Record ID of user (optional, default is null)
457 | *
458 | * @return boolean True if the user object was successfully loaded
459 | */
460 | private function load($id = null)
461 | {
462 |
463 | $this->initialize();
464 | $this->id = $id;
465 | $dataConnector = $this->getDataConnector();
466 | if (!is_null($dataConnector)) {
467 | return $dataConnector->loadUser($this);
468 | }
469 |
470 | return false;
471 | }
472 |
473 | }
474 |
--------------------------------------------------------------------------------