├── .gitignore
├── README.mediawiki
├── classes
├── plugininfo
│ └── ltiproviderextension.php
└── table_syncreport.php
├── db
├── access.php
├── install.xml
├── subplugins.php
└── upgrade.php
├── edit.php
├── edit_form.php
├── extension
└── scormbridge
│ ├── lang
│ └── en
│ │ └── ltiproviderextension_scormbridge.php
│ ├── lib.php
│ ├── logout.php
│ ├── tracking.js.php
│ └── version.php
├── ims-blti
├── LICENSE.txt
├── OAuth.php
├── OAuthBody.php
├── TrivialOAuthDataStore.php
├── blti.php
└── blti_util.php
├── index.php
├── js
└── syncreport.js
├── lang
└── en
│ └── local_ltiprovider.php
├── lib.php
├── locallib.php
├── modinfo
├── chat.php
├── forum.php
└── quiz.php
├── services.php
├── settings.php
├── styles.js.php
├── styles.php
├── syncmembers.php
├── syncreport.php
├── test
├── cron.php
├── forcesendgrades.php
├── lms.php
└── misc.php
├── tool.php
└── version.php
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | /stuff
--------------------------------------------------------------------------------
/README.mediawiki:
--------------------------------------------------------------------------------
1 | IMS LTI PROVIDER PLUGIN FOR MOODLE
2 |
3 | == Description ==
4 |
5 | === About IMS LTI ===
6 |
7 | According IMS:
8 |
9 | ''IMS is developing Learning Tools Interoperability (LTI) to allow remote tools and content to be integrated into a Learning Management System (LMS).''
10 |
11 | === About this plugin ===
12 |
13 | This is a local plugin for making Moodle a LTI provider tool.
14 |
15 | It can be use to provide access to full courses or activities from remote systems (other Moodle installations, Sakai, any LMS LTI consumer compliant)
16 |
17 | Please note that since Moodle 2.2 there is a core activity plugin called "External tool" that is a LTI consumer.
18 |
19 | === Why this plugin ===
20 |
21 | This plugin allow remote systems users (LTI consumers) access to Moodle courses or Moodle activities inside a course.
22 |
23 | Moodle (version 2.2 and onwards) is a LTI consumer tool also.
24 |
25 | You can use this plugin to share activities and courses between Moodle installations without configuring a Moodle network.
26 |
27 | You can also share activities and courses with other LTI consumer tools like Sakai
28 |
29 | You have a detailed view of this plugin possibilities in [http://www.somerandomthoughts.com/blog/2012/01/08/review-lti-provider-for-moodle-2-2/ this post by Gavin Henrik]
30 |
31 | == Main feautres ==
32 |
33 | Provide access to full courses or single activities.
34 |
35 | Change the navigation block of a course or activity for displaying information and links only regarding to your current course.
36 |
37 | Send backs course or activity final grades to the LTI consumer tool
38 |
39 | Modify the course or activity page for hiding the header, footer and left or right blocks
40 |
41 | == Plugin version 2.4 and above features ==
42 |
43 | === The plugin settings link is displayed in the settings block, instead the course one ===
44 |
45 | === Several new settings for control different features of the plugin: ===
46 | - How the user profile is updated
47 | - Default authentication method
48 | - Format of the course shortname, fullname and idnumber (using LTI variables)
49 | - Roles allowed to create new contexts
50 | - Roles allowed to create new resources
51 |
52 | === The remote tool can be opened using the context_id ===
53 |
54 | The tool can be opened using also the context_id instead the current internal Moodle id
55 |
56 | === Support for context memberships service ===
57 |
58 | See http://developers.imsglobal.org/ext_membership.html
59 |
60 | === LTI custom parameters to force settings on SSO ===
61 |
62 | See https://tracker.moodle.org/browse/CONTRIB-4502
63 |
64 | === Service for context (course) creation, using other courses as template ===
65 |
66 | Service URL local/ltiprovider/services.php
67 |
68 | Custom parameters:
69 |
70 | custom_service = create_context
71 |
72 | custom_context_template = Moodle idnumber for a course to be used as a template (the course will be duplicate)
73 |
74 | The course will be created populating the fullname, shortname and idnumber configured in the plugin settings
75 |
76 | === Service for resources duplication ===
77 |
78 | Service URL local/ltiprovider/services.php
79 |
80 | Custom parameters:
81 |
82 | custom_service = duplicate_resource
83 |
84 | custom_resource_link_copy_id = Moodle idnumber of the activity to be duplicated in the current context
85 |
86 | === SSO to resources ===
87 |
88 | If the context resource_link_id matches to an activity idnumber, the user will be redirect to that activity in Moodle
89 |
90 | === Automatic creation of resources (moodle activities) ===
91 |
92 | If this additional parameter is present in the request custom_resource_link_type (mod_forum, etc...) and also resource_link_title and resource_link_description a new moodle activity will be created
93 |
94 | See: https://tracker.moodle.org/browse/CONTRIB-4409
95 |
96 | === Automatic creation of contexts on SSO ===
97 |
98 | Two additional request parameters are required:
99 |
100 | custom_create_context (0 or 1)
101 |
102 | custom_context_template (Moodle course idnumber)
103 |
104 |
105 | === Resources duplication on SSO ==
106 |
107 | Additional parameter required:
108 |
109 | custom_resource_link_copy_id = Moodle idnumber of the activity to be duplicated in the current context
110 |
111 |
112 | == Installing and configuring ==
113 |
114 | Follow instructions here: http://moodle.org/plugins/pluginversions.php?plugin=local_ltiprovider
115 |
116 | '''Important''' If you are using Moodle 2.2 or above, please, be sure that this option:
117 |
118 | Home / > Site administration / > Security / > HTTP security Allow frame embedding
119 |
120 | Is checked, if you leave this option unchecked your provider site will not be "embedable" via an iframe in other sites.
121 |
122 | Once installed, a new link called "LTI Provider" will be displayed in the course navigation block .
123 |
124 | In this page, you can add, modify and disable the tools provided in your course.
125 |
126 | Please note that you can provide a tool n times with different configurations
127 |
128 | There are options for hiding the page header, footer, and left and right blocks and also options for force the Moodle navigation inside a course or activity.
129 |
130 | There are also options for assign different roles in the course or activity to the remote users.
131 |
132 | Once added a tool, you will need to use two settings in your consumer tool:
133 |
134 | * Shared secret
135 |
136 | * Launch URL
137 |
138 | Your consumer tool will ask you for a consumer private key, you can use a random string (please, do not use the shared secret as the private key)
139 |
140 | Configure your consumer tool with these two settings. That's all
141 |
142 | For a more detailed view of the plugin options see [http://www.somerandomthoughts.com/blog/2012/01/08/review-lti-provider-for-moodle-2-2/ this detailed review of the plugin by Gavin Henrik]
143 |
144 | == How it works ==
145 |
146 | === User authentication ===
147 |
148 | * Users are created automatically in their first access to the system.
149 | * Users are created with a hashed username and also with an auth method that disable direct login to Moodle.
150 | * Users are allways enrolled in the course where the activities are.
151 |
152 | You can choose which role has the Learner and the Teacher from the remote system.
153 |
154 | There is also settings for setting Users profile default values (email visible, etc...)
155 |
156 | If you are going to have courses with local and remote users enrolled, I recommend you to create these new roles:
157 |
158 | * External teacher
159 | * External student
160 |
161 | === Grading ===
162 |
163 | A cron job checks periodically activities for sending back grades (overall course grade or activity grade).
164 |
165 | In order to work correctly, your php.ini settings file needs to have the following setting enabled:
166 |
167 | allow_url_fopen = On
168 |
169 |
170 | == Credits ==
171 |
172 | Juan Leyva
173 |
174 | http://moodle.org/user/profile.php?id=49568
175 |
176 | The Universitat Oberta de Catalunya (UOC) has sponsored the version 2.3 of this plugin
177 |
178 | == See also ==
179 |
180 | [http://www.somerandomthoughts.com/blog/2012/01/08/review-lti-provider-for-moodle-2-2/ Review: LTI Provider by Gavin Henrik]
181 |
182 | [http://moodle.org/plugins/pluginversions.php?plugin=local_ltiprovider Plugin entry]
183 |
184 | [https://github.com/jleyva/moodle-local_ltiprovider Github page]
185 |
186 | [[Category: Contributed code]]
187 |
--------------------------------------------------------------------------------
/classes/plugininfo/ltiproviderextension.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * LTI Provider source plugin info.
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2014 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | namespace local_ltiprovider\plugininfo;
27 |
28 | use core\plugininfo\base;
29 |
30 | defined('MOODLE_INTERNAL') || die();
31 |
32 |
33 | class ltiproviderextension extends base {
34 | // Accepts 100% base implementation.
35 |
36 | }
--------------------------------------------------------------------------------
/classes/table_syncreport.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Table for displaying syncreports.
19 | *
20 | *
21 | * @package local
22 | * @subpackage ltiprovider
23 | * @copyright 2017 Juan Leyva , Antoni Bertran
24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 | */
26 |
27 | defined('MOODLE_INTERNAL') || die;
28 | require_once($CFG->libdir . '/tablelib.php');
29 |
30 |
31 | class local_ltiprovider_table_syncreport extends table_sql {
32 |
33 | /** @var stdClass lti_toolprovider parameters */
34 | protected $lti_toolprovider;
35 |
36 | /** @var stdClass filters parameters */
37 | protected $filterparams;
38 |
39 | /**
40 | * Sets up the table_log parameters.
41 | *
42 | * @param string $uniqueid unique id of form.
43 | * @param stdClass $filterparams (optional) filter params.
44 | * - int courseid: id of course
45 | * - int userid: user id
46 | * - int|string modid: Module id or "site_errors" to view site errors
47 | * - int groupid: Group id
48 | * - \core\log\sql_reader logreader: reader from which data will be fetched.
49 | * - int edulevel: educational level.
50 | * - string action: view action
51 | * - int date: Date from which logs to be viewed.
52 | */
53 | public function __construct($uniqueid, $lti_toolprovider, $filterparams=null) {
54 | parent::__construct($uniqueid);
55 |
56 | $this->set_attribute('class', 'generaltable generalbox');
57 | $this->set_attribute('aria-live', 'polite');
58 | $this->lti_toolprovider = $lti_toolprovider;
59 | $this->filterparams = $filterparams;
60 | $params = array('id' => $lti_toolprovider->id);
61 | if ($this->filterparams) {
62 | if (!empty($this->filterparams->firstname)) {
63 | $params['firstname'] = $this->filterparams->firstname;
64 | }
65 | if (!empty($this->filterparams->lastname)) {
66 | $params['lastname'] = $this->filterparams->lastname;
67 | }
68 | if (!empty($this->filterparams->sifirst)) {
69 | $params['sifirst'] = $this->filterparams->sifirst;
70 | }
71 | if (!empty($this->filterparams->silast)) {
72 | $params['silast'] = $this->filterparams->silast;
73 | }
74 | }
75 | $this->define_baseurl(new moodle_url('/local/ltiprovider/syncreport.php', $params));
76 |
77 | $this->define_columns(array('checkbox', 'fullnameuser', 'lastsync', 'lastgrade', 'forcesendbutton', 'serviceurl', 'sourceid'));
78 | $this->define_headers(array(
79 | get_string('select'),
80 | get_string('fullnameuser'),
81 | get_string('time'),
82 | get_string('grade'),
83 | '',
84 | get_string('gradessourceid', 'local_ltiprovider'),
85 | get_string('gradesserviceurl', 'local_ltiprovider')
86 | )
87 | );
88 | $this->no_sorting('checkbox');
89 | $this->no_sorting('forcesendbutton');
90 | $this->no_sorting('serviceurl');
91 | $this->no_sorting('sourceid');
92 | $this->collapsible(true);
93 | $this->sortable(true);
94 | $this->pageable(true);
95 | $this->is_downloadable(false);
96 | }
97 |
98 | /**
99 | * Generate select checkbox column.
100 | *
101 | * @param stdClass $row_report event data.
102 | * @return string HTML for the username column
103 | */
104 | public function col_checkbox($row_report) {
105 |
106 | $checked = '';
107 | return ' ';
108 |
109 | }
110 |
111 | /**
112 | * Generate the username column.
113 | *
114 | * @param stdClass $row_report event data.
115 | * @return string HTML for the username column
116 | */
117 | public function col_fullnameuser($row_report) {
118 |
119 | return fullname($row_report);
120 |
121 | }
122 |
123 | /**
124 | * Generate the lastsync column.
125 | *
126 | * @param stdClass $row_report event data.
127 | * @return string HTML for the time column
128 | */
129 | public function col_time($row_report) {
130 | $recenttimestr = get_string('strftimerecent', 'core_langconfig');
131 | return userdate($row_report->lastsync, $recenttimestr);
132 | }
133 |
134 | /**
135 | * Generate the forcesendbutton column.
136 | *
137 | * @param stdClass $row_report event data.
138 | * @return string HTML for the course column.
139 | */
140 | public function col_forcesendbutton($row_report) {
141 | global $OUTPUT;
142 | $forcesendurl = new \moodle_url('test/forcesendgrades.php', array('toolid' => $this->lti_toolprovider->id, 'userid' => $row_report->id, 'printresponse' => 1));
143 | $forcesendbutton = $OUTPUT->single_button($forcesendurl, get_string('forcesendgrades', 'local_ltiprovider'));
144 |
145 | return $forcesendbutton;
146 | }
147 |
148 |
149 | /**
150 | * Generate the sourceid column.
151 | *
152 | * @param stdClass $row_report event data.
153 | * @return string HTML for the related username column
154 | */
155 | public function col_sourceid($row_report) {
156 |
157 | return s($row_report->sourceid);
158 | }
159 |
160 | /**
161 | * Builds the SQL query.
162 | *
163 | * @param bool $count When true, return the count SQL.
164 | * @return array containing sql to use and an array of params.
165 | */
166 | protected function get_sql_and_params($count = false) {
167 | global $DB;
168 | $fields = 'u.*, g.serviceurl, g.sourceid, g.lastgrade, g.lastsync';
169 | list($extra_sql, $params) = $this->get_sql_filters();
170 |
171 | if ($count) {
172 | $select = "COUNT(1)";
173 | } else {
174 | $select = "$fields";
175 | }
176 |
177 | $sql = "SELECT $select
178 | FROM {local_ltiprovider_user} g JOIN {user} u
179 | ON u.id = g.userid
180 | WHERE g.lastsync > 0 AND g.toolid = :toolid ".
181 | $extra_sql;
182 | $params = array_merge($params, array('toolid' => $this->lti_toolprovider->id));
183 |
184 | // Add order by if needed.
185 | if (!$count && $sqlsort = $this->get_sql_sort()) {
186 | if (strpos($sqlsort, 'fullnameuser')!==false) {
187 | $sqlsort = str_replace('fullnameuser', $DB->sql_fullname(), $sqlsort);
188 | }
189 | $sql .= " ORDER BY " . $sqlsort;
190 | }
191 |
192 | return array($sql, $params);
193 | }
194 |
195 | /**
196 | * Get the SQL filters
197 | * @return array
198 | */
199 | private function get_sql_filters() {
200 | global $DB;
201 | $params = array();
202 | $extra_sql = '';
203 | if ($this->filterparams) {
204 |
205 | if (!empty($this->filterparams->firstname)) {
206 | $field = 'firstname';
207 | $name = 'firstname';
208 | $value = $this->filterparams->firstname;
209 | $extra_sql .= ' AND ' . $DB->sql_like($field, ":$name", false, false);
210 | $params[$name] = "%$value%";
211 | }
212 | if (!empty($this->filterparams->lastname)) {
213 | $field = 'lastname';
214 | $name = 'lastname';
215 | $value = $this->filterparams->lastname;
216 | $extra_sql .= ' AND ' . $DB->sql_like($field, ":$name", false, false);
217 | $params[$name] = "%$value%";
218 | }
219 | if (!empty($this->filterparams->sifirst)) {
220 | $field = 'firstname';
221 | $name = 'sifirst';
222 | $value = $this->filterparams->sifirst;
223 | $extra_sql .= ' AND ' . $DB->sql_like($field, ":$name", false, false);
224 | $params[$name] = "$value%";
225 | }
226 | if (!empty($this->filterparams->silast)) {
227 | $field = 'lastname';
228 | $name = 'silast';
229 | $value = $this->filterparams->silast;
230 | $extra_sql .= ' AND ' . $DB->sql_like($field, ":$name", false, false);
231 | $params[$name] = "$value%";
232 | }
233 | }
234 |
235 | return array($extra_sql, $params);
236 | }
237 |
238 | /**
239 | * Query the reader. Store results in the object for use by build_table.
240 | *
241 | * @param int $pagesize size of page for paginated displayed table.
242 | * @param bool $useinitialsbar do you want to use the initials bar.
243 | */
244 | public function query_db($pagesize, $useinitialsbar = true) {
245 |
246 | global $DB;
247 |
248 | list($countsql, $countparams) = $this->get_sql_and_params(true);
249 | list($sql, $params) = $this->get_sql_and_params();
250 | $total = $DB->count_records_sql($countsql, $countparams);
251 | $this->pagesize($pagesize, $total);
252 | $this->rawdata = $DB->get_records_sql($sql, $params, $this->get_page_start(), $this->get_page_size());
253 |
254 | // Set initial bars.
255 | if ($useinitialsbar) {
256 | $this->initialbars($total > $pagesize);
257 | }
258 |
259 | }
260 |
261 | /**
262 | * Renders html to display a syncreport search form
263 | *
264 | * @param int $tool_id the lti tool id
265 | * @param string $value default value to populate the search field
266 | * @return string
267 | */
268 | function syncreport_search_form($tool_id, $value_firstname = '', $value_lastname = '', $firstinitial='', $lastinitial = '') {
269 | $formid = 'ltiprovidersyncreport';
270 | $inputid = 'coursesearchbox';
271 | $inputidlastname= 'coursesearchboxlastname';
272 | $inputsize = 30;
273 |
274 | $strsearchfirstname = get_string('firstname');
275 | $strsearchlastname = get_string('lastname');
276 | $searchurl = new moodle_url('/local/ltiprovider/syncreport.php');
277 | $url = new moodle_url('/local/ltiprovider/syncreport.php', array('id' => $tool_id));
278 | $strall = get_string('all');
279 | $alpha = explode(',', get_string('alphabet', 'langconfig'));
280 |
281 | $output = html_writer::start_tag('form', array('id' => $formid, 'action' => $searchurl, 'method' => 'get'));
282 | $output .= html_writer::start_tag('fieldset', array('class' => 'ltiprovidersearchbox invisiblefieldset'));
283 | $output .= html_writer::tag('label', $strsearchfirstname.': ', array('for' => $inputid));
284 | $output .= html_writer::empty_tag('input', array('type' => 'text', 'id' => $inputid,
285 | 'size' => $inputsize, 'name' => 'search_firstname', 'value' => s($value_firstname)));
286 | $output .= html_writer::start_tag('span', array('class' => 'initialbar firstinitial'));
287 | if (!empty($firstinitial)) {
288 | $output .= html_writer::link($url.'&sifirst='.'&silast='.$lastinitial, $strall);
289 | } else {
290 | $output .= html_writer::tag('strong', $strall);
291 | }
292 | foreach ($alpha as $letter) {
293 | if ($letter == $firstinitial) {
294 | $output .= html_writer::tag('strong', $letter);
295 | } else {
296 | $output .= html_writer::link($url.'&sifirst='.$letter.'&silast='.$lastinitial, $letter);
297 | }
298 | }
299 | $output .= html_writer::end_tag('span');
300 |
301 |
302 | $output .= html_writer::tag('label', $strsearchlastname.': ', array('for' => $inputidlastname));
303 | $output .= html_writer::empty_tag('input', array('type' => 'text', 'id' => $inputidlastname,
304 | 'size' => $inputsize, 'name' => 'search_lastname', 'value' => s($value_lastname)));
305 | $output .= html_writer::start_tag('span', array('class' => 'initialbar lastinitial'));
306 |
307 | if (!empty($lastinitial)) {
308 | $output .= html_writer::link($url.'&silast='.'&sifirst='.$firstinitial, $strall);
309 | } else {
310 | $output .= html_writer::tag('strong', $strall);
311 | }
312 | foreach ($alpha as $letter) {
313 | if ($letter == $lastinitial) {
314 | $output .= html_writer::tag('strong', $letter);
315 | } else {
316 | $output .= html_writer::link($url.'&silast='.$letter.'&sifirst='.$firstinitial, $letter);
317 | }
318 | }
319 | $output .= html_writer::end_tag('span');
320 |
321 |
322 |
323 | $output .= html_writer::start_tag('div');
324 | $output .= html_writer::empty_tag('input', array('type' => 'submit',
325 | 'value' => get_string('go')));
326 | $output .= html_writer::end_tag('div');
327 | $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'id',
328 | 'value' => $tool_id));
329 | $output .= html_writer::end_tag('fieldset');
330 | $output .= html_writer::end_tag('form');
331 |
332 |
333 |
334 | return $output;
335 | }
336 |
337 |
338 |
339 |
340 | }
341 |
--------------------------------------------------------------------------------
/db/access.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Capability definitions.
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | defined('MOODLE_INTERNAL') || die;
27 |
28 | $capabilities = array(
29 |
30 | 'local/ltiprovider:manage' => array(
31 |
32 | 'riskbitmask' => RISK_SPAM,
33 |
34 | 'captype' => 'write',
35 | 'contextlevel' => CONTEXT_COURSE,
36 | 'archetypes' => array(
37 | 'editingteacher' => CAP_ALLOW,
38 | 'manager' => CAP_ALLOW
39 | )
40 | ),
41 | 'local/ltiprovider:view' => array(
42 |
43 | 'riskbitmask' => RISK_SPAM,
44 |
45 | 'captype' => 'write',
46 | 'contextlevel' => CONTEXT_COURSE,
47 | 'archetypes' => array(
48 | 'teacher' => CAP_ALLOW,
49 | 'editingteacher' => CAP_ALLOW,
50 | 'manager' => CAP_ALLOW
51 | )
52 | )
53 | );
54 |
--------------------------------------------------------------------------------
/db/install.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/db/subplugins.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * This file keeps track of upgrades to the ltiprovider plugin
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2014 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | defined('MOODLE_INTERNAL') || die();
27 |
28 | $subplugins = array(
29 | 'ltiproviderextension' => 'local/ltiprovider/extension',
30 | );
--------------------------------------------------------------------------------
/db/upgrade.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * This file keeps track of upgrades to the ltiprovider plugin
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | defined('MOODLE_INTERNAL') || die();
27 |
28 | function xmldb_local_ltiprovider_upgrade($oldversion) {
29 | global $CFG, $DB, $OUTPUT;
30 |
31 | $dbman = $DB->get_manager();
32 |
33 | if ($oldversion < 2011121703) {
34 |
35 | $table = new xmldb_table('local_ltiprovider');
36 |
37 | $field = new xmldb_field('enrolperiod', XMLDB_TYPE_INTEGER, 10, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0, 'sendgrades');
38 | if (!$dbman->field_exists($table, $field)) {
39 | $dbman->add_field($table, $field);
40 | }
41 |
42 | $field = new xmldb_field('enrolstartdate', XMLDB_TYPE_INTEGER, 10, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0, 'enrolperiod');
43 | if (!$dbman->field_exists($table, $field)) {
44 | $dbman->add_field($table, $field);
45 | }
46 |
47 | $field = new xmldb_field('enrolenddate', XMLDB_TYPE_INTEGER, 10, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0, 'enrolstartdate');
48 | if (!$dbman->field_exists($table, $field)) {
49 | $dbman->add_field($table, $field);
50 | }
51 |
52 | $field = new xmldb_field('maxenrolled', XMLDB_TYPE_INTEGER, 10, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0, 'enrolenddate');
53 | if (!$dbman->field_exists($table, $field)) {
54 | $dbman->add_field($table, $field);
55 | }
56 |
57 | upgrade_plugin_savepoint(true, 2011121703, 'local', 'ltiprovider');
58 | }
59 |
60 | if ($oldversion < 2011121707) {
61 |
62 | $table = new xmldb_table('local_ltiprovider');
63 |
64 | $field = new xmldb_field('userprofileupdate', XMLDB_TYPE_INTEGER, 1, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 1, 'maxenrolled');
65 | if (!$dbman->field_exists($table, $field)) {
66 | $dbman->add_field($table, $field);
67 | }
68 | $field = new xmldb_field('syncmembers', XMLDB_TYPE_INTEGER, 1, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0, 'userprofileupdate');
69 | if (!$dbman->field_exists($table, $field)) {
70 | $dbman->add_field($table, $field);
71 | }
72 | $field = new xmldb_field('syncmode', XMLDB_TYPE_INTEGER, 2, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0, 'syncmembers');
73 | if (!$dbman->field_exists($table, $field)) {
74 | $dbman->add_field($table, $field);
75 | }
76 | $field = new xmldb_field('syncperiod', XMLDB_TYPE_INTEGER, 10, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0, 'syncmode');
77 | if (!$dbman->field_exists($table, $field)) {
78 | $dbman->add_field($table, $field);
79 | }
80 |
81 | $table = new xmldb_table('local_ltiprovider_user');
82 |
83 | $field = new xmldb_field('membershipsurl', XMLDB_TYPE_TEXT, "small", null, null, null, null, 'lastaccess');
84 | if (!$dbman->field_exists($table, $field)) {
85 | $dbman->add_field($table, $field);
86 | }
87 | $field = new xmldb_field('membershipsid', XMLDB_TYPE_TEXT, "small", null, null, null, null, 'membershipsurl');
88 | if (!$dbman->field_exists($table, $field)) {
89 | $dbman->add_field($table, $field);
90 | }
91 |
92 | upgrade_plugin_savepoint(true, 2011121707, 'local', 'ltiprovider');
93 | }
94 |
95 | if ($oldversion < 2014080102) {
96 |
97 | $table = new xmldb_table('local_ltiprovider');
98 |
99 | $field = new xmldb_field('requirecompletion', XMLDB_TYPE_INTEGER, 2, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0, 'lastsync');
100 | if (!$dbman->field_exists($table, $field)) {
101 | $dbman->add_field($table, $field);
102 | }
103 |
104 | upgrade_plugin_savepoint(true, 2014080102, 'local', 'ltiprovider');
105 | }
106 |
107 | if ($oldversion < 2014080103) {
108 |
109 | $table = new xmldb_table('local_ltiprovider');
110 |
111 | $field = new xmldb_field('enrolinst', XMLDB_TYPE_INTEGER, 2, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 1, 'requirecompletion');
112 | if (!$dbman->field_exists($table, $field)) {
113 | $dbman->add_field($table, $field);
114 | }
115 | $field = new xmldb_field('enrollearn', XMLDB_TYPE_INTEGER, 2, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 1, 'enrolinst');
116 | if (!$dbman->field_exists($table, $field)) {
117 | $dbman->add_field($table, $field);
118 | }
119 |
120 | upgrade_plugin_savepoint(true, 2014080103, 'local', 'ltiprovider');
121 | }
122 |
123 | if ($oldversion < 2016020101) {
124 |
125 | $table = new xmldb_table('local_ltiprovider');
126 |
127 | $field = new xmldb_field('addtogroup', XMLDB_TYPE_CHAR, '64', null, XMLDB_NOTNULL, null, null, 'enrollearn');
128 | if (!$dbman->field_exists($table, $field)) {
129 | $dbman->add_field($table, $field);
130 | }
131 | upgrade_plugin_savepoint(true, 2016020101, 'local', 'ltiprovider');
132 | }
133 |
134 | if ($oldversion < 2016020103) {
135 |
136 | $table = new xmldb_table('local_ltiprovider');
137 |
138 | $field = new xmldb_field('sendcompletion', XMLDB_TYPE_INTEGER, 2, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0, 'addtogroup');
139 | if (!$dbman->field_exists($table, $field)) {
140 | $dbman->add_field($table, $field);
141 | }
142 |
143 | upgrade_plugin_savepoint(true, 2016020103, 'local', 'ltiprovider');
144 | }
145 |
146 | return true;
147 | }
148 |
--------------------------------------------------------------------------------
/edit.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Edit a tool provided in a course
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | require_once(dirname(__FILE__) . '/../../config.php');
27 | require_once($CFG->dirroot.'/local/ltiprovider/lib.php');
28 | require_once($CFG->dirroot.'/local/ltiprovider/edit_form.php');
29 |
30 | $id = optional_param('id', -1, PARAM_INT); // user id; -1 if creating new tool
31 | $courseid = optional_param('courseid', 0, PARAM_INT); // course id (defaults to Site)
32 | $delete = optional_param('delete', 0, PARAM_BOOL);
33 | $confirm = optional_param('confirm', 0, PARAM_BOOL);
34 | $hide = optional_param('hide', 0, PARAM_INT);
35 | $show = optional_param('show', 0, PARAM_INT);
36 |
37 | if ($id > 0) {
38 | if (! ($tool = $DB->get_record('local_ltiprovider', array('id'=>$id)))) {
39 | print_error('invalidtoolid', 'local_ltiprovider');
40 | }
41 | $courseid = $tool->courseid;
42 | } else {
43 | $tool = new stdClass();
44 | $tool->id = -1;
45 | $tool->courseid = $courseid;
46 | }
47 |
48 | if (! ($course = $DB->get_record('course', array('id'=>$courseid)))) {
49 | print_error('invalidcourseid', 'error');
50 | }
51 |
52 | $PAGE->set_url('/local/ltiprovider/edit.php', array('id' => $id, 'courseid' => $courseid));
53 |
54 | context_helper::preload_course($course->id);
55 | if (!$context = context_course::instance($course->id)) {
56 | print_error('nocontext');
57 | }
58 |
59 | require_login($course);
60 | require_capability('local/ltiprovider:manage', $context);
61 |
62 | $returnurl = new moodle_url('/local/ltiprovider/index.php', array('courseid' => $courseid));
63 |
64 | $strheading = get_string('providetool', 'local_ltiprovider');
65 | $PAGE->set_context($context);
66 |
67 | if ($delete and $tool->id) {
68 | $PAGE->url->param('delete', 1);
69 | if ($confirm and confirm_sesskey()) {
70 | local_ltiprovider_delete_tool($tool);
71 | redirect($returnurl);
72 | }
73 | $strheading = get_string('deletetool', 'local_ltiprovider');
74 | $PAGE->navbar->add($strheading);
75 | $PAGE->set_title($strheading);
76 | $PAGE->set_heading($COURSE->fullname);
77 |
78 | echo $OUTPUT->header();
79 | echo $OUTPUT->heading($strheading);
80 | $yesurl = new moodle_url('/local/ltiprovider/edit.php', array('id'=>$tool->id, 'delete'=>1, 'confirm'=>1, 'sesskey'=>sesskey()));
81 | $message = get_string('delconfirm', 'local_ltiprovider');
82 | echo $OUTPUT->confirm($message, $yesurl, $returnurl);
83 | echo $OUTPUT->footer();
84 | die;
85 | }
86 |
87 | if ((!empty($hide) or !empty($show)) and $tool->id and confirm_sesskey()) {
88 | if (!empty($hide)) {
89 | $disabled = 1;
90 | } else {
91 | $disabled = 0;
92 | }
93 | $DB->set_field('local_ltiprovider', 'disabled', $disabled, array('id' => $tool->id));
94 | redirect($returnurl);
95 | }
96 |
97 | $PAGE->navbar->add(get_string('pluginname', 'local_ltiprovider'), new moodle_url('/local/ltiprovider/index.php', array('courseid'=>$course->id)));
98 | $PAGE->navbar->add($strheading);
99 | $PAGE->set_title($strheading);
100 | $PAGE->set_heading($course->fullname . ': '.$strheading);
101 |
102 | $editform = new edit_form(null, compact('context', 'courseid', 'tool'));
103 |
104 | $userprofileupdate = get_config('local_ltiprovider', 'userprofileupdate');
105 | if ($userprofileupdate != -1) {
106 | $tool->userprofileupdate = $userprofileupdate;
107 | }
108 |
109 | $editform->set_data($tool);
110 |
111 | if ($editform->is_cancelled()) {
112 | redirect($returnurl);
113 |
114 | } else if ($data = $editform->get_data()) {
115 |
116 | if ($data->id > 0) {
117 | // Update
118 | local_ltiprovider_update_tool($data);
119 | } else {
120 | // Create new
121 | local_ltiprovider_add_tool($data);
122 | }
123 | redirect($returnurl);
124 | }
125 |
126 | echo $OUTPUT->header();
127 | $editform->display();
128 | echo $OUTPUT->footer();
--------------------------------------------------------------------------------
/edit_form.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Edit a tool provided in a course
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | defined('MOODLE_INTERNAL') || die;
27 |
28 | require_once($CFG->dirroot.'/lib/formslib.php');
29 | require_once($CFG->dirroot.'/course/lib.php');
30 |
31 | /// get url variables
32 | class edit_form extends moodleform {
33 |
34 | // Define the form
35 | public function definition () {
36 | global $USER, $CFG, $COURSE;
37 |
38 | $mform =& $this->_form;
39 | $templateuser = $USER;
40 | $context = $this->_customdata['context'];
41 | $tool = $this->_customdata['tool'];
42 |
43 | $mform->addElement('header', 'settingsheader', get_string('toolsettings', 'local_ltiprovider'));
44 |
45 | $tools = array();
46 | $tools[$context->id] = get_string('course');
47 |
48 | $modinfo = get_fast_modinfo($this->_customdata['courseid']);
49 | $mods = $modinfo->get_cms();
50 |
51 | foreach ($mods as $mod) {
52 | $tools[$mod->context->id] = format_string($mod->name);
53 | }
54 |
55 | $mform->addElement('select', 'contextid', get_string('tooltobeprovide', 'local_ltiprovider'), $tools);
56 | $mform->setDefault('contextid', $context->id);
57 |
58 | $mform->addElement('checkbox', 'forcenavigation', null, get_string('forcenavigation', 'local_ltiprovider'));
59 | $mform->setDefault('forcenavigation', 1);
60 |
61 | $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', 'local_ltiprovider'), array('optional' => true, 'defaultunit' => 86400));
62 | $mform->setDefault('enrolperiod', 0);
63 | $mform->addHelpButton('enrolperiod', 'enrolperiod', 'local_ltiprovider');
64 |
65 | $mform->addElement('date_selector', 'enrolstartdate', get_string('enrolstartdate', 'local_ltiprovider'), array('optional' => true));
66 | $mform->setDefault('enrolstartdate', 0);
67 | $mform->addHelpButton('enrolstartdate', 'enrolstartdate', 'local_ltiprovider');
68 |
69 | $mform->addElement('date_selector', 'enrolenddate', get_string('enrolenddate', 'local_ltiprovider'), array('optional' => true));
70 | $mform->setDefault('enrolenddate', 0);
71 | $mform->addHelpButton('enrolenddate', 'enrolenddate', 'local_ltiprovider');
72 |
73 | $mform->addElement('text', 'maxenrolled', get_string('maxenrolled', 'local_ltiprovider'));
74 | $mform->setDefault('maxenrolled', 0);
75 | $mform->addHelpButton('maxenrolled', 'maxenrolled', 'local_ltiprovider');
76 | $mform->setType('maxenrolled', PARAM_INT);
77 |
78 | $mform->addElement('text', 'addtogroup', get_string('addtogroup', 'local_ltiprovider'));
79 | $mform->setDefault('addtogroup', '');
80 | $mform->addHelpButton('addtogroup', 'addtogroup', 'local_ltiprovider');
81 | $mform->setType('addtogroup', PARAM_NOTAGS);
82 |
83 | $assignableroles = get_assignable_roles($context);
84 |
85 | $mform->addElement('checkbox', 'enrolinst', null, get_string('enrolinst', 'local_ltiprovider'));
86 | $mform->setDefault('enrolinst', 1);
87 | $mform->addHelpButton('enrolinst', 'enrolinst', 'local_ltiprovider');
88 | $mform->setAdvanced('enrolinst');
89 | $mform->addElement('checkbox', 'enrollearn', null, get_string('enrollearn', 'local_ltiprovider'));
90 | $mform->setDefault('enrollearn', 1);
91 | $mform->addHelpButton('enrollearn', 'enrollearn', 'local_ltiprovider');
92 | $mform->setAdvanced('enrollearn');
93 |
94 | $mform->addElement('select', 'croleinst', get_string('courseroleinstructor', 'local_ltiprovider'), $assignableroles);
95 | $mform->setDefault('croleinst', '3');
96 | $mform->setAdvanced('croleinst');
97 | $mform->addElement('select', 'crolelearn', get_string('courserolelearner', 'local_ltiprovider'), $assignableroles);
98 | $mform->setDefault('crolelearn', '5');
99 | $mform->setAdvanced('crolelearn');
100 |
101 | $mform->addElement('select', 'aroleinst', get_string('activityroleinstructor', 'local_ltiprovider'), $assignableroles);
102 | $mform->disabledIf('aroleinst', 'contextid', 'eq', $context->id);
103 | $mform->setDefault('aroleinst', '3');
104 | $mform->setAdvanced('aroleinst');
105 | $mform->addElement('select', 'arolelearn', get_string('activityrolelearner', 'local_ltiprovider'), $assignableroles);
106 | $mform->disabledIf('arolelearn', 'contextid', 'eq', $context->id);
107 | $mform->setDefault('arolelearn', '5');
108 | $mform->setAdvanced('arolelearn');
109 |
110 | $mform->addElement('header', 'remotesystem', get_string('remotesystem', 'local_ltiprovider'));
111 |
112 | $mform->addElement('text', 'secret', get_string('secret', 'local_ltiprovider'), 'maxlength="64" size="25"');
113 | $mform->setType('secret', PARAM_MULTILANG);
114 | $mform->setDefault('secret', md5(uniqid(rand(), 1)));
115 | $mform->addRule('secret', get_string('required'), 'required');
116 |
117 |
118 | $choices = core_text::get_encodings();
119 | $mform->addElement('select', 'encoding', get_string('remoteencoding', 'local_ltiprovider'), $choices);
120 | $mform->setDefault('encoding', 'UTF-8');
121 |
122 | $mform->addElement('header', 'defaultheader', get_string('userdefaultvalues', 'local_ltiprovider'));
123 |
124 | $choices = array(0 => get_string('never'), 1 => get_string('always'));
125 | $mform->addElement('select', 'userprofileupdate', get_string('userprofileupdate', 'local_ltiprovider'), $choices);
126 |
127 | $userprofileupdate = get_config('local_ltiprovider', 'userprofileupdate');
128 | if ($userprofileupdate != -1) {
129 | $mform->setDefault('userprofileupdate', $userprofileupdate);
130 | $mform->freeze('userprofileupdate');
131 | } else {
132 | $mform->setDefault('userprofileupdate', 1);
133 | }
134 |
135 | $choices = array(0 => get_string('emaildisplayno'), 1 => get_string('emaildisplayyes'), 2 => get_string('emaildisplaycourse'));
136 | $mform->addElement('select', 'maildisplay', get_string('emaildisplay'), $choices);
137 | $mform->setDefault('maildisplay', 2);
138 |
139 | $mform->addElement('text', 'city', get_string('city'), 'maxlength="100" size="25"');
140 | $mform->setType('city', PARAM_MULTILANG);
141 | if (empty($CFG->defaultcity)) {
142 | $mform->setDefault('city', $templateuser->city);
143 | } else {
144 | $mform->setDefault('city', $CFG->defaultcity);
145 | }
146 |
147 | $mform->addElement('select', 'country', get_string('selectacountry'), get_string_manager()->get_list_of_countries());
148 | if (empty($CFG->country)) {
149 | $mform->setDefault('country', $templateuser->country);
150 | } else {
151 | $mform->setDefault('country', $CFG->country);
152 | }
153 | $mform->setAdvanced('country');
154 |
155 | $choices = core_date::get_list_of_timezones();
156 | $choices['99'] = get_string('serverlocaltime');
157 | $mform->addElement('select', 'timezone', get_string('timezone'), $choices);
158 | $mform->setDefault('timezone', $templateuser->timezone);
159 | $mform->setAdvanced('timezone');
160 |
161 | $mform->addElement('select', 'lang', get_string('preferredlanguage'), get_string_manager()->get_list_of_translations());
162 | $mform->setDefault('lang', $templateuser->lang);
163 | $mform->setAdvanced('lang');
164 |
165 | $mform->addElement('text', 'institution', get_string('institution'), 'maxlength="40" size="25"');
166 | $mform->setType('institution', PARAM_MULTILANG);
167 | $mform->setDefault('institution', $templateuser->institution);
168 | $mform->setAdvanced('institution');
169 |
170 | $mform->addElement('header', 'outcomes', get_string('outcomessettings', 'local_ltiprovider'));
171 | $mform->addElement('checkbox', 'sendgrades', null, get_string('sendgrades', 'local_ltiprovider'));
172 | $mform->setDefault('sendgrades', 1);
173 |
174 | $mform->addElement('advcheckbox', 'requirecompletion', null, get_string('requirecompletion', 'local_ltiprovider'));
175 | $mform->setDefault('requirecompletion', 0);
176 | $mform->disabledIf('requirecompletion', 'sendgrades');
177 |
178 | $mform->addElement('advcheckbox', 'sendcompletion', null, get_string('sendcompletion', 'local_ltiprovider'));
179 | $mform->setDefault('sendcompletion', 0);
180 | $mform->disabledIf('sendcompletion', 'sendgrades');
181 | $mform->addHelpButton('sendcompletion', 'sendcompletion', 'local_ltiprovider');
182 |
183 | $mform->addElement('header', 'memberships', get_string('membershipsettings', 'local_ltiprovider'));
184 | $mform->addElement('checkbox', 'syncmembers', null, get_string('enablememberssync', 'local_ltiprovider'));
185 | $mform->disabledIf('syncmembers', 'contextid', 'neq', $context->id);
186 |
187 | $options = array();
188 | $options[30*60] = '30 ' . get_string('minutes');
189 | $options[60*60] = '1 ' . get_string('hour');
190 | $options[2*60*60] = '2 ' . get_string('hours');
191 | $options[6*60*60] = '6 ' . get_string('hours');
192 | $options[12*60*60] = '12 ' . get_string('hours');
193 | $options[24*60*60] = '24 ' . get_string('hours');
194 | $mform->addElement('select', 'syncperiod', get_string('syncperiod', 'local_ltiprovider'), $options);
195 | $mform->setDefault('syncperiod', 30*60);
196 | $mform->disabledIf('syncperiod', 'contextid', 'neq', $context->id);
197 |
198 | $options = array();
199 | $options[1] = get_string('enrolandunenrol' , 'local_ltiprovider');
200 | $options[2] = get_string('enrolnew' , 'local_ltiprovider');
201 | $options[3] = get_string('unenrolmissing' , 'local_ltiprovider');
202 | $mform->addElement('select', 'syncmode', get_string('syncmode', 'local_ltiprovider'), $options);
203 | $mform->setDefault('syncmode', 1);
204 | $mform->disabledIf('syncmode', 'contextid', 'neq', $context->id);
205 |
206 |
207 | $mform->addElement('header', 'layoutandcss', get_string('layoutandcss', 'local_ltiprovider'));
208 |
209 | $mform->addElement('checkbox', 'hidepageheader', null, get_string('hidepageheader', 'local_ltiprovider'));
210 | $mform->addElement('checkbox', 'hidepagefooter', null, get_string('hidepagefooter', 'local_ltiprovider'));
211 | $mform->addElement('checkbox', 'hideleftblocks', null, get_string('hideleftblocks', 'local_ltiprovider'));
212 | $mform->addElement('checkbox', 'hiderightblocks', null, get_string('hiderightblocks', 'local_ltiprovider'));
213 | $mform->setAdvanced('hideleftblocks');
214 | $mform->setAdvanced('hiderightblocks');
215 |
216 | $editoroptions = array();
217 | $displayoptions = array('rows'=>'4', 'cols'=>'');
218 | $mform->addElement('textarea', 'customcss', get_string('customcss', 'local_ltiprovider'), $displayoptions, $editoroptions);
219 | $mform->setAdvanced('customcss');
220 |
221 | $mform->addElement('hidden', 'id');
222 | $mform->setType('id', PARAM_INT);
223 |
224 | $mform->addElement('hidden', 'courseid');
225 | $mform->setType('courseid', PARAM_INT);
226 |
227 | local_ltiprovider_call_hook("add_settings", (object) array('mform' => $mform,
228 | 'customdata' => $this->_customdata,
229 | 'tool' => $tool));
230 |
231 | $this->add_action_buttons();
232 | }
233 |
234 | public function validation($data, $files) {
235 | global $COURSE, $DB, $CFG;
236 |
237 | $errors = parent::validation($data, $files);
238 |
239 | if (!empty($data['enrolenddate']) and $data['enrolenddate'] < $data['enrolstartdate']) {
240 | $errors['enrolenddate'] = get_string('enrolenddaterror', 'local_ltiprovider');
241 | }
242 |
243 | if (!empty($data['requirecompletion']) || !empty($data['sendcompletion'])) {
244 | $completion = new completion_info($COURSE);
245 | $moodlecontext = $DB->get_record('context', array('id' => $data['contextid']));
246 | if ($moodlecontext->contextlevel == CONTEXT_MODULE) {
247 | $cm = get_coursemodule_from_id(false, $moodlecontext->instanceid, 0, false, MUST_EXIST);
248 | } else {
249 | $cm = null;
250 | }
251 |
252 | if (! $completion->is_enabled($cm)) {
253 | if (!empty($data['requirecompletion'])) {
254 | $errors['requirecompletion'] = get_string('errorcompletionenabled', 'local_ltiprovider');
255 | }
256 | if (!empty($data['sendcompletion'])) {
257 | $errors['sendcompletion'] = get_string('errorcompletionenabled', 'local_ltiprovider');
258 | }
259 | }
260 | }
261 |
262 | return $errors;
263 | }
264 |
265 | }
266 |
--------------------------------------------------------------------------------
/extension/scormbridge/lang/en/ltiproviderextension_scormbridge.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Strings for component ltiproviderextension_scormbridge.
19 | *
20 | * @package ltiproviderextension
21 | * @subpackage scormbridge
22 | * @copyright 2014 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | $string['pluginname'] = 'SCORM bridge';
--------------------------------------------------------------------------------
/extension/scormbridge/lib.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Library functions.
19 | *
20 | * @package ltiproviderextension
21 | * @subpackage scormbridge
22 | * @copyright 2014 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | defined('MOODLE_INTERNAL') || die;
27 |
28 |
29 | /**
30 | * SCORM Bridge, we detect changes in quizzes for submitting back to the parent Window that will process SCORM API messages
31 | *
32 | * @param object $nav Global navigation object
33 | */
34 | function ltiproviderextension_scormbridge_navigation($nav) {
35 | global $DB, $SESSION, $USER, $PAGE;
36 |
37 | // First we need to check if we are in a LTI session and also if the module is a quiz.
38 | if (isset($SESSION->ltiprovider)) {
39 | $context = $SESSION->ltiprovider->context;
40 | if ($context->contextlevel == CONTEXT_MODULE) {
41 | $cm = get_coursemodule_from_id(false, $context->instanceid, 0, false, MUST_EXIST);
42 |
43 | if ($cm->modname == "quiz") {
44 | $url = new moodle_url('/local/ltiprovider/extension/scormbridge/tracking.js.php',
45 | array('quizid' => $cm->instance, 'rand' => rand(0, 1000)));
46 | $PAGE->requires->js($url);
47 |
48 | }
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/extension/scormbridge/logout.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Force uses logout
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | require_once(dirname(__FILE__) . '../../../../../config.php');
27 |
28 | $toolid = required_param('toolid', PARAM_INT);
29 |
30 | if ($tool = $DB->get_record('local_ltiprovider', array('id' => $toolid))) {
31 | // Force logout.
32 | $authsequence = get_enabled_auth_plugins();
33 | foreach ($authsequence as $authname) {
34 | $authplugin = get_auth_plugin($authname);
35 | $authplugin->logoutpage_hook();
36 | }
37 |
38 | require_logout();
39 | }
40 |
--------------------------------------------------------------------------------
/extension/scormbridge/tracking.js.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Retrieve the quiz tracking: Attempts and grades
19 | * Information can be send using Push (postMessage) or retreived by pulling (jsonp)
20 | *
21 | * @package local
22 | * @subpackage ltiprovider
23 | * @copyright 2011 Juan Leyva
24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 | */
26 |
27 | require_once(dirname(__FILE__) . '../../../../../config.php');
28 |
29 | $quizid = optional_param('quizid', 0, PARAM_INT);
30 | $toolid = optional_param('toolid', 0, PARAM_INT);
31 | $jsonp = optional_param('jsonp', "", PARAM_RAW); // Whether to use a function padding for the response.
32 |
33 | if (!$quizid and $toolid) {
34 | if (isset($SESSION->ltiprovider)) {
35 | $context = $SESSION->ltiprovider->context;
36 | if ($context->contextlevel == CONTEXT_MODULE) {
37 | $cm = get_coursemodule_from_id(false, $context->instanceid, 0, false, MUST_EXIST);
38 |
39 | if ($cm->modname == "quiz") {
40 | $quizid = $cm->instance;
41 | }
42 | }
43 | }
44 | }
45 |
46 | if (!$quizid) {
47 | die;
48 | }
49 |
50 | // Check that the user is logged and the LTI session launched.
51 | if (isset($SESSION->ltiprovider) and isloggedin()) {
52 | $conditions = array("userid" => $USER->id, "quiz" => $quizid);
53 | $attempts = $DB->get_records('quiz_attempts', $conditions, "id ASC");
54 | $grades = $DB->get_records('quiz_grades', $conditions, "id ASC");
55 | } else {
56 | $attempts = array();
57 | $grades = array();
58 | }
59 |
60 | $data = array(
61 | 'grades' => array_values($grades),
62 | 'attempts' => array_values($attempts)
63 | );
64 |
65 | $data = json_encode($data);
66 |
67 | // Using JSONP, we use a padding function that must be implemented in the consumer side.
68 | if ($jsonp) {
69 | ?>
70 |
71 | ('');
72 |
73 |
76 |
77 | parent.postMessage('', "*");
78 |
79 | .
16 |
17 | /**
18 | * Version details.
19 | *
20 | * @package ltiproviderextension
21 | * @subpackage scormbridge
22 | * @copyright 2014 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | defined('MOODLE_INTERNAL') || die;
27 |
28 | $plugin->version = 2014080100;
29 | $plugin->requires = 2014051200; // Require Moodle version (2.7).
30 | $plugin->component = "ltiproviderextension_scormbridge";
31 |
--------------------------------------------------------------------------------
/ims-blti/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2007 Andy Smith
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/ims-blti/OAuthBody.php:
--------------------------------------------------------------------------------
1 | add_consumer($oauth_consumer_key, $oauth_consumer_secret);
39 |
40 | $server = new OAuthServer($store);
41 |
42 | $method = new OAuthSignatureMethod_HMAC_SHA1();
43 | $server->add_signature_method($method);
44 | $request = OAuthRequest::from_request();
45 |
46 | global $LastOAuthBodyBaseString;
47 | $LastOAuthBodyBaseString = $request->get_signature_base_string();
48 | // echo($LastOAuthBodyBaseString."\n");
49 |
50 | try {
51 | $server->verify_request($request);
52 | } catch (Exception $e) {
53 | $message = $e->getMessage();
54 | throw new Exception("OAuth signature failed: " . $message);
55 | }
56 |
57 | $postdata = file_get_contents('php://input');
58 | // echo($postdata);
59 |
60 | $hash = base64_encode(sha1($postdata, TRUE));
61 |
62 | if ( $hash != $oauth_body_hash ) {
63 | throw new Exception("OAuth oauth_body_hash mismatch");
64 | }
65 |
66 | return $postdata;
67 | }
68 |
69 | function sendOAuthBodyPOST($method, $endpoint, $oauth_consumer_key, $oauth_consumer_secret, $content_type, $body)
70 | {
71 | $hash = base64_encode(sha1($body, TRUE));
72 |
73 | $parms = array('oauth_body_hash' => $hash);
74 |
75 | $test_token = '';
76 | $hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
77 | $test_consumer = new OAuthConsumer($oauth_consumer_key, $oauth_consumer_secret, NULL);
78 |
79 | $acc_req = OAuthRequest::from_consumer_and_token($test_consumer, $test_token, $method, $endpoint, $parms);
80 | $acc_req->sign_request($hmac_method, $test_consumer, $test_token);
81 |
82 | // Pass this back up "out of band" for debugging
83 | global $LastOAuthBodyBaseString;
84 | $LastOAuthBodyBaseString = $acc_req->get_signature_base_string();
85 | // echo($LastOAuthBodyBaseString."\m");
86 |
87 | $headers = array();
88 | $headers[] = $acc_req->to_header();
89 | $headers[] = "Content-type: " . $content_type;
90 |
91 | $curl = new \curl();
92 | $curl->setHeader($headers);
93 | $response = $curl->post($endpoint, $body);
94 |
95 | return $response;
96 | }
97 |
98 | function sendOAuthParamsPOST($method, $endpoint, $oauth_consumer_key, $oauth_consumer_secret, $content_type, $params)
99 | {
100 |
101 | if (is_array($params)) {
102 | $body = http_build_query($params, '', '&');
103 | } else {
104 | $body = $params;
105 | }
106 |
107 | $hash = base64_encode(sha1($body, TRUE));
108 |
109 | $parms = $params;
110 | $parms['oauth_body_hash'] = $hash;
111 |
112 | $test_token = '';
113 | $hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
114 | $test_consumer = new OAuthConsumer($oauth_consumer_key, $oauth_consumer_secret, NULL);
115 |
116 | $acc_req = OAuthRequest::from_consumer_and_token($test_consumer, $test_token, $method, $endpoint, $parms);
117 | $acc_req->sign_request($hmac_method, $test_consumer, $test_token);
118 |
119 | // Pass this back up "out of band" for debugging
120 | global $LastOAuthBodyBaseString;
121 | $LastOAuthBodyBaseString = $acc_req->get_signature_base_string();
122 | // echo($LastOAuthBodyBaseString."\m");
123 |
124 | $header = $acc_req->to_header();
125 | $header = $header . "\r\nContent-type: " . $content_type . "\r\n";
126 |
127 | $params = array('http' => array(
128 | 'method' => 'POST',
129 | 'content' => $body,
130 | 'header' => $header
131 | ));
132 | $ctx = stream_context_create($params);
133 | $fp = @fopen($endpoint, 'rb', false, $ctx);
134 | if (!$fp) {
135 | throw new \Exception("Problem with $endpoint, $php_errormsg");
136 | }
137 | $response = @stream_get_contents($fp);
138 | if ($response === false) {
139 | throw new \Exception("Problem reading data from $endpoint, $php_errormsg");
140 | }
141 | return $response;
142 | }
143 |
144 | ?>
145 |
--------------------------------------------------------------------------------
/ims-blti/TrivialOAuthDataStore.php:
--------------------------------------------------------------------------------
1 | consumers[$consumer_key] = $consumer_secret;
13 | }
14 |
15 | function lookup_consumer($consumer_key) {
16 | if ( strpos($consumer_key, "http://" ) === 0 ) {
17 | $consumer = new OAuthConsumer($consumer_key,"secret", NULL);
18 | return $consumer;
19 | }
20 | if ( $this->consumers[$consumer_key] ) {
21 | $consumer = new OAuthConsumer($consumer_key,$this->consumers[$consumer_key], NULL);
22 | return $consumer;
23 | }
24 | return NULL;
25 | }
26 |
27 | function lookup_token($consumer, $token_type, $token) {
28 | return new OAuthToken($consumer, "");
29 | }
30 |
31 | // Return NULL if the nonce has not been used
32 | // Return $nonce if the nonce was previously used
33 | function lookup_nonce($consumer, $token, $nonce, $timestamp) {
34 | // Should add some clever logic to keep nonces from
35 | // being reused - for no we are really trusting
36 | // that the timestamp will save us
37 | return NULL;
38 | }
39 |
40 | function new_request_token($consumer) {
41 | return NULL;
42 | }
43 |
44 | function new_access_token($token, $consumer) {
45 | return NULL;
46 | }
47 | }
48 | ?>
--------------------------------------------------------------------------------
/ims-blti/blti.php:
--------------------------------------------------------------------------------
1 | 0 ) {
40 | $row = $_SESSION['_basiclti_lti_row'];
41 | if ( isset($row) ) $this->row = $row;
42 | $context_id = $_SESSION['_basiclti_lti_context_id'];
43 | if ( isset($context_id) ) $this->context_id = $context_id;
44 | $info = $_SESSION['_basic_lti_context'];
45 | if ( isset($info) ) {
46 | $this->info = $info;
47 | $this->valid = true;
48 | return;
49 | }
50 | $this->message = "Could not find context in session";
51 | return;
52 | }
53 | $this->message = "Session not available";
54 | return;
55 | }
56 |
57 | // Insure we have a valid launch
58 | if ( empty($_REQUEST["oauth_consumer_key"]) ) {
59 | $this->message = "Missing oauth_consumer_key in request";
60 | return;
61 | }
62 | $oauth_consumer_key = $_REQUEST["oauth_consumer_key"];
63 |
64 | // Find the secret - either form the parameter as a string or
65 | // look it up in a database from parameters we are given
66 | $secret = false;
67 | $row = false;
68 | if ( is_string($parm) ) {
69 | $secret = $parm;
70 | } else if ( ! is_array($parm) ) {
71 | $this->message = "Constructor requires a secret or database information.";
72 | return;
73 | }
74 |
75 | // Verify the message signature
76 | $store = new ltiprovider\TrivialOAuthDataStore();
77 | $store->add_consumer($oauth_consumer_key, $secret);
78 |
79 | $server = new ltiprovider\OAuthServer($store);
80 |
81 | $method = new ltiprovider\OAuthSignatureMethod_HMAC_SHA1();
82 | $server->add_signature_method($method);
83 | $request = ltiprovider\OAuthRequest::from_request();
84 |
85 | $this->basestring = $request->get_signature_base_string();
86 |
87 | try {
88 | $server->verify_request($request);
89 | $this->valid = true;
90 | } catch (Exception $e) {
91 | $this->message = $e->getMessage();
92 | return;
93 | }
94 |
95 | // Store the launch information in the session for later
96 | $newinfo = array();
97 | foreach($_POST as $key => $value ) {
98 | if ( $key == "basiclti_submit" ) continue;
99 | if ( strpos($key, "oauth_") === false ) {
100 | $newinfo[$key] = $value;
101 | continue;
102 | }
103 | if ( $key == "oauth_consumer_key" ) {
104 | $newinfo[$key] = $value;
105 | continue;
106 | }
107 | }
108 |
109 | //Added abertranb to decode base 64 20120801
110 | if (isset($newinfo['custom_lti_message_encoded_base64']) && $newinfo['custom_lti_message_encoded_base64']==1){
111 | $newinfo = $this->decodeBase64($newinfo);
112 | }
113 |
114 | $this->info = $newinfo;
115 |
116 | if ( $usesession == true and strlen(session_id()) > 0 ) {
117 | $_SESSION['_basic_lti_context'] = $this->info;
118 | unset($_SESSION['_basiclti_lti_row']);
119 | unset($_SESSION['_basiclti_lti_context_id']);
120 | if ( $this->row ) $_SESSION['_basiclti_lti_row'] = $this->row;
121 | if ( $this->context_id ) $_SESSION['_basiclti_lti_context_id'] = $this->context_id;
122 | }
123 |
124 | if ( $this->valid && $doredirect ) {
125 | $this->redirect();
126 | $this->complete = true;
127 | }
128 | }
129 |
130 | function addSession($location) {
131 | if ( ini_get('session.use_cookies') == 0 ) {
132 | if ( strpos($location,'?') > 0 ) {
133 | $location = $location . '&';
134 | } else {
135 | $location = $location . '?';
136 | }
137 | $location = $location . session_name() . '=' . session_id();
138 | }
139 | return $location;
140 | }
141 |
142 | function isInstructor() {
143 | $roles = $this->info['roles'];
144 | $roles = strtolower($roles);
145 | if ( ! ( strpos($roles,"instructor") === false ) ) return true;
146 | if ( ! ( strpos($roles,"administrator") === false ) ) return true;
147 | return false;
148 | }
149 |
150 | function getUserEmail() {
151 | # set default email in the event privacy settings don't pass in email.
152 | $email = md5($this->info['user_id']) . "@ltiuser.com";
153 | if ( !empty($this->info['lis_person_contact_email_primary']) ) $email = $this->info['lis_person_contact_email_primary'];
154 | # Sakai Hack
155 | if ( !empty($this->info['lis_person_contact_emailprimary']) ) $email = $this->info['lis_person_contact_emailprimary'];
156 | return $email;
157 | }
158 |
159 | function getUserShortName() {
160 | $email = $this->getUserEmail();
161 | $givenname = $this->info['lis_person_name_given'];
162 | $familyname = $this->info['lis_person_name_family'];
163 | $fullname = $this->info['lis_person_name_full'];
164 | if ( strlen($email) > 0 ) return $email;
165 | if ( strlen($givenname) > 0 ) return $givenname;
166 | if ( strlen($familyname) > 0 ) return $familyname;
167 | return $this->getUserName();
168 | }
169 |
170 | function getUserName() {
171 | $givenname = $this->info['lis_person_name_given'];
172 | $familyname = $this->info['lis_person_name_family'];
173 | $fullname = $this->info['lis_person_name_full'];
174 | if ( strlen($fullname) > 0 ) return $fullname;
175 | if ( strlen($familyname) > 0 and strlen($givenname) > 0 ) return $givenname + $familyname;
176 | if ( strlen($givenname) > 0 ) return $givenname;
177 | if ( strlen($familyname) > 0 ) return $familyname;
178 | return $this->getUserEmail();
179 | }
180 |
181 | function getUserKey() {
182 | $oauth = $this->info['oauth_consumer_key'];
183 | $id = $this->info['user_id'];
184 | if ( strlen($id) > 0 and strlen($oauth) > 0 ) return $oauth . ':' . $id;
185 | return false;
186 | }
187 |
188 | function getUserImage() {
189 | $image = $this->info['user_image'];
190 | if ( strlen($image) > 0 ) return $image;
191 | $email = $this->getUserEmail();
192 | if ( $email === false ) return false;
193 | $size = 40;
194 | $grav_url = $_SERVER['HTTPS'] ? 'https://' : 'http://';
195 | $grav_url = $grav_url . "www.gravatar.com/avatar.php?gravatar_id=".md5( strtolower($email) )."&size=".$size;
196 | return $grav_url;
197 | }
198 |
199 | function getResourceKey() {
200 | $oauth = $this->info['oauth_consumer_key'];
201 | $id = $this->info['resource_link_id'];
202 | if ( strlen($id) > 0 and strlen($oauth) > 0 ) return $oauth . ':' . $id;
203 | return false;
204 | }
205 |
206 | function getResourceTitle() {
207 | $title = $this->info['resource_link_title'];
208 | if ( strlen($title) > 0 ) return $title;
209 | return false;
210 | }
211 |
212 | function getConsumerKey() {
213 | $oauth = $this->info['oauth_consumer_key'];
214 | return $oauth;
215 | }
216 |
217 | function getCourseKey() {
218 | if ( $this->context_id ) return $this->context_id;
219 | $oauth = $this->info['oauth_consumer_key'];
220 | $id = $this->info['context_id'];
221 | if ( strlen($id) > 0 and strlen($oauth) > 0 ) return $oauth . ':' . $id;
222 | return false;
223 | }
224 |
225 | function getCourseName() {
226 | $label = $this->info['context_label'];
227 | $title = $this->info['context_title'];
228 | $id = $this->info['context_id'];
229 | if ( strlen($label) > 0 ) return $label;
230 | if ( strlen($title) > 0 ) return $title;
231 | if ( strlen($id) > 0 ) return $id;
232 | return false;
233 | }
234 |
235 | // TODO: Add javasript version if headers are already sent
236 | function redirect() {
237 | $host = $_SERVER['HTTP_HOST'];
238 | $uri = $_SERVER['PHP_SELF'];
239 | $location = $_SERVER['HTTPS'] ? 'https://' : 'http://';
240 | $location = $location . $host . $uri;
241 | $location = $this->addSession($location);
242 | header("Location: $location");
243 | }
244 |
245 | function dump() {
246 | if ( ! $this->valid or $this->info == false ) return "Context not valid\n";
247 | $ret = "";
248 | if ( $this->isInstructor() ) {
249 | $ret .= "isInstructor() = true\n";
250 | } else {
251 | $ret .= "isInstructor() = false\n";
252 | }
253 | $ret .= "getUserKey() = ".$this->getUserKey()."\n";
254 | $ret .= "getUserEmail() = ".$this->getUserEmail()."\n";
255 | $ret .= "getUserShortName() = ".$this->getUserShortName()."\n";
256 | $ret .= "getUserName() = ".$this->getUserName()."\n";
257 | $ret .= "getUserImage() = ".$this->getUserImage()."\n";
258 | $ret .= "getResourceKey() = ".$this->getResourceKey()."\n";
259 | $ret .= "getResourceTitle() = ".$this->getResourceTitle()."\n";
260 | $ret .= "getCourseName() = ".$this->getCourseName()."\n";
261 | $ret .= "getCourseKey() = ".$this->getCourseKey()."\n";
262 | $ret .= "getConsumerKey() = ".$this->getConsumerKey()."\n";
263 | return $ret;
264 | }
265 |
266 | /**
267 | * Data submitter are in base64 then we have to decode
268 | * @author Antoni Bertran (antoni@tresipunt.com)
269 | * @param $info array
270 | * @date 20120801
271 | */
272 | function decodeBase64($info) {
273 | $keysNoEncode = array("lti_version", "lti_message_type", "tool_consumer_instance_description", "tool_consumer_instance_guid", "oauth_consumer_key", "custom_lti_message_encoded_base64", "oauth_nonce", "oauth_version", "oauth_callback", "oauth_timestamp", "basiclti_submit", "oauth_signature_method", "ext_ims_lis_memberships_id", "ext_ims_lis_memberships_url");
274 | foreach ($info as $key => $item){
275 | if (!in_array($key, $keysNoEncode))
276 | $info[$key] = base64_decode($item);
277 | }
278 | return $info;
279 | }
280 |
281 | }
282 |
283 | ?>
284 |
--------------------------------------------------------------------------------
/ims-blti/blti_util.php:
--------------------------------------------------------------------------------
1 | "120988f929-274612",
11 | "resource_link_title" => "Weekly Blog",
12 | "resource_link_description" => "Each student needs to reflect on the weekly reading. These should be one paragraph long.",
13 | "user_id" => "292832126",
14 | "roles" => "Instructor", // or Learner
15 | "lis_person_name_full" => 'Jane Q. Public',
16 | "lis_person_contact_email_primary" => "user@school.edu",
17 | "lis_person_sourcedid" => "school.edu:user",
18 | "context_id" => "456434513",
19 | "context_title" => "Design of Personal Environments",
20 | "context_label" => "SI182",
21 | );
22 |
23 | return $parms;
24 | }
25 |
26 | function validateDescriptor($descriptor)
27 | {
28 | $xml = new SimpleXMLElement($xmldata);
29 | if ( ! $xml ) {
30 | echo("Error parsing Descriptor XML\n");
31 | return;
32 | }
33 | $launch_url = $xml->secure_launch_url[0];
34 | if ( ! $launch_url ) $launch_url = $xml->launch_url[0];
35 | if ( $launch_url ) $launch_url = (string) $launch_url;
36 | return $launch_url;
37 | }
38 |
39 | // Parse a descriptor
40 | function launchInfo($xmldata) {
41 | $xml = new SimpleXMLElement($xmldata);
42 | if ( ! $xml ) {
43 | echo("Error parsing Descriptor XML\n");
44 | return;
45 | }
46 | $launch_url = $xml->secure_launch_url[0];
47 | if ( ! $launch_url ) $launch_url = $xml->launch_url[0];
48 | if ( $launch_url ) $launch_url = (string) $launch_url;
49 | $custom = array();
50 | if ( $xml->custom[0]->parameter )
51 | foreach ( $xml->custom[0]->parameter as $resource) {
52 | $key = (string) $resource['key'];
53 | $key = strtolower($key);
54 | $nk = "";
55 | for($i=0; $i < strlen($key); $i++) {
56 | $ch = substr($key,$i,1);
57 | if ( $ch >= "a" && $ch <= "z" ) $nk .= $ch;
58 | else if ( $ch >= "0" && $ch <= "9" ) $nk .= $ch;
59 | else $nk .= "_";
60 | }
61 | $value = (string) $resource;
62 | $custom["custom_".$nk] = $value;
63 | }
64 | return array("launch_url" => $launch_url, "custom" => $custom ) ;
65 | }
66 |
67 | function local_ltiprovider_split_custom_parameters($customstr) {
68 | $lines = preg_split("/[\n;]/",$customstr);
69 | $retval = array();
70 | foreach ($lines as $line){
71 | $pos = strpos($line,"=");
72 | if ( $pos === false || $pos < 1 ) continue;
73 | $key = trim(substr($line, 0, $pos));
74 | $val = trim(substr($line, $pos+1));
75 | $key = local_ltiprovider_map_keyname($key);
76 | $retval['custom_'.$key] = $val;
77 | }
78 | return $retval;
79 | }
80 |
81 | function local_ltiprovider_map_keyname($key) {
82 | $newkey = "";
83 | $key = strtolower(trim($key));
84 | foreach (str_split($key) as $ch) {
85 | if ( ($ch >= 'a' && $ch <= 'z') || ($ch >= '0' && $ch <= '9') ) {
86 | $newkey .= $ch;
87 | } else {
88 | $newkey .= '_';
89 | }
90 | }
91 | return $newkey;
92 | }
93 |
94 | function signParameters($oldparms, $endpoint, $method, $oauth_consumer_key, $oauth_consumer_secret,
95 | $submit_text = false, $org_id = false, $org_desc = false)
96 | {
97 | global $last_base_string;
98 | $parms = $oldparms;
99 | if ( ! isset($parms["lti_version"]) ) $parms["lti_version"] = "LTI-1p0";
100 | if ( ! isset($parms["lti_message_type"]) ) $parms["lti_message_type"] = "basic-lti-launch-request";
101 | if ( ! isset($parms["oauth_callback"]) ) $parms["oauth_callback"] = "about:blank";
102 | if ( $org_id ) $parms["tool_consumer_instance_guid"] = $org_id;
103 | if ( $org_desc ) $parms["tool_consumer_instance_description"] = $org_desc;
104 | if ( $submit_text ) $parms["ext_submit"] = $submit_text;
105 |
106 | $test_token = '';
107 |
108 | $hmac_method = new ltiprovider\OAuthSignatureMethod_HMAC_SHA1();
109 | $test_consumer = new ltiprovider\OAuthConsumer($oauth_consumer_key, $oauth_consumer_secret, NULL);
110 |
111 | $acc_req = ltiprovider\OAuthRequest::from_consumer_and_token($test_consumer, $test_token, $method, $endpoint, $parms);
112 |
113 | $acc_req->sign_request($hmac_method, $test_consumer, $test_token);
114 |
115 | // Pass this back up "out of band" for debugging
116 | $last_base_string = $acc_req->get_signature_base_string();
117 |
118 | $newparms = $acc_req->get_parameters();
119 |
120 | return $newparms;
121 | }
122 |
123 | function signOnly($oldparms, $endpoint, $method, $oauth_consumer_key, $oauth_consumer_secret)
124 | {
125 | global $last_base_string;
126 | $parms = $oldparms;
127 |
128 | $test_token = '';
129 |
130 | $hmac_method = new ltiprovider\OAuthSignatureMethod_HMAC_SHA1();
131 | $test_consumer = new ltiprovider\OAuthConsumer($oauth_consumer_key, $oauth_consumer_secret, NULL);
132 |
133 | $acc_req = ltiprovider\OAuthRequest::from_consumer_and_token($test_consumer, $test_token, $method, $endpoint, $parms);
134 | $acc_req->sign_request($hmac_method, $test_consumer, $test_token);
135 |
136 | // Pass this back up "out of band" for debugging
137 | $last_base_string = $acc_req->get_signature_base_string();
138 |
139 | $newparms = $acc_req->get_parameters();
140 |
141 | return $newparms;
142 | }
143 |
144 | function postLaunchHTML($newparms, $endpoint, $debug=false, $iframeattr=false) {
145 | global $last_base_string;
146 | $r = "\n";
216 | return $r;
217 | }
218 |
219 | /* This is a bit of homage to Moodle's pattern of internationalisation */
220 | function get_stringIMS($key,$bundle) {
221 | return $key;
222 | }
223 |
224 | function do_post_request($url, $data, $optional_headers = null)
225 | {
226 | $params = array('http' => array(
227 | 'method' => 'POST',
228 | 'content' => $data
229 | ));
230 |
231 | if ($optional_headers !== null) {
232 | $header = $optional_headers . "\r\n";
233 | }
234 | // $header = $header . "Content-type: application/x-www-form-urlencoded\r\n";
235 | $params['http']['header'] = $header;
236 | $ctx = stream_context_create($params);
237 | $fp = @fopen($url, 'rb', false, $ctx);
238 | if (!$fp) {
239 | echo @stream_get_contents($fp);
240 | throw new Exception("Problem with $url, $php_errormsg");
241 | }
242 | $response = @stream_get_contents($fp);
243 | if ($response === false) {
244 | throw new Exception("Problem reading data from $url, $php_errormsg");
245 | }
246 | return $response;
247 | }
248 |
249 |
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * List the tool provided in a course
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | require_once(dirname(__FILE__) . '/../../config.php');
27 | require_once($CFG->dirroot.'/local/ltiprovider/lib.php');
28 |
29 | $courseid = required_param('courseid', PARAM_INT);
30 |
31 | if (! ($course = $DB->get_record('course', array('id'=>$courseid)))) {
32 | print_error('invalidcourseid', 'error');
33 | }
34 |
35 | $PAGE->set_url('/local/ltiprovider/index.php', array('courseid' => $courseid));
36 |
37 | context_helper::preload_course($course->id);
38 | if (!$context = context_course::instance($course->id)) {
39 | print_error('nocontext');
40 | }
41 |
42 | require_login($course);
43 | require_capability('local/ltiprovider:view', $context);
44 |
45 | // $PAGE->navbar->add(get_string('toolsprovided', 'local_ltiprovider'));
46 | echo $OUTPUT->header();
47 |
48 | echo $OUTPUT->heading(get_string('toolsprovided', 'local_ltiprovider'));
49 |
50 | $tools = $DB->get_records('local_ltiprovider', array('courseid' => $course->id));
51 |
52 | $data = array();
53 | foreach ($tools as $tool) {
54 | if (!$toolcontext = context::instance_by_id($tool->contextid, IGNORE_MISSING)) {
55 | local_ltiprovider_delete_tool($tool);
56 | continue;
57 | }
58 | $line = array();
59 | $line[] = $toolcontext->get_context_name();
60 | $line[] = $tool->secret;
61 | $line[] = new moodle_url('/local/ltiprovider/tool.php', array('id' => $tool->id));
62 |
63 | if (has_capability('local/ltiprovider:manage', $context)) {
64 | $buttons = array();
65 |
66 | $buttons[] = html_writer::link(new moodle_url('/local/ltiprovider/edit.php', array('id'=>$tool->id, 'delete'=>1, 'sesskey'=>sesskey())), $OUTPUT->pix_icon('t/delete', get_string('delete')));
67 | $buttons[] = html_writer::link(new moodle_url('/local/ltiprovider/edit.php', array('id'=>$tool->id, 'sesskey'=>sesskey())), $OUTPUT->pix_icon('t/edit', get_string('edit')));
68 |
69 | if ($tool->disabled) {
70 | $buttons[] = html_writer::link(new moodle_url('/local/ltiprovider/edit.php', array('id'=>$tool->id, 'show'=>1, 'sesskey'=>sesskey())), $OUTPUT->pix_icon('t/show', get_string('show')));
71 | } else {
72 | $buttons[] = html_writer::link(new moodle_url('/local/ltiprovider/edit.php', array('id'=>$tool->id, 'hide'=>1, 'sesskey'=>sesskey())), $OUTPUT->pix_icon('t/hide', get_string('hide')));
73 | }
74 |
75 | if ($tool->sendgrades) {
76 | $buttons[] = html_writer::link(new moodle_url('/local/ltiprovider/syncreport.php', array('id'=>$tool->id)), $OUTPUT->pix_icon('i/grades', get_string('gradessyncreport', 'local_ltiprovider')));
77 | }
78 |
79 | if ($tool->syncmembers) {
80 | $buttons[] = html_writer::link(new moodle_url('/local/ltiprovider/syncmembers.php', array('id'=>$tool->id)), $OUTPUT->pix_icon('i/users', get_string('forcesyncmembers', 'local_ltiprovider')));
81 | }
82 |
83 | $line[] = implode(' ', $buttons);
84 | } else {
85 | $line[] = '';
86 | }
87 | $data[] = $line;
88 | }
89 |
90 | // Moodle 2.2 and onwards
91 | if (isset($CFG->allowframembedding) and !$CFG->allowframembedding) {
92 | echo $OUTPUT->box(get_string('allowframembedding', 'local_ltiprovider'));
93 | }
94 |
95 | $table = new html_table();
96 | $table->head = array(
97 | get_string('name', 'local_ltiprovider'),
98 | get_string('secret', 'local_ltiprovider'),
99 | get_string('url', 'local_ltiprovider'),
100 | get_string('edit'));
101 | $table->size = array('20%', '20%', '50%', '10%');
102 | $table->align = array('left', 'left', 'left', 'center');
103 | $table->width = '99%';
104 | $table->data = $data;
105 | echo html_writer::table($table);
106 |
107 | echo $OUTPUT->single_button(new moodle_url('/local/ltiprovider/edit.php', array('id' => -1, 'courseid' => $course->id)), get_string('add'));
108 |
109 | echo $OUTPUT->footer();
110 |
--------------------------------------------------------------------------------
/js/syncreport.js:
--------------------------------------------------------------------------------
1 | // This file is part of Moodle - http://moodle.org/
2 | //
3 | // Moodle is free software: you can redistribute it and/or modify
4 | // it under the terms of the GNU General Public License as published by
5 | // the Free Software Foundation, either version 3 of the License, or
6 | // (at your option) any later version.
7 | //
8 | // Moodle is distributed in the hope that it will be useful,
9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | // GNU General Public License for more details.
12 | //
13 | // You should have received a copy of the GNU General Public License
14 | // along with Moodle. If not, see .
15 |
16 | /**
17 | * Javascript helper function for Sync Report plugin
18 | *
19 | * @package local-ltiprovider
20 | * @author Antoni Bertran antoni@tresipunt.com
21 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22 | */
23 |
24 | require(['jquery'], function($) {
25 | var lastChecked = null;
26 |
27 | $(document).ready(function() {
28 | var $chkboxes = $('.usercheckbox');
29 | $chkboxes.click(function(e) {
30 | if(!lastChecked) {
31 | lastChecked = this;
32 | return;
33 | }
34 |
35 | if(e.shiftKey) {
36 | var start = $chkboxes.index(this);
37 | var end = $chkboxes.index(lastChecked);
38 |
39 | $chkboxes.slice(Math.min(start,end), Math.max(start,end)+ 1).prop('checked', lastChecked.checked);
40 |
41 | }
42 |
43 | lastChecked = this;
44 | });
45 | });
46 | });
47 |
48 | function ltiprovider_send_syncreport() {
49 | event.preventDefault();
50 | var checkboxes = document.querySelectorAll('input[name="user_force_grade_checkbox"]:checked'), checks = '';
51 | Array.prototype.forEach.call(checkboxes, function(el) {
52 | checks += (checks.length>0?',':'') + el.value;
53 | });
54 | if (checks.length>0) {
55 | var x = document.getElementsByName("user_force_grade");
56 | var i;
57 | //should be only one
58 | for (i = 0; i < x.length; i++) {
59 | x[i].value = checks;
60 | }
61 | document.getElementById('ltiprovider_send_syncreport_form').submit();
62 | } else {
63 | alert(M.util.get_string('youhavetoselectauser', 'local_ltiprovider'));
64 | }
65 | return false;
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/lang/en/local_ltiprovider.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Language strings
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | $string['pluginname'] = 'LTI Provider';
27 | $string['providetool'] = 'Provide a tool for an external system';
28 |
29 | $string['remotesystem'] = 'Remote system';
30 | $string['userdefaultvalues'] = 'User default values';
31 | $string['remoteencoding'] = 'Remote system encoding';
32 | $string['secret'] = 'Shared secret';
33 | $string['toolsettings'] = 'Tool settings';
34 |
35 | $string['enrolperiod'] = 'Enrolment duration';
36 | $string['enrolperiod_desc'] = 'Default length of time that the enrolment is valid (in seconds). If set to zero, the enrolment duration will be unlimited by default.';
37 | $string['enrolperiod_help'] = 'Length of time that the enrolment is valid, starting with the moment the user enrols themselves from the remote system. If disabled, the enrolment duration will be unlimited.';
38 | $string['enrolstartdate'] = 'Start date';
39 | $string['enrolstartdate_help'] = 'If enabled, users can access from this date onward only.';
40 | $string['enrolenddate'] = 'End date';
41 | $string['enrolenddate_help'] = 'If enabled, users can access until this date only.';
42 | $string['enrolenddaterror'] = 'Enrolment end date cannot be earlier than start date';
43 |
44 | $string['maxenrolled'] = 'Max enrolled users';
45 | $string['maxenrolled_help'] = 'Specifies the maximum number of users that can access from the remote system. 0 means no limit.';
46 | $string['maxenrolledreached'] = 'Maximum number of users allowed to access was already reached.';
47 |
48 | $string['courseroleinstructor'] = 'Course role for Instructor';
49 | $string['courserolelearner'] = 'Course role for Learner';
50 | $string['activityroleinstructor'] = 'Activity role for Instructor';
51 | $string['activityrolelearner'] = 'Activity role for Learner';
52 |
53 | $string['tooldisabled'] = 'Access to the tool is disabled';
54 | $string['tooltobeprovide'] = 'Tool to be provided';
55 | $string['delconfirm'] = 'Are you sure you want to delete this tool?';
56 | $string['deletetool'] = 'Delete a tool';
57 | $string['toolsprovided'] = 'List of tools provided';
58 | $string['name'] = 'Tool name';
59 | $string['url'] = 'Launch URL';
60 | $string['layoutandcss'] = 'Layout and CSS';
61 | $string['hidepageheader'] = 'Hide page header';
62 | $string['hidepagefooter'] = 'Hide page footer';
63 | $string['hideleftblocks'] = 'Hide left blocks';
64 | $string['hiderightblocks'] = 'Hide right blocks';
65 | $string['customcss'] = 'Custom CSS';
66 | $string['sendgrades'] = 'Send grades back';
67 | $string['forcenavigation'] = 'Force course or activity navigation';
68 |
69 | $string['invalidcredentials'] = 'Invalid credentials';
70 | $string['allowframembedding'] = 'In order to avoid problems embedding this site, please enable the allowframembedding setting in Admin -> Security -> HTTP security';
71 | $string['newpopupnotice'] = 'The tool will be opened in a new Window. Please, check that popups for this site are enabled in your browser. You can use the link displayed bellow for opening the tool.';
72 | $string['opentool'] = 'Open tool in a new window';
73 |
74 | $string['enrolmentnotstarted'] = 'The enrolment period has not started';
75 | $string['enrolmentfinished'] = 'The enrolment period has finished';
76 | $string['ltiprovider:manage'] = 'Manage tools (provide)';
77 | $string['ltiprovider:view'] = 'View tools provided';
78 |
79 | $string['globalsharedsecret'] = 'Global Shared Secret';
80 | $string['defaultauthmethod'] = 'Default auth method';
81 | $string['defaultauthmethodhelp'] = 'This is the auth method assigned a new users created by the plugin';
82 | $string['delegate'] = 'Delegate';
83 | $string['userprofileupdate'] = 'User profile update';
84 | $string['userprofileupdatehelp'] = 'Never for not update the user profile on every remote access, Delegate to be configured at tool level';
85 | $string['rolesallowedcreateresources'] = 'Roles allowed to create resources (from the remote site)';
86 | $string['rolesallowedcreatecontexts'] = 'Roles allowed to create contexts (from the remote site)';
87 | $string['cantdeterminecontext'] = 'Can\' determine the context, it seems that there are more than one tool provided for this context_id';
88 |
89 | $string['invalidtplcourse'] = 'Invalid course template id';
90 | $string['missingrequiredtool'] = 'For duplicating a resource, you must point the request to an existing resource type course';
91 | $string['invalidtypetool'] = 'For duplicating a resource, you must point the request to a resource type course';
92 | $string['invalidresourcecopyid'] = 'Invalid resource to be copied identifier';
93 |
94 | $string['coursebeingrestored'] = 'This course is being restored, it can take some minutes to finish';
95 |
96 | $string['membershipsettings'] = 'Memberships service settings';
97 | $string['enablememberssync'] = 'Enable members synchronization';
98 | $string['syncperiod'] = 'Synchronization period';
99 | $string['syncmode'] = 'Synchronization mode';
100 | $string['enrolandunenrol'] = 'Enrol new and unenrol missing members';
101 | $string['enrolnew'] = 'Enrol new members';
102 | $string['unenrolmissing'] = 'Unenrol missing members';
103 |
104 | $string['idnumberformat'] = 'Idnumber format for new created courses';
105 | $string['shortnameformat'] = 'Shortname format for new created courses';
106 | $string['fullnameformat'] = 'Fullname format for new created courses';
107 | $string['genericformathelp'] = 'For remotely new create courses you can select the remote parameters for creating the name';
108 |
109 | $string['duplicatecourseswithoutusers'] = 'Duplicate courses without users';
110 | $string['duplicatecourseswithoutusershelp'] = 'When creating a new course, do not import the users from the template course';
111 |
112 | $string['subplugintype_ltiproviderextension'] = 'LTI extension';
113 | $string['subplugintype_ltiproviderextension_plural'] = 'LTI extensions';
114 |
115 | $string['requirecompletion'] = 'Require course or activity completed before sending the grades';
116 | $string['errorcompletionenabled'] = 'Completion should be enabled for the course or the activity';
117 |
118 | $string['enrolinst'] = 'Automatically enrol Instructors';
119 | $string['enrolinst_help'] = 'Uncheck this box to redirect instructors to the course page so they can select a self-enrolment option';
120 | $string['enrollearn'] = 'Automatically enrol Learners';
121 | $string['enrollearn_help'] = 'Uncheck this box to redirect learners to the course page so they can select a self-enrolment option';
122 |
123 | $string['forcesendgrades'] = 'Force send grades';
124 | $string['forcesendgradesallusers'] = 'Force send grades for all users';
125 | $string['forcesendgradesallusersomittingcompletion'] = 'Force send grades for all users omitting completion status';
126 | $string['gradessent'] = 'Grades sent';
127 | $string['gradesserviceurl'] = 'Grades service URL';
128 | $string['gradessourceid'] = 'Grades source Id';
129 | $string['gradessyncreport'] = 'Grades synchronization report';
130 | $string['notifycompletion'] = 'This tool is configured to require completion, if the user has not completed the course or activity he won\'t have grade.';
131 |
132 | $string['addtogroup'] = 'Add users to group (idnumber or request based)';
133 | $string['addtogroup_help'] = 'Add new users to the indicated group (use the group idnumber). If you want to use or create groups based on a parameter from the request, you must add "request:" before parameter name in the setting field, for example, request:myrememotegroup';
134 | $string['forcesendgradesselectedusers'] = 'Force send grades for selected users';
135 | $string['youhavetoselectauser'] = 'Must select at least one user';
136 |
137 | $string['outcomessettings'] = 'Outcomes service settings';
138 | $string['sendcompletion'] = 'Send completion status instead grades';
139 | $string['sendcompletion_help'] = 'If this setting is checked, instead the course grade the completion status will be returned:
140 | 1: When the course or activity is completed
141 | 0: When the course or activity is not completed yet';
142 |
143 | $string['forcesyncmembers'] = 'Force sync members';
144 |
145 | $string['nomanualenrol'] = 'Manual enrol method is not enabled';
146 |
147 |
--------------------------------------------------------------------------------
/lib.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * General plugin functions.
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | defined('MOODLE_INTERNAL') or die;
27 | require_once($CFG->dirroot.'/local/ltiprovider/ims-blti/blti_util.php');
28 | require_once($CFG->dirroot.'/local/ltiprovider/locallib.php');
29 |
30 | use moodle\local\ltiprovider as ltiprovider;
31 |
32 | /**
33 | * Display the LTI settings in the course settings block
34 | * For 2.3 and onwards
35 | *
36 | * @param settings_navigation $nav The settings navigatin object
37 | * @param stdclass $context Course context
38 | */
39 | function local_ltiprovider_extend_settings_navigation(settings_navigation $nav, $context) {
40 | if ($context->contextlevel >= CONTEXT_COURSE and ($branch = $nav->get('courseadmin'))
41 | and has_capability('local/ltiprovider:view', $context)) {
42 | $ltiurl = new moodle_url('/local/ltiprovider/index.php', array('courseid' => $context->instanceid));
43 | $branch->add(get_string('pluginname', 'local_ltiprovider'), $ltiurl, $nav::TYPE_CONTAINER, null, 'ltiprovider'.$context->instanceid);
44 | }
45 | }
46 |
47 | /**
48 | * Change the navigation block and bar only for external users
49 | * Force course or activity navigation and modify CSS also
50 | * Please note that this function is only called in pages where the navigation block is present
51 | *
52 | * @global moodle_user $USER
53 | * @global moodle_database $DB
54 | * @param navigation_node $nav Current navigation object
55 | */
56 | function local_ltiprovider_extend_navigation ($nav) {
57 | global $CFG, $USER, $PAGE, $SESSION, $ME;
58 |
59 | if (isset($USER) and isset($USER->auth) and strpos($USER->username, 'ltiprovider') === 0) {
60 | // Force course or activity navigation.
61 | if (isset($SESSION->ltiprovider) and $SESSION->ltiprovider->forcenavigation) {
62 | $context = $SESSION->ltiprovider->context;
63 | $urltogo = '';
64 | if ($context->contextlevel == CONTEXT_COURSE and $PAGE->course->id != $SESSION->ltiprovider->courseid) {
65 | $urltogo = new moodle_url('/course/view.php', array('id' => $SESSION->ltiprovider->courseid));
66 | } else if ($context->contextlevel == CONTEXT_MODULE and $PAGE->context->id != $context->id) {
67 | $cm = get_coursemodule_from_id(false, $context->instanceid, 0, false, MUST_EXIST);
68 | $urltogo = new moodle_url('/mod/'.$cm->modname.'/view.php', array('id' => $cm->id));
69 | }
70 |
71 | // Special case, user policy, we don't have to do nothing to avoid infinites loops.
72 | if (strpos($ME, 'user/policy.php')) {
73 | return;
74 | }
75 |
76 | if ($urltogo) {
77 | local_ltiprovider_call_hook("navigation", $nav);
78 | if (!$PAGE->requires->is_head_done()) {
79 | $PAGE->set_state($PAGE::STATE_IN_BODY);
80 | }
81 | redirect($urltogo);
82 | }
83 | }
84 |
85 | // Delete all the navigation nodes except the course one.
86 | if ($coursenode = $nav->find($PAGE->course->id, $nav::TYPE_COURSE)) {
87 | foreach (array('myprofile', 'users', 'site', 'home', 'myhome', 'mycourses', 'courses', '1') as $nodekey) {
88 | if ($node = $nav->get($nodekey)) {
89 | $node->remove();
90 | }
91 | }
92 | $nav->children->add($coursenode);
93 | }
94 |
95 | // Custom CSS.
96 | if (isset($SESSION->ltiprovider) and !$PAGE->requires->is_head_done()) {
97 | $PAGE->requires->css(new moodle_url('/local/ltiprovider/styles.php', array('id' => $SESSION->ltiprovider->id)));
98 | } elseif (isset($SESSION->ltiprovider) && isset($SESSION->ltiprovider->id)) {
99 | $url = new moodle_url('/local/ltiprovider/styles.js.php',
100 | array('id' => $SESSION->ltiprovider->id, 'rand' => rand(0, 1000)));
101 | $PAGE->requires->js($url);
102 | }
103 |
104 | local_ltiprovider_call_hook("navigation", $nav);
105 | }
106 | }
107 |
108 | /**
109 | * Add new tool.
110 | *
111 | * @param object $tool
112 | * @return int
113 | */
114 | function local_ltiprovider_add_tool($tool) {
115 | global $DB;
116 |
117 | if (!isset($tool->disabled)) {
118 | $tool->disabled = 0;
119 | }
120 | if (!isset($tool->timecreated)) {
121 | $tool->timecreated = time();
122 | }
123 | if (!isset($tool->timemodified)) {
124 | $tool->timemodified = $tool->timecreated;
125 | }
126 |
127 | if (!isset($tool->sendgrades)) {
128 | $tool->sendgrades = 0;
129 | }
130 | if (!isset($tool->forcenavigation)) {
131 | $tool->forcenavigation = 0;
132 | }
133 | if (!isset($tool->enrolinst)) {
134 | $tool->enrolinst = 0;
135 | }
136 | if (!isset($tool->enrollearn)) {
137 | $tool->enrollearn = 0;
138 | }
139 | if (!isset($tool->hidepageheader)) {
140 | $tool->hidepageheader = 0;
141 | }
142 | if (!isset($tool->hidepagefooter)) {
143 | $tool->hidepagefooter = 0;
144 | }
145 | if (!isset($tool->hideleftblocks)) {
146 | $tool->hideleftblocks = 0;
147 | }
148 | if (!isset($tool->hiderightblocks)) {
149 | $tool->hiderightblocks = 0;
150 | }
151 | if (!isset($tool->syncmembers)) {
152 | $tool->syncmembers = 0;
153 | }
154 |
155 | $tool->id = $DB->insert_record('local_ltiprovider', $tool);
156 | local_ltiprovider_call_hook('save_settings', $tool);
157 |
158 | return $tool->id;
159 | }
160 |
161 | /**
162 | * Update existing tool.
163 | * @param object $tool
164 | * @return void
165 | */
166 | function local_ltiprovider_update_tool($tool) {
167 | global $DB;
168 |
169 | $tool->timemodified = time();
170 |
171 | if (!isset($tool->sendgrades)) {
172 | $tool->sendgrades = 0;
173 | }
174 | if (!isset($tool->forcenavigation)) {
175 | $tool->forcenavigation = 0;
176 | }
177 | if (!isset($tool->enrolinst)) {
178 | $tool->enrolinst = 0;
179 | }
180 | if (!isset($tool->enrollearn)) {
181 | $tool->enrollearn = 0;
182 | }
183 | if (!isset($tool->hidepageheader)) {
184 | $tool->hidepageheader = 0;
185 | }
186 | if (!isset($tool->hidepagefooter)) {
187 | $tool->hidepagefooter = 0;
188 | }
189 | if (!isset($tool->hideleftblocks)) {
190 | $tool->hideleftblocks = 0;
191 | }
192 | if (!isset($tool->hiderightblocks)) {
193 | $tool->hiderightblocks = 0;
194 | }
195 | if (!isset($tool->syncmembers)) {
196 | $tool->syncmembers = 0;
197 | }
198 |
199 | local_ltiprovider_call_hook('save_settings', $tool);
200 | $DB->update_record('local_ltiprovider', $tool);
201 | }
202 |
203 | /**
204 | * Delete tool.
205 | * @param object $tool
206 | * @return void
207 | */
208 | function local_ltiprovider_delete_tool($tool) {
209 | global $DB;
210 | $DB->delete_records('local_ltiprovider_user', array('toolid' => $tool->id));
211 | $DB->delete_records('local_ltiprovider', array('id' => $tool->id));
212 | }
213 |
214 | /**
215 | * Checks if a course linked to a tool is missing, is so, delete the lti entries
216 | * @param stdclass $tool Tool record
217 | * @return bool True if the course was missing
218 | */
219 | function local_ltiprovider_check_missing_course($tool) {
220 | global $DB;
221 |
222 | if (! $course = $DB->get_record('course', array('id' => $tool->courseid))) {
223 | $DB->delete_records('local_ltiprovider', array('courseid' => $tool->courseid));
224 | $DB->delete_records('local_ltiprovider_user', array('toolid' => $tool->id));
225 | mtrace("Tool: $tool->id deleted (courseid: $tool->courseid missing)");
226 | return true;
227 | }
228 | return false;
229 | }
230 |
231 | /**
232 | * Cron function for sync grades
233 | * @return void
234 | */
235 | function local_ltiprovider_cron() {
236 | global $DB, $CFG;
237 | require_once($CFG->dirroot."/local/ltiprovider/locallib.php");
238 | require_once($CFG->dirroot."/local/ltiprovider/ims-blti/OAuth.php");
239 | require_once($CFG->dirroot."/local/ltiprovider/ims-blti/OAuthBody.php");
240 | require_once($CFG->libdir.'/gradelib.php');
241 | require_once($CFG->dirroot.'/grade/querylib.php');
242 |
243 | // TODO - Add a global setting for this
244 | $synctime = 60*60; // Every 1 hour grades are sync
245 | $timenow = time();
246 |
247 | mtrace('Running cron for ltiprovider');
248 |
249 | mtrace('Deleting LTI tools assigned to deleted courses');
250 | if ($tools = $DB->get_records('local_ltiprovider')) {
251 | foreach ($tools as $tool) {
252 | local_ltiprovider_check_missing_course($tool);
253 | }
254 | }
255 |
256 | // Grades service.
257 | if ($tools = $DB->get_records_select('local_ltiprovider', 'disabled = ? AND sendgrades = ?', array(0, 1))) {
258 | foreach ($tools as $tool) {
259 | if ($tool->lastsync + $synctime < $timenow) {
260 | mtrace(" Starting sync tool for grades id $tool->id course id $tool->courseid");
261 | if ($tool->requirecompletion) {
262 | mtrace(" Grades require activity or course completion");
263 | }
264 | $user_count = 0;
265 | $send_count = 0;
266 | $error_count = 0;
267 |
268 | $completion = new completion_info(get_course($tool->courseid));
269 |
270 | if ($users = $DB->get_records('local_ltiprovider_user', array('toolid' => $tool->id))) {
271 | foreach ($users as $user) {
272 |
273 | $data = array(
274 | 'tool' => $tool,
275 | 'user' => $user,
276 | );
277 | local_ltiprovider_call_hook('grades', (object) $data);
278 |
279 | $user_count = $user_count + 1;
280 | // This can happen is the sync process has an unexpected error
281 | if ( strlen($user->serviceurl) < 1 ) {
282 | mtrace(" Empty serviceurl");
283 | continue;
284 | }
285 | if ( strlen($user->sourceid) < 1 ) {
286 | mtrace(" Empty sourceid");
287 | continue;
288 | }
289 |
290 | if ($user->lastsync > $tool->lastsync) {
291 | mtrace(" Skipping user {$user->id} due to recent sync");
292 | continue;
293 | }
294 |
295 | $grade = false;
296 | if ($context = $DB->get_record('context', array('id' => $tool->contextid))) {
297 | if ($context->contextlevel == CONTEXT_COURSE) {
298 |
299 | if ($tool->requirecompletion and !$completion->is_course_complete($user->userid)) {
300 | mtrace(" Skipping user $user->userid since he didn't complete the course");
301 | continue;
302 | }
303 |
304 | if ($tool->sendcompletion) {
305 | $grade = $completion->is_course_complete($user->userid) ? 1 : 0;
306 | $grademax = 1;
307 | } else if ($grade = grade_get_course_grade($user->userid, $tool->courseid)) {
308 | $grademax = floatval($grade->item->grademax);
309 | $grade = $grade->grade;
310 | }
311 | } else if ($context->contextlevel == CONTEXT_MODULE) {
312 | $cm = get_coursemodule_from_id(false, $context->instanceid, 0, false, MUST_EXIST);
313 |
314 | if ($tool->requirecompletion) {
315 | $data = $completion->get_data($cm, false, $user->userid);
316 | if ($data->completionstate != COMPLETION_COMPLETE_PASS and $data->completionstate != COMPLETION_COMPLETE) {
317 | mtrace(" Skipping user $user->userid since he didn't complete the activity");
318 | continue;
319 | }
320 | }
321 |
322 | if ($tool->sendcompletion) {
323 | $data = $completion->get_data($cm, false, $user->userid);
324 | if ($data->completionstate == COMPLETION_COMPLETE_PASS ||
325 | $data->completionstate == COMPLETION_COMPLETE ||
326 | $data->completionstate == COMPLETION_COMPLETE_FAIL) {
327 | $grade = 1;
328 | } else {
329 | $grade = 0;
330 | }
331 | $grademax = 1;
332 | } else {
333 | $grades = grade_get_grades($cm->course, 'mod', $cm->modname, $cm->instance, $user->userid);
334 | if (empty($grades->items[0]->grades)) {
335 | $grade = false;
336 | } else {
337 | $grade = reset($grades->items[0]->grades);
338 | if (!empty($grade->item)) {
339 | $grademax = floatval($grade->item->grademax);
340 | } else {
341 | $grademax = floatval($grades->items[0]->grademax);
342 | }
343 | $grade = $grade->grade;
344 | }
345 | }
346 | }
347 |
348 | if ( $grade === false || $grade === NULL || strlen($grade) < 1) {
349 | mtrace(" Invalid grade $grade");
350 | continue;
351 | }
352 |
353 | // No need to be dividing by zero
354 | if ( $grademax == 0.0 ) $grademax = 100.0;
355 |
356 | // TODO: Make lastgrade should be float or string - but it is integer so we truncate
357 | // TODO: Then remove those intval() calls
358 |
359 | // Don't double send
360 | if ( intval($grade) == $user->lastgrade ) {
361 | mtrace(" Skipping, last grade send is equal to current grade");
362 | continue;
363 | }
364 |
365 | // We sync with the external system only when the new grade differs with the previous one
366 | // TODO - Global setting for check this
367 | if ($grade >= 0 and $grade <= $grademax) {
368 | $float_grade = $grade / $grademax;
369 | $body = local_ltiprovider_create_service_body($user->sourceid, $float_grade);
370 |
371 | try {
372 | $response = ltiprovider\sendOAuthBodyPOST('POST', $user->serviceurl, $user->consumerkey, $user->consumersecret, 'application/xml', $body);
373 | } catch (Exception $e) {
374 | mtrace(" ".$e->getMessage());
375 | $error_count = $error_count + 1;
376 | continue;
377 | }
378 |
379 | // TODO - Check for errors in $retval in a correct way (parsing xml)
380 | if (strpos(strtolower($response), 'success') !== false) {
381 |
382 | $DB->set_field('local_ltiprovider_user', 'lastsync', $timenow, array('id' => $user->id));
383 | $DB->set_field('local_ltiprovider_user', 'lastgrade', intval($grade), array('id' => $user->id));
384 | mtrace(" User grade sent to remote system. userid: $user->userid grade: $float_grade");
385 | $send_count = $send_count + 1;
386 | } else {
387 | mtrace(" User grade send failed. userid: $user->userid grade: $float_grade: " . $response);
388 | $error_count = $error_count + 1;
389 | }
390 | } else {
391 | mtrace(" User grade for user $user->userid out of range: grade = ".$grade);
392 | $error_count = $error_count + 1;
393 | }
394 | } else {
395 | mtrace(" Invalid context: contextid = ".$tool->contextid);
396 | }
397 | }
398 | }
399 | mtrace(" Completed sync tool id $tool->id course id $tool->courseid users=$user_count sent=$send_count errors=$error_count");
400 | $DB->set_field('local_ltiprovider', 'lastsync', $timenow, array('id' => $tool->id));
401 | }
402 | }
403 | }
404 |
405 | $timenow = time();
406 | // Automatic course restaurations.
407 | if ($croncourses = get_config('local_ltiprovider', 'croncourses')) {
408 | $croncourses = unserialize($croncourses);
409 | if (is_array($croncourses)) {
410 | mtrace('Starting restauration of pending courses');
411 |
412 | foreach ($croncourses as $key => $course) {
413 | mtrace('Starting restoration of ' . $key);
414 |
415 | // We limit the backups to 1 hour, then retry.
416 | if ($course->restorestart and ($timenow < $course->restorestart + 3600)) {
417 | mtrace('Skipping restoration in process for: ' . $key);
418 | continue;
419 | }
420 |
421 | $course->restorestart = time();
422 | $croncourses[$key] = $course;
423 | $croncoursessafe = serialize($croncourses);
424 | set_config('croncourses', $croncoursessafe, 'local_ltiprovider');
425 |
426 | if ($destinationcourse = $DB->get_record('course', array('id' => $course->destinationid))) {
427 | // Duplicate course + users.
428 | local_ltiprovider_duplicate_course($course->id, $destinationcourse, 1,
429 | $options = array(array('name' => 'users',
430 | 'value' => 1)),
431 | $course->userrestoringid, $course->context);
432 | mtrace('Restoration for ' .$key. ' finished');
433 | } else {
434 | mtrace('Restoration for ' .$key. ' finished (destination course not exists)');
435 | }
436 |
437 | unset($croncourses[$key]);
438 | $croncoursessafe = serialize($croncourses);
439 | set_config('croncourses', $croncoursessafe, 'local_ltiprovider');
440 | }
441 | }
442 | }
443 |
444 | // Membership service.
445 | $timenow = time();
446 | $userphotos = array();
447 |
448 | if ($tools = $DB->get_records('local_ltiprovider', array('disabled' => 0, 'syncmembers' => 1))) {
449 | mtrace('Starting sync of member using the memberships service');
450 | $consumers = array();
451 |
452 | foreach ($tools as $tool) {
453 | $lastsync = get_config('local_ltiprovider', 'membershipslastsync-' . $tool->id);
454 | if (!$lastsync) {
455 | $lastsync = 0;
456 | }
457 | if ($lastsync + $tool->syncperiod < $timenow) {
458 | $ret = local_ltiprovider_membership_service($tool, $timenow, $userphotos, $consumers);
459 | $userphotos = $ret['userphotos'];
460 | $consumers = $ret['consumers'];
461 | } else {
462 | $last = format_time((time() - $lastsync));
463 | mtrace("Tool $tool->id synchronized $last ago");
464 | }
465 | mtrace('Finished sync of member using the memberships service');
466 | }
467 | }
468 |
469 | local_ltiprovider_membership_service_update_userphotos($userphotos);
470 |
471 | }
472 |
473 | /**
474 | * Call a hook present in a subplugin
475 | *
476 | * @param string $hookname The hookname (function without franken style prefix)
477 | * @param object $data Object containing data to be used by the hook function
478 | * @return bool Allways false
479 | */
480 | function local_ltiprovider_call_hook($hookname, $data) {
481 | $plugins = get_plugin_list_with_function('ltiproviderextension', $hookname);
482 | if (!empty($plugins)) {
483 | foreach ($plugins as $plugin) {
484 | call_user_func($plugin, $data);
485 | }
486 | }
487 | return false;
488 | }
489 |
--------------------------------------------------------------------------------
/modinfo/chat.php:
--------------------------------------------------------------------------------
1 | time());
5 |
6 |
--------------------------------------------------------------------------------
/modinfo/forum.php:
--------------------------------------------------------------------------------
1 | 0,
5 | "availableuntil" => 0,
6 | "showavailability" => 0);
7 |
8 |
--------------------------------------------------------------------------------
/modinfo/quiz.php:
--------------------------------------------------------------------------------
1 | "");
5 |
6 |
--------------------------------------------------------------------------------
/services.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Main entry for extra services request.
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | require_once(dirname(__FILE__) . '/../../config.php');
27 | require_once($CFG->dirroot.'/local/ltiprovider/locallib.php');
28 | require_once($CFG->dirroot.'/local/ltiprovider/ims-blti/blti.php');
29 |
30 | $service = required_param('custom_service', PARAM_RAW_TRIMMED);
31 | $toolid = optional_param('id', 0, PARAM_INT);
32 | $lticontextid = optional_param('context_id', false, PARAM_RAW);
33 |
34 | if (isset($newinfo['custom_lti_message_encoded_base64']) and $newinfo['custom_lti_message_encoded_base64'] == 1) {
35 | $lticontextid = base64_decode($lticontextid);
36 | $service = base64_decode($service);
37 | }
38 |
39 | if (!$toolid and $lticontextid) {
40 | // Check if there is more that one course for this LTI context id.
41 | if ($DB->count_records('course', array('idnumber' => $lticontextid)) > 1) {
42 | print_error('cantdeterminecontext', 'local_ltiprovider');
43 | }
44 |
45 | if ($course = $DB->get_record('course', array('idnumber' => $lticontextid))) {
46 | // Look for a course created for this LTI context id.
47 | if ($coursecontext = context_course::instance($course->id)) {
48 | if ($DB->count_records('local_ltiprovider', array('contextid' => $coursecontext->id)) > 1) {
49 | print_error('cantdeterminecontext', 'local_ltiprovider');
50 | }
51 | $toolid = $DB->get_field('local_ltiprovider', 'id', array('contextid' => $coursecontext->id));
52 | }
53 | }
54 | }
55 |
56 | $secret = '';
57 | // If we dont receive a request for a specific tool, we use the global shared secret.
58 | if ($tool = $DB->get_record('local_ltiprovider', array('id' => $toolid))) {
59 | if ($tool->disabled) {
60 | print_error('tooldisabled', 'local_ltiprovider');
61 | }
62 | $secret = $tool->secret;
63 | } else {
64 | $secret = get_config('local_ltiprovider', 'globalsharedsecret');
65 | }
66 |
67 | if (!$secret) {
68 | print_error('invalidtoolid', 'local_ltiprovider');
69 | }
70 |
71 | // Do not set session, do not redirect.
72 | $context = new BLTI($secret, false, false);
73 |
74 | // Correct launch request.
75 | if ($context->valid) {
76 |
77 | set_time_limit(0);
78 | raise_memory_limit(MEMORY_EXTRA);
79 |
80 | // Are we creating a new context (that means a new course tool)?
81 | if ($service == 'create_context') {
82 | $custom_context_template = $context->info['custom_context_template'];
83 |
84 | if (!$tplcourse = $DB->get_record('course', array('idnumber' => $custom_context_template), '*', IGNORE_MULTIPLE)) {
85 | print_error('invalidtplcourse', 'local_ltiprovider');
86 | }
87 |
88 | require_once("$CFG->dirroot/course/lib.php");
89 | $newcourse = new stdClass();
90 | $newcourse->fullname = local_ltiprovider_get_new_course_info('fullname', $context);
91 | $newcourse->shortname = local_ltiprovider_get_new_course_info('shortname', $context);
92 | $newcourse->idnumber = local_ltiprovider_get_new_course_info('idnumber', $context);
93 |
94 | $categories = $DB->get_records('course_categories', null, '', 'id', 0, 1);
95 | $category = array_shift($categories);
96 | $newcourse->category = $category->id;
97 |
98 | $course = create_course($newcourse);
99 |
100 | $coursecontext = context_course::instance($course->id);
101 |
102 | // Create the tool that provide the full course.
103 | $tool = local_ltiprovider_create_tool($course->id, $coursecontext->id, $context);
104 |
105 | $username = local_ltiprovider_create_username($context->info['oauth_consumer_key'], $context->info['user_id']);
106 | $userrestoringid = $DB->get_field('user', 'id', array('username' => $username));
107 |
108 | // Duplicate course + users.
109 | $course = local_ltiprovider_duplicate_course($tplcourse->id, $course, 1,
110 | $options = array(array('name' => 'users',
111 | 'value' => 1)), $userrestoringid, $context);
112 | echo json_encode($course);
113 |
114 | } else if ($service == 'duplicate_resource') {
115 | $idnumber = $context->info['custom_resource_link_copy_id'];
116 | $resource_link_id = $context->info['resource_link_id'];
117 |
118 | if (!$tool) {
119 | print_error('missingrequiredtool', 'local_ltiprovider');
120 | }
121 |
122 | if (! $context = $DB->get_record('context', array('id' => $tool->contextid))) {
123 | print_error("invalidcontext");
124 | }
125 |
126 | if ($context->contextlevel != CONTEXT_COURSE) {
127 | print_error('invalidtypetool', 'local_ltiprovider');
128 | }
129 |
130 | if (!$cm = $DB->get_record('course_modules', array('idnumber' => $idnumber), '*', IGNORE_MULTIPLE)) {
131 | print_error('invalidresourcecopyid', 'local_ltiprovider');
132 | }
133 |
134 | $courseid = $context->instanceid;
135 |
136 | $cmid = local_ltiprovider_duplicate_module($cm->id, $courseid, $resource_link_id, $context);
137 | if ($cm = get_coursemodule_from_id(false, $cmid)) {
138 | echo json_encode($cm);
139 | }
140 | } else if ($service == 'force_logout') {
141 | // Force logout.
142 | $authsequence = get_enabled_auth_plugins();
143 | foreach ($authsequence as $authname) {
144 | $authplugin = get_auth_plugin($authname);
145 | $authplugin->logoutpage_hook();
146 | }
147 |
148 | require_logout();
149 | }
150 |
151 | } else {
152 | echo $context->message;
153 | }
154 |
--------------------------------------------------------------------------------
/settings.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * General plugin functions.
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | defined('MOODLE_INTERNAL') || die;
27 |
28 | if ($hassiteconfig) { // needs this condition or there is error on login page
29 |
30 | $ADMIN->add('root', new admin_category('ltiprovider', get_string('pluginname', 'local_ltiprovider')));
31 | $ADMIN->add('ltiprovider', new admin_externalpage('ltiprovidersettings', get_string('settings'),
32 | $CFG->wwwroot.'/admin/settings.php?section=local_ltiprovider', 'local/ltiprovider:manage'));
33 |
34 | $settings = new admin_settingpage('local_ltiprovider', 'LTI Provider');
35 | $ADMIN->add('localplugins', $settings);
36 |
37 | $settings->add(new admin_setting_configtext('local_ltiprovider/globalsharedsecret',
38 | get_string('globalsharedsecret', 'local_ltiprovider'), '', '', PARAM_RAW_TRIMMED));
39 |
40 | $options = array(-1 => get_string('delegate', 'local_ltiprovider'), 0 => get_string('never'), 1 => get_string('always'));
41 | $settings->add(new admin_setting_configselect('local_ltiprovider/userprofileupdate', get_string('userprofileupdate',
42 | 'local_ltiprovider'), get_string('userprofileupdatehelp', 'local_ltiprovider'), 1, $options));
43 |
44 | $auths = get_plugin_list('auth');
45 | $authmethods = array();
46 | foreach ($auths as $auth => $unused) {
47 | if (is_enabled_auth($auth)) {
48 | $authmethods[$auth] = get_string('pluginname', "auth_{$auth}");
49 | }
50 | }
51 | $settings->add(new admin_setting_configselect('local_ltiprovider/defaultauthmethod', get_string('defaultauthmethod',
52 | 'local_ltiprovider'), get_string('defaultauthmethodhelp', 'local_ltiprovider'), 'manual', $authmethods));
53 |
54 | $options = array('context_id', 'context_title' , 'context_label', 'consumer_key : context_id', 'consumer_key : context_title' , 'consumer_key : context_label');
55 |
56 | $settings->add(new admin_setting_configselect('local_ltiprovider/fullnameformat', get_string('fullnameformat',
57 | 'local_ltiprovider'), get_string('genericformathelp', 'local_ltiprovider'), 1, $options));
58 |
59 | $settings->add(new admin_setting_configselect('local_ltiprovider/shortnameformat', get_string('shortnameformat',
60 | 'local_ltiprovider'), get_string('genericformathelp', 'local_ltiprovider'), 2, $options));
61 |
62 | $settings->add(new admin_setting_configselect('local_ltiprovider/idnumberformat', get_string('idnumberformat',
63 | 'local_ltiprovider'), get_string('genericformathelp', 'local_ltiprovider'), 0, $options));
64 |
65 | $settings->add(new admin_setting_configcheckbox('local_ltiprovider/duplicatecourseswithoutusers', get_string('duplicatecourseswithoutusers', 'local_ltiprovider'),
66 | get_string('duplicatecourseswithoutusershelp', 'local_ltiprovider'), 0));
67 |
68 | $settings->add(new admin_setting_configmultiselect('local_ltiprovider/rolesallowedcreatecontexts', get_string('rolesallowedcreatecontexts', 'local_ltiprovider'),
69 | '', array('Administrator'),
70 | array(
71 | 'Student' => 'Student',
72 | 'Faculty' => 'Faculty',
73 | 'Member' => 'Member',
74 | 'Learner' => 'Learner',
75 | 'Instructor' => 'Instructor',
76 | 'Mentor' => 'Mentor',
77 | 'Staff' => 'Staff',
78 | 'Alumni' => 'Alumni',
79 | 'ProspectiveStudent' => 'ProspectiveStudent',
80 | 'Guest' => 'Guest',
81 | 'Other' => 'Other',
82 | 'Administrator' => 'Administrator',
83 | 'Observer' => 'Observer',
84 | 'None' => 'None'
85 | )));
86 |
87 | $settings->add(new admin_setting_configmultiselect('local_ltiprovider/rolesallowedcreateresources', get_string('rolesallowedcreateresources', 'local_ltiprovider'),
88 | '', array('Administrator'),
89 | array(
90 | 'Student' => 'Student',
91 | 'Faculty' => 'Faculty',
92 | 'Member' => 'Member',
93 | 'Learner' => 'Learner',
94 | 'Instructor' => 'Instructor',
95 | 'Mentor' => 'Mentor',
96 | 'Staff' => 'Staff',
97 | 'Alumni' => 'Alumni',
98 | 'ProspectiveStudent' => 'ProspectiveStudent',
99 | 'Guest' => 'Guest',
100 | 'Other' => 'Other',
101 | 'Administrator' => 'Administrator',
102 | 'Observer' => 'Observer',
103 | 'None' => 'None'
104 | )));
105 | }
--------------------------------------------------------------------------------
/styles.js.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Custom styles for a tool.
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | require_once(dirname(__FILE__) . '/../../config.php');
27 |
28 | $toolid = required_param('id', PARAM_INT);
29 |
30 | $url = $CFG->wwwroot . "/local/ltiprovider/styles.php?id=$toolid";
31 | ?>
32 |
33 | function local_ltiprovider_loadjscssfile(){
34 | var url = "";
35 | var fileref=document.createElement("link");
36 | fileref.setAttribute("rel", "stylesheet");
37 | fileref.setAttribute("type", "text/css");
38 | fileref.setAttribute("href", url);
39 | var head = document.getElementsByTagName("head");
40 | if (head) {
41 | head[0].appendChild(fileref)
42 | }
43 | }
44 |
45 | // Waiting DOM ready (hide effect).
46 | YUI().use('node', function(Y) {
47 | Y.on("domready", function(){
48 | local_ltiprovider_loadjscssfile();
49 | });
50 | });
51 |
52 | // Without waiting.
53 | local_ltiprovider_loadjscssfile();
--------------------------------------------------------------------------------
/styles.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Custom styles for a tool.
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | require_once(dirname(__FILE__) . '/../../config.php');
27 |
28 | $toolid = required_param('id', PARAM_INT);
29 |
30 | if (! ($tool = $DB->get_record('local_ltiprovider', array('id'=>$toolid)))) {
31 | print_error('invalidtoolid', 'local_ltiprovider');
32 | }
33 |
34 | if ($tool->disabled) {
35 | print_error('tooldisabled', 'local_ltiprovider');
36 | }
37 |
38 | $lifetime = 60*60*24*3;
39 |
40 | $css = '';
41 |
42 | if ($tool->hidepageheader or $SESSION->ltiprovider->hidepageheader) {
43 | $css .= '
44 | #page-header{
45 | display: none;
46 | }
47 |
48 | header.navbar {
49 | display: none;
50 | }
51 | ';
52 | }
53 | if ($tool->hidepagefooter or $SESSION->ltiprovider->hidepagefooter) {
54 | $css .= '
55 | #page-footer{
56 | display: none;
57 | }
58 | ';
59 | }
60 | if ($tool->hideleftblocks or $SESSION->ltiprovider->hideleftblocks) {
61 | $css .= '
62 | #region-pre .block, #block-region-side-pre .block{
63 | display: none;
64 | }
65 | #mod_quiz_navblock {
66 | display: block !important;
67 | }
68 | .empty-region-side-post.used-region-side-pre #region-main.span8 {
69 | width: inherit;
70 | }
71 | ';
72 | }
73 | if ($tool->hiderightblocks or $SESSION->ltiprovider->hiderightblocks) {
74 | $css .= '
75 | #region-post, #block-region-side-post {
76 | display: none;
77 | }
78 | ';
79 | }
80 |
81 | if ($tool->customcss or $SESSION->ltiprovider->customcss) {
82 |
83 | $css .= $tool->customcss;
84 |
85 | if ($SESSION->ltiprovider->customcss and $SESSION->ltiprovider->customcss != $tool->customcss) {
86 | $css .= $SESSION->ltiprovider->customcss;
87 | }
88 | }
89 |
90 | header('Content-Disposition: inline; filename="styles.php"');
91 | header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
92 | header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
93 | header('Pragma: ');
94 | header('Accept-Ranges: none');
95 | header('Content-Type: text/css; charset=utf-8');
96 | header('Content-Length: '.strlen($css));
97 |
98 | echo $css;
99 | die;
100 |
--------------------------------------------------------------------------------
/syncmembers.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Force to sync members of current tool
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2017 Antoni Bertran Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | require_once(dirname(__FILE__) . '/../../config.php');
27 | require_once(dirname(__FILE__) . '/lib.php');
28 |
29 | $id = required_param('id', PARAM_INT);
30 |
31 | $tool = $DB->get_record('local_ltiprovider', array('id' => $id), '*', MUST_EXIST);
32 | $courseid = $tool->courseid;
33 | $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
34 |
35 | $params_url = array('id' => $id);
36 |
37 | $PAGE->set_url('/local/ltiprovider/syncmembers.php', $params_url );
38 |
39 | $context = context_course::instance($course->id);
40 |
41 | require_login($course);
42 | require_capability('local/ltiprovider:manage', $context);
43 |
44 | $returnurl = new moodle_url('/local/ltiprovider/index.php', array('courseid' => $courseid));
45 |
46 | $strheading = get_string('forcesyncmembers', 'local_ltiprovider');
47 | $PAGE->set_context($context);
48 | $PAGE->navbar->add(get_string('pluginname', 'local_ltiprovider'), new moodle_url('/local/ltiprovider/index.php', array('courseid'=>$course->id)));
49 | $PAGE->navbar->add($strheading);
50 | $PAGE->set_title($strheading);
51 | $PAGE->set_heading($course->fullname . ': '.$strheading);
52 |
53 | echo $OUTPUT->header();
54 |
55 | $userphotos = array();
56 | $consumers = array();
57 | $timenow = time();
58 | $ret = local_ltiprovider_membership_service($tool, $timenow, $userphotos, $consumers);
59 | echo 'Response =>
'.htmlentities($ret['response']).'
';
60 |
61 | $userphotos = $ret['userphotos'];
62 | local_ltiprovider_membership_service_update_userphotos($userphotos);
63 |
64 | echo $OUTPUT->footer();
65 |
--------------------------------------------------------------------------------
/syncreport.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Edit a tool provided in a course
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2016 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | require_once(dirname(__FILE__) . '/../../config.php');
27 | require_once($CFG->dirroot.'/local/ltiprovider/classes/table_syncreport.php');
28 |
29 | $id = required_param('id', PARAM_INT);
30 | $search_firstname = optional_param('search_firstname', '', PARAM_TEXT);
31 | $search_lastname = optional_param('search_lastname', '', PARAM_TEXT);
32 | $sifirst = optional_param('sifirst', null, PARAM_NOTAGS);
33 | $silast = optional_param('silast', null, PARAM_NOTAGS);
34 |
35 |
36 | $tool = $DB->get_record('local_ltiprovider', array('id' => $id), '*', MUST_EXIST);
37 | $courseid = $tool->courseid;
38 | $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
39 |
40 | $params_url = array('id' => $id);
41 | if (!empty($search_firstname)) {
42 | $params_url['search_firstname'] = $search_firstname;
43 | }
44 | if (!empty($search_surname)) {
45 | $params_url['search_surname'] = $search_surname;
46 | }
47 | if (!empty($sifirst)) {
48 | $params_url['sifirst'] = $sifirst;
49 | }
50 | if (!empty($silast)) {
51 | $params_url['silast'] = $silast;
52 | }
53 |
54 | $PAGE->set_url('/local/ltiprovider/syncreport.php', $params_url );
55 |
56 | $context = context_course::instance($course->id);
57 |
58 | require_login($course);
59 | require_capability('local/ltiprovider:manage', $context);
60 |
61 | $returnurl = new moodle_url('/local/ltiprovider/index.php', array('courseid' => $courseid));
62 |
63 | $strheading = get_string('gradessyncreport', 'local_ltiprovider');
64 | $PAGE->set_context($context);
65 | $PAGE->navbar->add(get_string('pluginname', 'local_ltiprovider'), new moodle_url('/local/ltiprovider/index.php', array('courseid'=>$course->id)));
66 | $PAGE->navbar->add($strheading);
67 | $PAGE->set_title($strheading);
68 | $PAGE->set_heading($course->fullname . ': '.$strheading);
69 | $PAGE->requires->jquery();
70 | $PAGE->requires->js('/local/ltiprovider/js/syncreport.js');
71 | $PAGE->requires->strings_for_js(array('youhavetoselectauser'), 'local_ltiprovider');
72 |
73 | echo $OUTPUT->header();
74 | echo $OUTPUT->heading(get_string('gradessent', 'local_ltiprovider'));
75 |
76 | if ($tool->requirecompletion) {
77 | echo $OUTPUT->notification(get_string('notifycompletion', 'local_ltiprovider'), 'notifymessage');
78 | }
79 |
80 | $filterparams = new stdClass();
81 | $filterparams->firstname = $search_firstname;
82 | $filterparams->sifirst = $sifirst;
83 | $filterparams->lastname = $search_lastname;
84 | $filterparams->silast = $silast;
85 | $table = new local_ltiprovider_table_syncreport('local_ltiprovider_syncreport', $tool, $filterparams);
86 | echo $table->syncreport_search_form($id, $search_firstname, $search_lastname, $sifirst, $silast);
87 |
88 | $table->out(50, true);
89 |
90 | //Button to send grade to selected users
91 | $forcesendurl_participant = new \moodle_url('test/forcesendgrades.php', array('toolid' => $tool->id,
92 | 'printresponse' => 1, 'selected_users' => 1, 'user_force_grade' => ''));
93 | $action = new component_action('click', 'ltiprovider_send_syncreport');
94 | echo $OUTPUT->single_button($forcesendurl_participant, get_string('forcesendgradesselectedusers', 'local_ltiprovider'),
95 | 'post', array('actions' => array($action), 'formid' => 'ltiprovider_send_syncreport_form'));
96 |
97 | //Button to send grade to all users
98 | $forcesendurl = new \moodle_url('test/forcesendgrades.php', array('toolid' => $tool->id, 'printresponse' => 1));
99 | echo $OUTPUT->single_button($forcesendurl, get_string('forcesendgradesallusers', 'local_ltiprovider'));
100 |
101 | if ($tool->requirecompletion) {
102 | $forcesendurl = new \moodle_url('test/forcesendgrades.php', array('toolid' => $tool->id, 'printresponse' => 1, 'omitcompletion' => 1));
103 | echo $OUTPUT->single_button($forcesendurl, get_string('forcesendgradesallusersomittingcompletion', 'local_ltiprovider'));
104 | }
105 |
106 | if (!$table->is_downloading()) {
107 | echo $OUTPUT->footer();
108 | }
109 |
--------------------------------------------------------------------------------
/test/cron.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Launch destination url. Main entry point for the external system.
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | require_once(dirname(__FILE__) . '/../../../config.php');
27 |
28 | require_login();
29 | require_capability('moodle/site:config', context_system::instance());
30 |
31 | require_once('../lib.php');
32 |
33 | if ($tools = $DB->get_records('local_ltiprovider', array('disabled' => 0))) {
34 | foreach ($tools as $tool) {
35 | set_config('membershipslastsync-' . $tool->id, 0, 'local_ltiprovider');
36 | $tool->lastsync = 0;
37 | $DB->update_record('local_ltiprovider', $tool);
38 | }
39 | }
40 |
41 | if ($users = $DB->get_records('local_ltiprovider_user')) {
42 | foreach ($users as $user) {
43 | $user->lastsync = 0;
44 | $DB->update_record('local_ltiprovider_user', $user);
45 | }
46 | }
47 |
48 | @header('Content-Type: text/plain; charset=utf-8');
49 |
50 | $start = time();
51 | echo "Starting LTI provider cron";
52 | local_ltiprovider_cron();
53 | $end = time();
54 | echo "LTI provider cron finished, duration: " . ($end - $start) . " secs";
55 |
--------------------------------------------------------------------------------
/test/forcesendgrades.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Force send grades back (overwritten). You can force by course, tool and userid (paremeters $courseid, $toolid, $userid)
19 | * Completion check can be omitted too ($omitcompletion)
20 | *
21 | * @package local
22 | * @subpackage ltiprovider
23 | * @copyright 2011 Juan Leyva
24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 | */
26 |
27 | require_once(dirname(__FILE__) . '/../../../config.php');
28 |
29 | require_once($CFG->dirroot . "/local/ltiprovider/lib.php");
30 | require_once($CFG->dirroot . "/local/ltiprovider/locallib.php");
31 | require_once($CFG->dirroot . "/local/ltiprovider/ims-blti/OAuth.php");
32 | require_once($CFG->dirroot . "/local/ltiprovider/ims-blti/OAuthBody.php");
33 | require_once($CFG->libdir . "/gradelib.php");
34 | require_once($CFG->dirroot . "/grade/querylib.php");
35 |
36 | use moodle\local\ltiprovider as ltiprovider;
37 |
38 | ini_set("display_erros", 1);
39 | error_reporting(E_ALL);
40 |
41 | $courseid = optional_param('courseid', 0, PARAM_INT);
42 | $toolid = optional_param('toolid', 0, PARAM_INT);
43 | $userid = optional_param('userid', 0, PARAM_INT);
44 | $omitcompletion = optional_param('omitcompletion', 0, PARAM_BOOL);
45 | $printresponse = optional_param('printresponse', 0, PARAM_BOOL);
46 | $sesskey = optional_param('sesskey', null, PARAM_RAW);
47 | $user_force_grade = optional_param('user_force_grade', null, PARAM_RAW);
48 | $selected_users = optional_param('selected_users', 0, PARAM_BOOL);
49 |
50 | if (!empty($sesskey)) {
51 | require_sesskey();
52 | }
53 |
54 | if (empty($toolid)) {
55 | require_login();
56 | require_capability('moodle/site:config', context_system::instance());
57 | } else {
58 | $tool = $DB->get_record('local_ltiprovider', array('id' => $toolid), '*', MUST_EXIST);
59 | $course = $DB->get_record('course', array('id' => $tool->courseid), '*', MUST_EXIST);
60 | $coursecontext = context_course::instance($course->id);
61 | require_login($course);
62 | require_capability('local/ltiprovider:manage', $coursecontext);
63 | }
64 |
65 | $log = array();
66 |
67 | if ($selected_users && strlen($user_force_grade)==0) {
68 | $log[] = s(" Error, there are not selected users");
69 | } else {
70 |
71 | $select = 'disabled = ? AND sendgrades = ?';
72 | $params_select = array(0, 1);
73 | if (!empty($courseid)) {
74 | $select .= ' AND courseid=?';
75 | array_push($params_select, $courseid);
76 | }
77 | if (!empty($toolid)) {
78 | $select .= ' AND id=?';
79 | array_push($params_select, $toolid);
80 | }
81 | if ($tools = $DB->get_records_select('local_ltiprovider', $select, $params_select)) {
82 | foreach ($tools as $tool) {
83 |
84 | $log[] = s(" Starting sync tool for grades id $tool->id course id $tool->courseid");
85 |
86 | if ($omitcompletion) {
87 | $tool->requirecompletion = 0;
88 | }
89 |
90 | if ($tool->requirecompletion) {
91 | $log[] = s(" Grades require activity or course completion");
92 | }
93 | $user_count = 0;
94 | $send_count = 0;
95 | $error_count = 0;
96 |
97 | $completion = new completion_info(get_course($tool->courseid));
98 |
99 | $select_user = 'toolid = :toolid';
100 | $params_user = array('toolid'=>$tool->id);
101 | if (!empty($userid)) {
102 | $select_user .= ' AND userid = :userid';
103 | $params_user += array('userid' => $userid);
104 | } elseif ($selected_users) {
105 | list($sql_in, $params_in) = $DB->get_in_or_equal(explode(",", $user_force_grade), SQL_PARAMS_NAMED);
106 | $select_user .= ' AND userid '.$sql_in;
107 | $params_user += $params_in;
108 | }
109 |
110 | if ($users = $DB->get_records_select('local_ltiprovider_user', $select_user, $params_user)) {
111 | foreach ($users as $user) {
112 |
113 | $data = array(
114 | 'tool' => $tool,
115 | 'user' => $user,
116 | );
117 |
118 | local_ltiprovider_call_hook('grades', (object)$data);
119 |
120 | $log[] = s(" Sending grades for user $user->userid");
121 | $user_count = $user_count + 1;
122 | // This can happen is the sync process has an unexpected error
123 | if (strlen($user->serviceurl) < 1) {
124 | $log[] = s(" Empty serviceurl");
125 | continue;
126 | }
127 | if (strlen($user->sourceid) < 1) {
128 | $log[] = s(" Empty sourceid");
129 | continue;
130 | }
131 |
132 | $grade = false;
133 | if ($context = $DB->get_record('context', array('id' => $tool->contextid))) {
134 | if ($context->contextlevel == CONTEXT_COURSE) {
135 |
136 | if ($tool->requirecompletion and !$completion->is_course_complete($user->userid)) {
137 | $log[] = s(" Skipping user $user->userid since he didn't complete the course");
138 | continue;
139 | }
140 |
141 | if ($grade = grade_get_course_grade($user->userid, $tool->courseid)) {
142 | $grademax = floatval($grade->item->grademax);
143 | $grade = $grade->grade;
144 | }
145 | } else {
146 | if ($context->contextlevel == CONTEXT_MODULE) {
147 | $cm = get_coursemodule_from_id(false, $context->instanceid, 0, false, MUST_EXIST);
148 |
149 | if ($tool->requirecompletion) {
150 | $data = $completion->get_data($cm, false, $user->userid);
151 | if ($data->completionstate != COMPLETION_COMPLETE_PASS and $data->completionstate != COMPLETION_COMPLETE) {
152 | $log[] = s(" Skipping user $user->userid since he didn't complete the activity");
153 | continue;
154 | }
155 | }
156 |
157 | $grades = grade_get_grades($cm->course, 'mod', $cm->modname, $cm->instance,
158 | $user->userid);
159 | if (empty($grades->items[0]->grades)) {
160 | $grade = false;
161 | } else {
162 | $grade = reset($grades->items[0]->grades);
163 | if (!empty($grade->item)) {
164 | $grademax = floatval($grade->item->grademax);
165 | } else {
166 | $grademax = floatval($grades->items[0]->grademax);
167 | }
168 | $grade = $grade->grade;
169 | }
170 | }
171 | }
172 |
173 | if ($grade === false || $grade === null || strlen($grade) < 1) {
174 | $log[] = s(" Invalid grade $grade");
175 | continue;
176 | }
177 |
178 | // No need to be dividing by zero
179 | if ($grademax == 0.0) {
180 | $grademax = 100.0;
181 | }
182 |
183 | // TODO: Make lastgrade should be float or string - but it is integer so we truncate
184 | // TODO: Then remove those intval() calls
185 |
186 | // Don't double send
187 | if (intval($grade) == $user->lastgrade) {
188 | $log[] = s(" Last grade send is equal to current grade");
189 | }
190 |
191 | // We sync with the external system only when the new grade differs with the previous one
192 | // TODO - Global setting for check this
193 | if ($grade >= 0 and $grade <= $grademax) {
194 | $float_grade = $grade / $grademax;
195 | $body = local_ltiprovider_create_service_body($user->sourceid, $float_grade);
196 |
197 | try {
198 | $response = ltiprovider\sendOAuthBodyPOST('POST', $user->serviceurl, $user->consumerkey,
199 | $user->consumersecret, 'application/xml', $body);
200 | } catch (Exception $e) {
201 | $log[] = s(" Exception" . $e->getMessage());
202 | $error_count = $error_count + 1;
203 | $log[] = s("Invalid $response " . $response);
204 | continue;
205 | }
206 |
207 | if ($printresponse) {
208 | $log[] = s(" Remote system response: \n $response\n");
209 | }
210 |
211 | // TODO - Check for errors in $retval in a correct way (parsing xml)
212 | if (strpos(strtolower($response), 'success') !== false) {
213 | $DB->set_field('local_ltiprovider_user', 'lastsync', time(), array('id' => $user->id));
214 | $DB->set_field('local_ltiprovider_user', 'lastgrade', intval($grade),
215 | array('id' => $user->id));
216 | $log[] = s(" User grade sent to remote system. userid: $user->userid grade: $float_grade");
217 | $send_count = $send_count + 1;
218 | } else {
219 | $log[] = s(" User grade send failed. userid: $user->userid grade: $float_grade: " . $response);
220 | $error_count = $error_count + 1;
221 | }
222 | } else {
223 | $log[] = s(" User grade for user $user->userid out of range: grade = " . $grade);
224 | $error_count = $error_count + 1;
225 | }
226 | } else {
227 | $log[] = s(" Invalid context: contextid = " . $tool->contextid);
228 | }
229 | }
230 | }
231 | $log[] = s(" Completed sync tool id $tool->id course id $tool->courseid users=$user_count sent=$send_count errors=$error_count");
232 | }
233 | }
234 |
235 | }
236 | // Check if we requested this by URL or via the sync grades report.
237 | if ($toolid & !empty($sesskey)) {
238 | $link = new moodle_url('/local/ltiprovider/syncreport.php', array('id' => $toolid));
239 |
240 | $PAGE->set_context($coursecontext);
241 | $PAGE->set_url($link);
242 | notice(implode(" ", $log), $link);
243 | } else {
244 | @header('Content-Type: text/plain; charset=utf-8');
245 | echo implode("\n", $log);
246 | }
247 |
248 |
--------------------------------------------------------------------------------
/test/lms.php:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | IMS Basic Learning Tools Interoperability
9 |
10 |
22 |
23 |
24 |
25 | IMS BasicLTI PHP Consumer
26 | This is a very simple reference implementaton of the LMS side (i.e. consumer) for IMS BasicLTI.
27 | "120988f929-274612",
33 | "resource_link_title" => "Weekly Blog",
34 | "resource_link_description" => "A weekly blog.",
35 | "user_id" => "292832126",
36 | "roles" => "urn:lti:role:ims/lis/Instructor", // or urn:lti:instrole:ims/lis/Learner
37 | "lis_person_name_full" => 'Jane Q. Public',
38 | "lis_person_name_family" => 'Public',
39 | "lis_person_name_given" => 'Given',
40 | "lis_person_contact_email_primary" => "user@school.edu",
41 | "lis_person_sourcedid" => "school.edu:user",
42 | "context_id" => "456434513",
43 | "context_title" => "Design of Personal Environments",
44 | "context_label" => "SI182",
45 | "tool_consumer_instance_guid" => "lmsng.school.edu",
46 | "tool_consumer_instance_description" => "University of School (LMSng)",
47 | "custom_create_context" => "0",
48 | "custom_context_template" => "",
49 | "custom_resource_link_type" => "",
50 | "custom_resource_link_copy_id" => "",
51 | "custom_service" => "",
52 | "custom_force_navigation" => "",
53 | "custom_hide_left_blocks" => "",
54 | "custom_hide_right_blocks" => "",
55 | "custom_hide_page_header" => "",
56 | "custom_hide_page_footer" => "",
57 | "custom_custom_css" => "",
58 | "custom_show_blocks" => "",
59 | "ext_ims_lis_memberships_id" => "",
60 | "ext_ims_lis_memberships_url" => "",
61 | "user_image" => ""
62 | );
63 |
64 | foreach ($lmsdata as $k => $val ) {
65 | if ( $_POST[$k] && strlen($_POST[$k]) > 0 ) {
66 | $lmsdata[$k] = $_POST[$k];
67 | }
68 | }
69 |
70 | $cur_url = curPageURL();
71 | $key = $_REQUEST["key"];
72 | if ( ! $key ) $key = "12345";
73 | $secret = $_REQUEST["secret"];
74 | if ( ! $secret ) $secret = "secret";
75 | $endpoint = $_REQUEST["endpoint"];
76 |
77 | if ( ! $endpoint ) $endpoint = str_replace("test/lms.php","tool.php",$cur_url);
78 |
79 | $urlformat = $_REQUEST["format"];
80 | $urlformat = ( $urlformat != 'XML' );
81 | $tool_consumer_instance_guid = $lmsdata['tool_consumer_instance_guid'];
82 | $tool_consumer_instance_description = $lmsdata['tool_consumer_instance_description'];
83 |
84 | $xmldesc = str_replace("\\\"","\"",$_REQUEST["xmldesc"]);
85 | if ( ! $xmldesc ) $xmldesc = $default_desc;
86 | ?>
87 |
100 | Toggle Resource and Launch Data
101 | \n");
103 | echo(" \n");
104 | echo("\n");
105 | echo("URL plus Secret \n");
106 | if ( $urlformat ) {
107 | echo("XML Descriptor \n");
108 | } else {
109 | echo("XML Descriptor \n");
110 | }
111 | echo(" ");
112 | echo("(To set a value to 'empty' - set it to a blank)");
113 | echo("BasicLTI Resource \n");
114 | if ( $urlformat ) {
115 | echo("Launch URL: \n");
116 | } else {
117 | echo("XML BasicLTI Resource Descriptor: \n");
118 | }
119 | echo(" Key: \n");
120 | echo(" Secret: \n");
121 | echo("");
122 | echo("
Launch Data \n");
123 | foreach ($lmsdata as $k => $val ) {
124 | echo($k.": \n");
127 | }
128 | echo("");
129 | echo("");
130 | echo("
");
131 |
132 | if ( $urlformat ) {
133 | $parms = $lmsdata;
134 | } else {
135 | $cx = launchInfo($xmldesc);
136 | $endpoint = $cx["launch_url"];
137 | if ( ! $endpoint ) {
138 | echo("Error, did not find a launch_url or secure_launch_url in the XML descriptor
\n");
139 | exit();
140 | }
141 | $custom = $cx["custom"];
142 | $parms = array_merge($custom, $lmsdata);
143 | }
144 |
145 | // Cleanup parms before we sign
146 | foreach( $parms as $k => $val ) {
147 | if (strlen(trim($parms[$k]) ) < 1 ) {
148 | unset($parms[$k]);
149 | }
150 | }
151 |
152 | // Add oauth_callback to be compliant with the 1.0A spec
153 | $parms["oauth_callback"] = "about:blank";
154 |
155 | $parms = signParameters($parms, $endpoint, "POST", $key, $secret, "Press to Launch", $tool_consumer_instance_guid, $tool_consumer_instance_description);
156 |
157 | $content = postLaunchHTML($parms, $endpoint, true,
158 | "width=\"100%\" height=\"900\" scrolling=\"auto\" frameborder=\"1\" transparency");
159 | print($content);
160 |
161 | ?>
162 |
163 |
164 | Note: Unpublished drafts of IMS Specifications are only available to
165 | IMS members and any software based on an unpublished draft is subject to change.
166 | Sample code is provided to help developers understand the specification more quickly.
167 | Simply interoperating with this sample implementation code does not
168 | allow one to claim compliance with a specification.
169 |
170 | IMS Learning Tools Interoperability Working Group
171 | IMS Compliance Detail
172 | IMS Developer Community
173 | under the Apache 2 License.
174 |
--------------------------------------------------------------------------------
/test/misc.php:
--------------------------------------------------------------------------------
1 | \n");
16 | return;
17 | }
18 |
19 | $zip = zip_open($file_name);
20 | if (! is_resource($zip)) return;
21 |
22 | while ($zip_entry = zip_read($zip)) {
23 | if ( zip_entry_name($zip_entry) != $zip_file ) continue;
24 | if (zip_entry_open($zip, $zip_entry, "r")) {
25 | $buf = zip_entry_read($zip_entry, zip_entry_filesize($zip_entry));
26 | zip_entry_close($zip_entry);
27 | zip_close($zip);
28 | return $buf;
29 | }
30 | }
31 | zip_close($zip);
32 | }
33 |
34 | $default_desc = str_replace("CUR_URL", str_replace("lms.php", "tool.php", curPageURL()),
35 | '
36 |
38 | A Simple Descriptor
39 |
40 | 120
41 |
42 | CUR_URL
43 | ');
44 |
45 | ?>
46 |
47 |
--------------------------------------------------------------------------------
/tool.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Launch destination url. Main entry point for the external system.
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | require_once(dirname(__FILE__) . '/../../config.php');
27 | require_once($CFG->dirroot.'/local/ltiprovider/lib.php');
28 | require_once($CFG->dirroot.'/local/ltiprovider/locallib.php');
29 | require_once($CFG->dirroot.'/local/ltiprovider/ims-blti/blti.php');
30 |
31 | $toolid = optional_param('id', 0, PARAM_INT);
32 | $lticontextid = optional_param('context_id', false, PARAM_RAW);
33 | $custom_create_context = optional_param('custom_create_context', false, PARAM_RAW);
34 |
35 | // Temporary context.
36 | $mycontext = new stdClass();
37 | $mycontext->info = array();
38 | $mycontext->info['context_id'] = optional_param('context_id', false, PARAM_RAW);
39 | $mycontext->info['context_title'] = optional_param('context_title', false, PARAM_RAW);
40 | $mycontext->info['context_label'] = optional_param('context_label', false, PARAM_RAW);
41 | $mycontext->info['oauth_consumer_key'] = optional_param('oauth_consumer_key', false, PARAM_RAW);
42 | $mycontext->info['resource_link_id'] = optional_param('resource_link_id', false, PARAM_RAW);
43 |
44 | if (optional_param('custom_lti_message_encoded_base64', 0, PARAM_INT) == 1) {
45 | $lticontextid = base64_decode($lticontextid);
46 | $custom_create_context = base64_decode($custom_create_context);
47 | $blti = new BLTI(false, false, false);
48 | $mycontext->info = $blti->decodeBase64($mycontext->info);
49 | }
50 |
51 | if (!$toolid and !$lticontextid) {
52 | print_error('invalidtoolid', 'local_ltiprovider');
53 | }
54 |
55 | if (!$toolid and $lticontextid) {
56 | // Check if there is more that one course for this LTI context id.
57 | $idnumber = local_ltiprovider_get_new_course_info('idnumber', $mycontext);
58 | if ($DB->count_records('course', array('idnumber' => $idnumber)) > 1) {
59 | print_error('cantdeterminecontext', 'local_ltiprovider');
60 | }
61 | if ($course = $DB->get_record('course', array('idnumber' => $idnumber))) {
62 | // Look for a course created for this LTI context id.
63 | if ($coursecontext = context_course::instance($course->id)) {
64 | if ($DB->count_records('local_ltiprovider', array('contextid' => $coursecontext->id)) > 1) {
65 | print_error('cantdeterminecontext', 'local_ltiprovider');
66 | }
67 | $toolid = $DB->get_field('local_ltiprovider', 'id', array('contextid' => $coursecontext->id));
68 |
69 | // Now check if we are accessing a resource/activity instead a course.
70 | $resource_link_id = $mycontext->info['resource_link_id'];
71 | if ($resource_link_id) {
72 | if ($cm = $DB->get_record('course_modules', array('idnumber' => $resource_link_id, 'course' => $course->id), '*', IGNORE_MULTIPLE)) {
73 | $cmcontext = context_module::instance($cm->id);
74 |
75 | $toolinstances = $DB->count_records('local_ltiprovider', array('contextid' => $cmcontext->id));
76 | // More than one tool for the same resource/activity.
77 | if ($toolinstances and $toolinstances > 1) {
78 | print_error('cantdeterminecontext', 'local_ltiprovider');
79 | }
80 | if ($toolinstances) {
81 | $toolid = $DB->get_field('local_ltiprovider', 'id', array('contextid' => $cmcontext->id));
82 | }
83 | }
84 | }
85 | }
86 | }
87 | }
88 |
89 | $secret = '';
90 | // We may expect a valid tool / context id or custom parameters.
91 | if ($tool = $DB->get_record('local_ltiprovider', array('id'=>$toolid))) {
92 | if ($tool->disabled) {
93 | print_error('tooldisabled', 'local_ltiprovider');
94 | }
95 | $secret = $tool->secret;
96 | } else if ($custom_create_context) {
97 | $secret = get_config('local_ltiprovider', 'globalsharedsecret');
98 | }
99 |
100 | if (!$secret) {
101 | print_error('invalidtoolid', 'local_ltiprovider');
102 | }
103 |
104 | // Do not set session, do not redirect
105 | $context = new BLTI($secret, false, false);
106 |
107 | $data = array(
108 | 'tool' => $tool,
109 | 'context' => $context,
110 | );
111 | local_ltiprovider_call_hook('validate_request', (object) $data);
112 |
113 | // Correct launch request
114 | if ($context->valid) {
115 |
116 | // Are we creating a new context (that means a new course tool)?
117 | if ($custom_create_context) {
118 |
119 | // Check if the remote user can create contexts, checking the remote role.
120 | $cancreate = false;
121 | $rolesallowedcreatecontexts = get_config('local_ltiprovider', 'rolesallowedcreatecontexts');
122 | if ($rolesallowedcreatecontexts) {
123 | $rolesallowedcreatecontexts = explode(',', strtolower($rolesallowedcreatecontexts));
124 | $roles = explode(',', strtolower($context->info['roles']));
125 |
126 | foreach ($roles as $rol) {
127 | if (in_array($rol, $rolesallowedcreatecontexts)) {
128 | $cancreate = true;
129 | break;
130 | }
131 | }
132 |
133 | }
134 |
135 | require_once("$CFG->dirroot/course/lib.php");
136 | $newcourse = new stdClass();
137 | $newcourse->fullname = local_ltiprovider_get_new_course_info('fullname', $context);
138 | $newcourse->shortname = local_ltiprovider_get_new_course_info('shortname', $context);
139 | $newcourse->idnumber = local_ltiprovider_get_new_course_info('idnumber', $context);
140 |
141 | //Gets the first visible category by sort order
142 | $categories = $DB->get_records('course_categories', array('visible'=>1), 'sortorder', 'id', 0, 1);
143 | if (count($categories)==0){
144 | $categories = $DB->get_records('course_categories', null, '', 'id', 0, 1);
145 | }
146 | $category = array_shift($categories);
147 |
148 | $newcourse->category = $category->id;
149 |
150 | // Course exists?? First try idnumber.
151 | $course = $DB->get_record('course', array('idnumber' => $newcourse->idnumber));
152 |
153 | // Then try shortname.
154 | if (!$course) {
155 | $course = $DB->get_record('course', array('shortname' => $newcourse->shortname));
156 | }
157 |
158 | if (!$cancreate and !$course) {
159 | print_error('rolecannotcreatecontexts', 'local_ltiprovider');
160 | }
161 |
162 | if (!$course) {
163 | $course = create_course($newcourse);
164 |
165 | $coursecontext = context_course::instance($course->id);
166 |
167 | // Create the tool that provide the full course.
168 | $tool = local_ltiprovider_create_tool($course->id, $coursecontext->id, $context);
169 |
170 | // Are we using another course as template?
171 | // We have a setting for storing courses to be restored when the cron job is executed.
172 | $custom_context_template = $context->info['custom_context_template'];
173 | $tplcourse = $DB->get_record('course', array('idnumber' => $custom_context_template), '*', IGNORE_MULTIPLE);
174 |
175 | if ($custom_context_template and $tplcourse) {
176 |
177 | $username = local_ltiprovider_create_username($context->info['oauth_consumer_key'], $context->info['user_id']);
178 | $userrestoringid = $DB->get_field('user', 'id', array('username' => $username));;
179 |
180 | $newcourse = new stdClass();
181 | $newcourse->id = $tplcourse->id;
182 | $newcourse->destinationid = $course->id;
183 | $newcourse->userrestoringid = $userrestoringid;
184 | $newcourse->context = new stdClass;
185 | $newcourse->context->info['roles'] = $context->info['roles'];
186 | $newcourse->restorestart = 0;
187 | $aid = $newcourse->id . "-" . $newcourse->destinationid;
188 |
189 | if ($croncourses = get_config('local_ltiprovider', 'croncourses')) {
190 | $croncourses = unserialize($croncourses);
191 | if (is_array($croncourses)) {
192 | $croncourses[$aid] = $newcourse;
193 | } else {
194 | $croncourses = array($aid => $newcourse);
195 | }
196 | } else {
197 | $croncourses = array($aid => $newcourse);
198 | }
199 |
200 | $croncourses = serialize($croncourses);
201 | set_config('croncourses', $croncourses, 'local_ltiprovider');
202 |
203 | // Add the waiting label.
204 | $section = new stdClass();
205 | $section->course = $course->id;
206 | $section->section = 0;
207 | $section->name = "";
208 | $section->summary = get_string("coursebeingrestored", "local_ltiprovider");
209 | $section->summaryformat = 1;
210 | $section->sequence = 10;
211 | $section->visible = 1;
212 | $section->availablefrom = 0;
213 | $section->availableuntil = 0;
214 | $section->showavailability = 0;
215 | $section->groupingid = 0;
216 | if ($section = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0))) {
217 | $section->summary = get_string("coursebeingrestored", "local_ltiprovider");
218 | $DB->update_record('course_sections', $section);
219 | } else {
220 | $DB->insert_record('course_sections', $section);
221 | }
222 | rebuild_course_cache($course->id);
223 | }
224 | } else {
225 | $coursecontext = context_course::instance($course->id);
226 |
227 | if (!$tool = $DB->get_record('local_ltiprovider', array('contextid' => $coursecontext->id))) {
228 | print_error('cantdeterminecontext', 'local_ltiprovider');
229 | }
230 | }
231 | }
232 |
233 | // Check that we can perform enrolments
234 | if (enrol_is_enabled('manual')) {
235 | $manual = enrol_get_plugin('manual');
236 | } else {
237 | print_error('nomanualenrol', 'local_ltiprovider');
238 | }
239 |
240 | // Transform to utf8 all the post and get data
241 |
242 | foreach ($_POST as $key => $value) {
243 | $_POST[$key] = core_text::convert($value, $tool->encoding);
244 | }
245 | foreach ($_GET as $key => $value) {
246 | $_GET[$key] = core_text::convert($value, $tool->encoding);
247 | }
248 |
249 | // We need an username without extended chars
250 | // Later accounts add the ConsumerKey - we silently upgrade old accounts
251 | // Might want a flag for this -- Chuck
252 | $username = local_ltiprovider_create_username($context->info['oauth_consumer_key'], $context->info['user_id']);
253 | $dbuser = $DB->get_record('user', array('username' => $username));
254 | if ( ! $dbuser ) {
255 | $old_username = 'ltiprovider'.md5($context->getUserKey());
256 | $dbuser = $DB->get_record('user', array('username' => $old_username));
257 | if ( $dbuser ) {
258 | // Probably should log this
259 | $DB->set_field('user', 'username', $username, array('id' => $dbuser->id));
260 | }
261 | $dbuser = $DB->get_record('user', array('username' => $username));
262 | }
263 |
264 |
265 |
266 | // Check if the user exists
267 | $dbuser = $DB->get_record('user', array('username' => $username));
268 | if (! $dbuser ) {
269 | $user = new stdClass();
270 |
271 | // clean_param , email username text
272 | $auth = get_config('local_ltiprovider', 'defaultauthmethod');
273 | if ($auth) {
274 | $user->auth = $auth;
275 | } else {
276 | $user->auth = 'nologin';
277 | }
278 |
279 | $user->username = $username;
280 | $user->password = md5(uniqid(rand(), 1));
281 | local_ltiprovider_populate($user, $context, $tool);
282 | $user->id = $DB->insert_record('user', $user);
283 | // Reload full user
284 | $user = $DB->get_record('user', array('id' => $user->id));
285 | // Trigger event.
286 | $event = \core\event\user_created::create(
287 | array(
288 | 'objectid' => $user->id,
289 | 'relateduserid' => $user->id,
290 | 'context' => context_user::instance($user->id)
291 | )
292 | );
293 | $event->trigger();
294 | } else {
295 | $user = new stdClass();
296 | local_ltiprovider_populate($user, $context, $tool);
297 | if ( local_ltiprovider_user_match($user, $dbuser) ) {
298 | $user = $dbuser;
299 | } else {
300 | $user = $dbuser;
301 | $userprofileupdate = get_config('local_ltiprovider', 'userprofileupdate');
302 | if ($userprofileupdate == -1) {
303 | // Check the tool setting.
304 | $userprofileupdate = $tool->userprofileupdate;
305 | }
306 | if ($userprofileupdate) {
307 | local_ltiprovider_populate($user, $context, $tool);
308 | $DB->update_record('user', $user);
309 |
310 | // Trigger event.
311 | $event = \core\event\user_updated::create(
312 | array(
313 | 'objectid' => $user->id,
314 | 'relateduserid' => $user->id,
315 | 'context' => context_user::instance($user->id)
316 | )
317 | );
318 | $event->trigger();
319 | }
320 | }
321 | }
322 |
323 | // Update user image.
324 | if (!empty($context->info['user_image']) or !empty($context->info['custom_user_image'])) {
325 | $userimageurl = (!empty($context->info['user_image'])) ? $context->info['user_image'] : $context->info['custom_user_image'];
326 | local_ltiprovider_update_user_profile_image($user->id, $userimageurl);
327 | }
328 |
329 | // Enrol user in course and activity if needed
330 | if (! $moodlecontext = $DB->get_record('context', array('id' => $tool->contextid))) {
331 | print_error("invalidcontext");
332 | }
333 |
334 | if ($moodlecontext->contextlevel == CONTEXT_COURSE) {
335 | $courseid = $moodlecontext->instanceid;
336 | $urltogo = $CFG->wwwroot.'/course/view.php?id='.$courseid;
337 | // Check if we have to redirect to a specific module in the course.
338 | $resource_link_id = $context->info['resource_link_id'];
339 | if ($resource_link_id) {
340 | if ($cm = $DB->get_record('course_modules', array('idnumber' => $resource_link_id, 'course' => $courseid), '*', IGNORE_MULTIPLE)) {
341 | if ($cm = get_coursemodule_from_id(false, $cm->id, $courseid)) {
342 | $urltogo = new moodle_url('/mod/' .$cm->modname. '/view.php', array('id' => $cm->id));
343 | }
344 | }
345 | // Detect it we must create the resource.
346 | if (!$cm) {
347 | $resource_link_title = $context->info['resource_link_title'];
348 | $resource_link_description = (isset($context->info['resource_link_description'])) ? $context->info['resource_link_description'] : false;
349 | $resource_link_type = (isset($context->info['custom_resource_link_type'])) ? $context->info['custom_resource_link_type'] : false;
350 | if (!$resource_link_title) {
351 | $resource_link_title = $context->info['custom_resource_link_title'];
352 | }
353 | if (!$resource_link_description && isset($context->info['custom_resource_link_description'])) {
354 | $resource_link_description = $context->info['custom_resource_link_description'];
355 | }
356 |
357 | // Minimun for creating a module, title and type.
358 | if ($resource_link_title and $resource_link_type) {
359 |
360 | // Check if the remote user can create modules, checking the remote role.
361 | $rolesallowedcreateresources = get_config('local_ltiprovider', 'rolesallowedcreateresources');
362 | if ($rolesallowedcreateresources) {
363 | $rolesallowedcreateresources = explode(',', strtolower($rolesallowedcreateresources));
364 | $roles = explode(',', strtolower($context->info['roles']));
365 | $cancreate = false;
366 |
367 | foreach ($roles as $rol) {
368 | if (in_array($rol, $rolesallowedcreateresources)) {
369 | $cancreate = true;
370 | break;
371 | }
372 | }
373 |
374 | if ($cancreate) {
375 | require_once($CFG->dirroot . '/course/lib.php');
376 | $moduleinfo = new stdClass();
377 |
378 | // Always mandatory generic values to any module
379 | // TODO, check for valid types.
380 | $moduleinfo->modulename = $resource_link_type;
381 | $moduleinfo->section = 1;
382 | $moduleinfo->course = $courseid;
383 | $moduleinfo->visible = true;
384 | $moduleinfo->cmidnumber = $resource_link_id;
385 |
386 | // Sometimes optional generic values for some modules
387 | $moduleinfo->name= $resource_link_title;
388 |
389 | // Optional intro editor (depends of module)
390 | if ($resource_link_description) {
391 | $draftid_editor = 0;
392 | $USER = $user;
393 | file_prepare_draft_area($draftid_editor, null, null, null, null);
394 | $moduleinfo->introeditor = array('text'=> $resource_link_description, 'format'=>FORMAT_HTML, 'itemid'=>$draftid_editor);
395 | }
396 |
397 | // Add extra module info.
398 | $modinfofile = $CFG->dirroot . '/local/ltiprovider/modinfo/' . $moduleinfo->modulename . '.php';
399 | if (file_exists($modinfofile)) {
400 | require_once($modinfofile);
401 | foreach ($extramodinfo as $key => $val) {
402 | $moduleinfo->{$key} = $val;
403 | }
404 | }
405 |
406 | $modinfo = create_module($moduleinfo);
407 |
408 | if ($modinfo) {
409 | $urltogo = new moodle_url('/course/modedit.php', array('update' => $modinfo->coursemodule));
410 | }
411 | } else {
412 | print_error('rolecannotcreateresources', 'local_ltiprovider');
413 | }
414 | }
415 | }
416 | }
417 | }
418 |
419 | // Duplicate an existing resource on SSO.
420 | $custom_resource_link_copy_id = (!empty($context->info['custom_resource_link_copy_id'])) ? $context->info['custom_resource_link_copy_id'] : false;
421 | if ($custom_resource_link_copy_id) {
422 | if (!$cm = $DB->get_record('course_modules', array('idnumber' => $custom_resource_link_copy_id), '*', IGNORE_MULTIPLE)) {
423 | print_error('invalidresourcecopyid', 'local_ltiprovider');
424 | }
425 | $newcmid = local_ltiprovider_duplicate_module($cm->id, $courseid, $resource_link_id, $context);
426 | if ($cm = get_coursemodule_from_id(false, $newcmid)) {
427 | $urltogo = new moodle_url('/mod/' .$cm->modname. '/view.php', array('id' => $cm->id));
428 | }
429 | }
430 |
431 | } else if ($moodlecontext->contextlevel == CONTEXT_MODULE) {
432 | $cmid = $moodlecontext->instanceid;
433 | $cm = get_coursemodule_from_id(false, $moodlecontext->instanceid, 0, false, MUST_EXIST);
434 | $courseid = $cm->course;
435 | $urltogo = $CFG->wwwroot.'/mod/'.$cm->modname.'/view.php?id='.$cm->id;
436 | } else {
437 | print_error("invalidcontext");
438 | }
439 |
440 | $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
441 |
442 | // Enrol the user in the course
443 | $isinstructor = $context->isInstructor();
444 | local_ltiprovider_enrol_user($tool, $user, $isinstructor);
445 |
446 | // Check if we have to add the user to a group, and if so, add it.
447 | local_ltiprovider_add_user_to_group($tool, $user);
448 |
449 | if ($moodlecontext->contextlevel == CONTEXT_MODULE) {
450 | $role = $isinstructor ? 'instructor' : 'learner';
451 |
452 | // Enrol the user in the activity
453 | if (($tool->aroleinst and $isinstructor) or ($tool->arolelearn and !$isinstructor)) {
454 | $roleid = $isinstructor ? $tool->aroleinst : $tool->arolelearn;
455 | role_assign($roleid, $user->id, $tool->contextid);
456 | }
457 | }
458 |
459 | // Login user
460 | $sourceid = (!empty($context->info['lis_result_sourcedid'])) ? $context->info['lis_result_sourcedid'] : '';
461 | $serviceurl = (!empty($context->info['lis_outcome_service_url'])) ? $context->info['lis_outcome_service_url'] : '';
462 |
463 | if ($userlog = $DB->get_record('local_ltiprovider_user', array('toolid' => $tool->id, 'userid' => $user->id))) {
464 | if ( $userlog->sourceid != $sourceid ) {
465 | $DB->set_field('local_ltiprovider_user', 'sourceid', $sourceid, array('id' => $userlog->id));
466 | }
467 | if ( $userlog->serviceurl != $serviceurl ) {
468 | $DB->set_field('local_ltiprovider_user', 'serviceurl', $serviceurl, array('id' => $userlog->id));
469 | }
470 | $DB->set_field('local_ltiprovider_user', 'lastaccess', time(), array('id' => $userlog->id));
471 | } else {
472 | // These data is needed for sending backup outcomes (aka grades)
473 | $userlog = new stdClass();
474 | $userlog->userid = $user->id;
475 | $userlog->toolid = $tool->id;
476 | // TODO Improve these checks
477 | $userlog->serviceurl = $serviceurl;
478 | $userlog->sourceid = $sourceid;
479 | $userlog->consumerkey = $context->info['oauth_consumer_key'];
480 | // TODO Do not store secret here
481 | $userlog->consumersecret = $secret;
482 | $userlog->lastsync = 0;
483 | $userlog->lastgrade = 0;
484 | $userlog->lastaccess = time();
485 | $userlog->membershipsurl = (!empty($context->info['ext_ims_lis_memberships_url'])) ? $context->info['ext_ims_lis_memberships_url']: '';
486 | $userlog->membershipsid = (!empty($context->info['ext_ims_lis_memberships_id'])) ? $context->info['ext_ims_lis_memberships_id'] : '';
487 | $DB->insert_record('local_ltiprovider_user', $userlog);
488 | }
489 |
490 | $tool->context = $moodlecontext;
491 |
492 | $indexes = array('custom_force_navigation', 'custom_hide_left_blocks', 'custom_hide_right_blocks',
493 | 'custom_hide_page_header', 'custom_hide_page_footer', 'custom_custom_css', 'custom_show_blocks' );
494 |
495 | foreach ($indexes as $i) {
496 | if (empty($context->info[$i])) {
497 | $context->info[$i] = '';
498 | }
499 | }
500 |
501 | // Override some settings.
502 | if ($custom_force_navigation = $context->info['custom_force_navigation']) {
503 | $tool->forcenavigation = 1;
504 | }
505 | if ($custom_hide_left_blocks = $context->info['custom_hide_left_blocks']) {
506 | $tool->hideleftblocks = 1;
507 | }
508 | if ($custom_hide_right_blocks = $context->info['custom_hide_right_blocks']) {
509 | $tool->hiderightblocks = 1;
510 | }
511 | if ($custom_hide_page_header = $context->info['custom_hide_page_header']) {
512 | $tool->hidepageheader = 1;
513 | }
514 | if ($custom_hide_page_footer = $context->info['custom_hide_page_footer']) {
515 | $tool->hidepagefooter = 1;
516 | }
517 | if ($custom_custom_css = $context->info['custom_custom_css']) {
518 | $tool->customcss = $custom_custom_css;
519 | }
520 | if ($custom_show_blocks = $context->info['custom_show_blocks']) {
521 | $tool->showblocks = $custom_show_blocks;
522 | }
523 |
524 | $SESSION->ltiprovider = $tool;
525 |
526 | complete_user_login($user);
527 |
528 | // Trigger login event.
529 | $event = \core\event\user_loggedin::create(
530 | array(
531 | 'userid' => $user->id,
532 | 'objectid' => $user->id,
533 | 'other' => array('username' => $user->username),
534 | ));
535 | $event->trigger();
536 |
537 | // Moodle 2.2 and onwards.
538 | if (isset($CFG->allowframembedding) and !$CFG->allowframembedding) {
539 | echo '
540 |
541 |
542 | ';
543 | echo get_string('newpopupnotice', 'local_ltiprovider');
544 | $stropentool = get_string('opentool', 'local_ltiprovider');
545 | echo "$stropentool
";
546 | echo "".get_string('allowframembedding', 'local_ltiprovider')."
";
547 | echo '';
548 | } else {
549 | redirect($urltogo);
550 | }
551 | } else {
552 | // print_error('invalidcredentials', 'local_ltiprovider');
553 | echo $context->message;
554 | }
555 |
--------------------------------------------------------------------------------
/version.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Version details.
19 | *
20 | * @package local
21 | * @subpackage ltiprovider
22 | * @copyright 2011 Juan Leyva
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | defined('MOODLE_INTERNAL') || die;
27 |
28 | $plugin->version = 2016020104;
29 | $plugin->requires = 2015051100; // Require Moodle version (2.9).
30 | $plugin->maturity = MATURITY_STABLE;
31 | $plugin->release = '2.9.1';
32 | $plugin->component = 'local_ltiprovider';
33 |
--------------------------------------------------------------------------------