Merge branch 'MDL-54542-master-fixup' of https://github.com/FMCorz/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Thu, 19 May 2016 03:52:29 +0000 (11:52 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Thu, 19 May 2016 03:52:29 +0000 (11:52 +0800)
36 files changed:
blocks/myprofile/tests/behat/block_myprofile.feature [new file with mode: 0644]
blocks/myprofile/tests/behat/block_myprofile_activity.feature [new file with mode: 0644]
blocks/myprofile/tests/behat/block_myprofile_course.feature [new file with mode: 0644]
blocks/myprofile/tests/behat/block_myprofile_dashboard.feature [new file with mode: 0644]
blocks/myprofile/tests/behat/block_myprofile_frontpage.feature [new file with mode: 0644]
lib/amd/build/notification.min.js
lib/amd/src/notification.js
lib/grade/tests/grade_item_test.php
mod/lti/amd/build/external_registration.min.js
mod/lti/amd/build/tool_card_controller.min.js
mod/lti/amd/build/tool_configure_controller.min.js
mod/lti/amd/build/tool_proxy.min.js
mod/lti/amd/build/tool_proxy_card_controller.min.js [new file with mode: 0644]
mod/lti/amd/build/tool_type.min.js
mod/lti/amd/src/external_registration.js
mod/lti/amd/src/tool_card_controller.js
mod/lti/amd/src/tool_configure_controller.js
mod/lti/amd/src/tool_proxy.js
mod/lti/amd/src/tool_proxy_card_controller.js [new file with mode: 0644]
mod/lti/amd/src/tool_type.js
mod/lti/classes/external.php
mod/lti/classes/output/renderer.php
mod/lti/db/services.php
mod/lti/externalregistrationreturn.php
mod/lti/lang/en/lti.php
mod/lti/locallib.php
mod/lti/styles.css
mod/lti/templates/external_registration.mustache
mod/lti/templates/external_registration_return.mustache [moved from mod/lti/templates/registration_feedback.mustache with 57% similarity]
mod/lti/templates/tool_configure.mustache
mod/lti/templates/tool_list.mustache
mod/lti/templates/tool_proxy_card.mustache [new file with mode: 0644]
mod/lti/templates/tool_proxy_registration_form.mustache
mod/lti/toolssettings.php
mod/lti/typessettings.php
mod/lti/version.php

diff --git a/blocks/myprofile/tests/behat/block_myprofile.feature b/blocks/myprofile/tests/behat/block_myprofile.feature
new file mode 100644 (file)
index 0000000..8bd076c
--- /dev/null
@@ -0,0 +1,309 @@
+@block @block_myprofile
+Feature: The logged in user block allows users to view their profile information
+  In order to enable the logged in user block
+  As a user
+  I can add the logged in user block and configure it to show my information
+
+  Scenario: Configure the logged in user block to show / hide the users country
+    Given the following "users" exist:
+      | username | firstname | lastname | email                | country   |
+      | teacher1 | Teacher   | One      | teacher1@example.com | AU        |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display country       | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "Australia" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display country | Yes |
+    And I press "Save changes"
+    And I should see "Australia" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users city
+    Given the following "users" exist:
+      | username | firstname | lastname | email                | city  |
+      | teacher1 | Teacher   | One      | teacher1@example.com | Perth |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display city          | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "Perth" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display city | Yes |
+    And I press "Save changes"
+    And I should see "Perth" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users email
+    Given the following "users" exist:
+      | username | firstname | lastname | email                |
+      | teacher1 | Teacher   | One      | teacher1@example.com |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display email         | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "teacher1@example.com" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display email | Yes |
+    And I press "Save changes"
+    And I should see "teacher1@example.com" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users ICQ
+    Given the following "users" exist:
+      | username | firstname | lastname | email                | icq   |
+      | teacher1 | Teacher   | One      | teacher1@example.com | myicq |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display ICQ           | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "myicq" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display ICQ | Yes |
+    And I press "Save changes"
+    And I should see "myicq" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users Skype
+    Given the following "users" exist:
+      | username | firstname | lastname | email                | skype   |
+      | teacher1 | Teacher   | One      | teacher1@example.com | myskype |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display Skype         | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "myskype" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display Skype | Yes |
+    And I press "Save changes"
+    And I should see "myskype" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users Yahoo
+    Given the following "users" exist:
+      | username | firstname | lastname | email                | yahoo   |
+      | teacher1 | Teacher   | One      | teacher1@example.com | myyahoo |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display Yahoo         | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "myyahoo" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display Yahoo | Yes |
+    And I press "Save changes"
+    And I should see "myyahoo" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users AIM
+    Given the following "users" exist:
+      | username | firstname | lastname | email                | aim   |
+      | teacher1 | Teacher   | One      | teacher1@example.com | myaim |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display AIM           | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "myaim" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display AIM | Yes |
+    And I press "Save changes"
+    And I should see "myaim" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users MSN
+    Given the following "users" exist:
+      | username | firstname | lastname | email                | msn   |
+      | teacher1 | Teacher   | One      | teacher1@example.com | mymsn |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display MSN           | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "mymsn" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display MSN | Yes |
+    And I press "Save changes"
+    And I should see "mymsn" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users phone
+    Given the following "users" exist:
+      | username | firstname | lastname | email                | phone1   |
+      | teacher1 | Teacher   | One      | teacher1@example.com | 555-5555 |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display phone         | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "555-5555" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display phone | Yes |
+    And I press "Save changes"
+    And I should see "555-5555" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users mobile phone
+    Given the following "users" exist:
+      | username | firstname | lastname | email                | phone2   |
+      | teacher1 | Teacher   | One      | teacher1@example.com | 555-5555 |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display mobile phone | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "555-5555" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display mobile phone | Yes |
+    And I press "Save changes"
+    And I should see "555-5555" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users Institution
+    Given the following "users" exist:
+      | username | firstname | lastname | email                | institution   |
+      | teacher1 | Teacher   | One      | teacher1@example.com | myinstitution |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display institution | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "myinstitution" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display institution | Yes |
+    And I press "Save changes"
+    And I should see "myinstitution" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users address
+    Given the following "users" exist:
+      | username | firstname | lastname | email                | address   |
+      | teacher1 | Teacher   | One      | teacher1@example.com | myaddress |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display address | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "myaddress" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display address | Yes |
+    And I press "Save changes"
+    And I should see "myaddress" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users first access
+    Given the following "users" exist:
+      | username | firstname | lastname | email                |
+      | teacher1 | Teacher   | One      | teacher1@example.com |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display first access | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "First access:" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display first access | Yes |
+    And I press "Save changes"
+    And I should see "First access:" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users last access
+    Given the following "users" exist:
+      | username | firstname | lastname | email                |
+      | teacher1 | Teacher   | One      | teacher1@example.com |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display last access | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "Last access:" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display last access | Yes |
+    And I press "Save changes"
+    And I should see "Last access:" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users current login
+    Given the following "users" exist:
+      | username | firstname | lastname | email                |
+      | teacher1 | Teacher   | One      | teacher1@example.com |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display current login | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "Log in:" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display current login | Yes |
+    And I press "Save changes"
+    And I should see "Log in:" in the "Logged in user" "block"
+
+  Scenario: Configure the logged in user block to show / hide the users last ip
+    Given the following "users" exist:
+      | username | firstname | lastname | email                |
+      | teacher1 | Teacher   | One      | teacher1@example.com |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display last IP | No |
+    And I press "Save changes"
+    Then I should see "Teacher One" in the "Logged in user" "block"
+    And I should not see "IP:" in the "Logged in user" "block"
+    And I configure the "Logged in user" block
+    And I set the following fields to these values:
+      | Display last IP | Yes |
+    And I press "Save changes"
+    And I should see "IP:" in the "Logged in user" "block"
diff --git a/blocks/myprofile/tests/behat/block_myprofile_activity.feature b/blocks/myprofile/tests/behat/block_myprofile_activity.feature
new file mode 100644 (file)
index 0000000..fb7cfd2
--- /dev/null
@@ -0,0 +1,25 @@
+@block @block_myprofile
+Feature: The logged in user block allows users to view their profile information in an activity
+  In order to enable the logged in user block in an activity
+  As a teacher
+  I can add the logged in user block to an activity and view my information
+
+  Scenario: View the logged in user block by a user in an activity
+    Given the following "users" exist:
+      | username | firstname | lastname | email | idnumber |
+      | teacher1 | Teacher | One | teacher1@example.com | T1 |
+    And the following "courses" exist:
+      | fullname | shortname | category |
+      | Course 1 | C1 | 0 |
+    And the following "course enrolments" exist:
+      | user | course | role |
+      | teacher1 | C1 | editingteacher |
+    And the following "activities" exist:
+      | activity | course | idnumber | name           | intro                 |
+      | page     | C1     | page1    | Test page name | Test page description |
+    And I log in as "teacher1"
+    And I follow "Course 1"
+    And I turn editing mode on
+    And I follow "Test page name"
+    When I add the "Logged in user" block
+    Then I should see "Teacher One" in the "Logged in user" "block"
diff --git a/blocks/myprofile/tests/behat/block_myprofile_course.feature b/blocks/myprofile/tests/behat/block_myprofile_course.feature
new file mode 100644 (file)
index 0000000..81d2276
--- /dev/null
@@ -0,0 +1,21 @@
+@block @block_myprofile
+Feature: The logged in user block allows users to view their profile information in a course
+  In order to enable the logged in user block in a course
+  As a teacher
+  I can add the logged in user block to a course and view my information
+
+  Scenario: View the logged in user block by a user in a course
+    Given the following "users" exist:
+      | username | firstname | lastname | email                | idnumber |
+      | teacher1 | Teacher   | One      | teacher1@example.com | T1       |
+    And the following "courses" exist:
+      | fullname | shortname | category |
+      | Course 1 | C1 | 0 |
+    And the following "course enrolments" exist:
+      | user | course | role |
+      | teacher1 | C1 | editingteacher |
+    When I log in as "teacher1"
+    And I follow "Course 1"
+    And I turn editing mode on
+    And I add the "Logged in user" block
+    Then I should see "Teacher One" in the "Logged in user" "block"
diff --git a/blocks/myprofile/tests/behat/block_myprofile_dashboard.feature b/blocks/myprofile/tests/behat/block_myprofile_dashboard.feature
new file mode 100644 (file)
index 0000000..e130522
--- /dev/null
@@ -0,0 +1,14 @@
+@block @block_myprofile
+Feature: The logged in user block allows users to view their profile information in on the dashboard
+  In order to enable the logged in user block on the dashboard
+  As a user
+  I can add the logged in user block to a the dashboard and view my information
+
+  Scenario: View the logged in user block by a user on the dashboard
+    Given the following "users" exist:
+      | username | firstname | lastname | email                |
+      | teacher1 | Teacher   | One      | teacher1@example.com |
+    And I log in as "teacher1"
+    And I press "Customise this page"
+    When I add the "Logged in user" block
+    Then I should see "Teacher One" in the "Logged in user" "block"
diff --git a/blocks/myprofile/tests/behat/block_myprofile_frontpage.feature b/blocks/myprofile/tests/behat/block_myprofile_frontpage.feature
new file mode 100644 (file)
index 0000000..5b14df3
--- /dev/null
@@ -0,0 +1,25 @@
+@block @block_myprofile
+Feature: The logged in user block allows users to view their profile information on the front page
+  In order to enable the logged in user block on the frontpage
+  As an admin
+  I can add the logged in user block to the frontpage and view my information
+
+  Background:
+    Given the following "users" exist:
+      | username | firstname | lastname | email                | idnumber |
+      | teacher1 | Teacher   | One      | teacher1@example.com | T1       |
+    And I log in as "admin"
+    And I am on site homepage
+    And I navigate to "Turn editing on" node in "Front page settings"
+    And I add the "Logged in user" block
+    And I log out
+
+  Scenario: Try to view the logged in user block as a guest
+    Given I log in as "guest"
+    When I am on site homepage
+    Then I should not see "Logged in user"
+
+  Scenario: View the logged in user block by a logged in user
+    Given I log in as "teacher1"
+    When I am on site homepage
+    Then I should see "Teacher One" in the "Logged in user" "block"
index f03cbbb..cf10e4f 100644 (file)
Binary files a/lib/amd/build/notification.min.js and b/lib/amd/build/notification.min.js differ
index da34f43..a887c8e 100644 (file)
@@ -137,7 +137,7 @@ function(Y, $, log) {
             });
         },
 
-        confirm: function(title, question, yesLabel, noLabel, callback) {
+        confirm: function(title, question, yesLabel, noLabel, yesCallback, noCallback) {
             // Here we are wrapping YUI. This allows us to start transitioning, but
             // wait for a good alternative without having inconsistent dialogues.
             Y.use('moodle-core-notification-confirm', function () {
@@ -149,8 +149,13 @@ function(Y, $, log) {
                 });
 
                 modal.on('complete-yes', function() {
-                    callback();
+                    yesCallback();
                 });
+                if (noCallback) {
+                    modal.on('complete-no', function() {
+                        noCallback();
+                    });
+                }
                 modal.show();
             });
         },
@@ -225,7 +230,8 @@ function(Y, $, log) {
          * @param {string} question
          * @param {string} yesLabel
          * @param {string} noLabel
-         * @param {function} callback
+         * @param {function} yesCallback
+         * @param {function} noCallback Optional parameter to be called if the user presses cancel.
          */
         confirm: notificationModule.confirm,
 
index a8a8dd5..ade1d89 100644 (file)
@@ -651,6 +651,7 @@ class core_grade_item_testcase extends grade_base_testcase {
         $grade_item->itemtype = 'mod';
         $grade_item->itemmodule = 'quiz';
         $grade_item->iteminfo = 'Grade item used for unit testing';
+        $grade_item->iteminstance = $this->activities[7]->id;
         $grade_item->grademin = $min;
         $grade_item->grademax = $max;
         $grade_item->insert();
index b3d9e0f..6b58c4c 100644 (file)
Binary files a/mod/lti/amd/build/external_registration.min.js and b/mod/lti/amd/build/external_registration.min.js differ
index d1c8e68..716195b 100644 (file)
Binary files a/mod/lti/amd/build/tool_card_controller.min.js and b/mod/lti/amd/build/tool_card_controller.min.js differ
index a3bdb5c..e0b00e9 100644 (file)
Binary files a/mod/lti/amd/build/tool_configure_controller.min.js and b/mod/lti/amd/build/tool_configure_controller.min.js differ
index 7e2826f..f7db8dc 100644 (file)
Binary files a/mod/lti/amd/build/tool_proxy.min.js and b/mod/lti/amd/build/tool_proxy.min.js differ
diff --git a/mod/lti/amd/build/tool_proxy_card_controller.min.js b/mod/lti/amd/build/tool_proxy_card_controller.min.js
new file mode 100644 (file)
index 0000000..cb52434
Binary files /dev/null and b/mod/lti/amd/build/tool_proxy_card_controller.min.js differ
index befef5e..bc96935 100644 (file)
Binary files a/mod/lti/amd/build/tool_type.min.js and b/mod/lti/amd/build/tool_type.min.js differ
index cfffbc2..4b65abf 100644 (file)
@@ -34,7 +34,6 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
         function($, ajax, notification, templates, ltiEvents, toolProxy, toolType, KEYS, str) {
 
     var SELECTORS = {
-        REGISTRATION_URL: '#external-registration-url',
         EXTERNAL_REGISTRATION_CONTAINER: '#external-registration-page-container',
         EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER: '#external-registration-template-container',
         EXTERNAL_REGISTRATION_CANCEL_BUTTON: '#cancel-external-registration',
@@ -43,17 +42,6 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
         CAPABILITIES_AGREE_CONTAINER: '.capabilities-container',
     };
 
-    /**
-     * Return the URL the user entered for the registration.
-     *
-     * @method getRegistrationURL
-     * @private
-     * @return string
-     */
-    var getRegistrationURL = function() {
-        return $(SELECTORS.EXTERNAL_REGISTRATION_CONTAINER).attr('data-registration-url');
-    };
-
     /**
      * Return the external registration cancel button element. This button is
      * the cancel button that appears while the iframe is rendered.
@@ -230,6 +218,17 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
         button.removeAttr('data-tool-proxy-id');
     };
 
+    /**
+     * Returns true if a tool proxy id has been recorded.
+     *
+     * @method hasToolProxyId
+     * @private
+     * @return bool
+     */
+    var hasToolProxyId = function() {
+        return getToolProxyId() ? true : false;
+    };
+
     /**
      * Checks if this process has created a tool proxy within
      * Moodle yet.
@@ -239,7 +238,32 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
      * @return bool
      */
     var hasCreatedToolProxy = function() {
-        return getToolProxyId() ? true : false;
+        var button = getExternalRegistrationCancelButton();
+        return button.attr('data-tool-proxy-new') && hasToolProxyId();
+    };
+
+    /**
+     * Records that this process has created a tool proxy.
+     *
+     * @method setProxyAsNew
+     * @private
+     * @return bool
+     */
+    var setProxyAsNew = function() {
+        var button = getExternalRegistrationCancelButton();
+        return button.attr('data-tool-proxy-new', "new");
+    };
+
+    /**
+     * Records that this process has not created a tool proxy.
+     *
+     * @method setProxyAsOld
+     * @private
+     * @return bool
+     */
+    var setProxyAsOld = function() {
+        var button = getExternalRegistrationCancelButton();
+        return button.removeAttr('data-tool-proxy-new');
     };
 
     /**
@@ -412,49 +436,72 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
      * If the tool proxy creation fails then we redirect the page section back to the home section and
      * display the error, rather than rendering the external registration page.
      *
-     * @method submitExternalRegistration
+     * @method createAndRegisterToolProxy
      * @private
+     * @param url Tool registration URL to register
      * @return object jQuery Deferred object
      */
-    var submitExternalRegistration = function() {
+    var createAndRegisterToolProxy = function(url) {
         var promise = $.Deferred();
-        var url = getRegistrationURL();
 
-        if (url === "") {
+        if (!url || url === "") {
             // No URL has been input so do nothing.
             promise.resolve();
         } else {
-            // A tool proxy needs to exists before the external page is rendered because
+            // A tool proxy needs to exist before the external page is rendered because
             // the external page sends requests back to Moodle for information that is stored
             // in the proxy.
-            toolProxy.create({regurl: url}).done(function(result) {
-                var id = result.id;
-                var regURL = result.regurl;
-
-                // Save the id on the DOM to cleanup later.
-                setToolProxyId(id);
-
-                // There is a specific set of data needed to send to the external registration page
-                // in a form, so let's get it from our server.
-                getRegistrationRequest(id).done(function(registrationRequest) {
-
-                    registrationRequest.reg_url = regURL;
-                    renderExternalRegistrationWindow(registrationRequest).done(function() {
-
-                        promise.resolve();
-
-                    }).fail(promise.fail);
+            toolProxy.create({regurl: url})
+                .done(function(result) {
+                        // Note that it's a new proxy so we will always clean it up.
+                        setProxyAsNew();
+                        promise = registerProxy(result.id);
+                    })
+                .fail(function(exception) {
+                        // Clean up.
+                        cancelRegistration();
+                        // Let the user know what the error is.
+                        str.get_string('error', 'moodle')
+                            .done(function (s) {
+                                    var feedback = {
+                                        status: s,
+                                        message: exception.message,
+                                        error: true
+                                    };
+                                    $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, feedback);
+                                })
+                            .fail(notification.exception);
+                        promise.reject(exception);
+                    });
+        }
 
-                }).fail(promise.fail);
+        return promise;
+    };
 
-            }).fail(function(exception) {
-                // Clean up.
-                cancelRegistration();
-                // Let the user know what the error is.
-                $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, {status: 'error', message: exception.message, error: true});
-                promise.reject(exception);
-            });
-        }
+    /**
+     * Loads the window to register a proxy, given an ID.
+     *
+     * @method registerProxy
+     * @private
+     * @param id Proxy id to register
+     * @return jQuery Deferred object to fail or resolve
+     */
+    var registerProxy = function(id) {
+        var promise = $.Deferred();
+        // Save the id on the DOM to cleanup later.
+        setToolProxyId(id);
+
+        // There is a specific set of data needed to send to the external registration page
+        // in a form, so let's get it from our server.
+        getRegistrationRequest(id)
+            .done(function(registrationRequest) {
+                    renderExternalRegistrationWindow(registrationRequest)
+                        .done(function() {
+                                promise.resolve();
+                            })
+                        .fail(promise.fail);
+                })
+            .fail(promise.fail);
 
         return promise;
     };
@@ -467,9 +514,10 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
      * @private
      */
     var finishExternalRegistration = function() {
-        if (hasCreatedToolProxy()) {
+        if (hasToolProxyId()) {
             clearToolProxyId();
         }
+        setProxyAsOld(false);
 
         hideExternalRegistrationContent();
         var container = getExternalRegistrationTemplateContainer();
@@ -486,9 +534,17 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
      */
     var registerEventListeners = function() {
 
-        $(document).on(ltiEvents.START_EXTERNAL_REGISTRATION, function() {
-            submitExternalRegistration();
-        });
+        $(document).on(ltiEvents.START_EXTERNAL_REGISTRATION, function(event, data) {
+                if (!data) {
+                    return;
+                }
+                if (data.url) {
+                    createAndRegisterToolProxy(data.url);
+                }
+                if (data.proxyid) {
+                    registerProxy(data.proxyid);
+                }
+            });
 
         var cancelExternalRegistrationButton = getExternalRegistrationCancelButton();
         cancelExternalRegistrationButton.click(function(e) {
index 1f0a222..19b4c48 100644 (file)
@@ -26,8 +26,9 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  * @since      3.1
  */
-define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/tool_type', 'mod_lti/events', 'mod_lti/keys'],
-        function($, ajax, notification, templates, toolType, ltiEvents, KEYS) {
+define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/tool_type', 'mod_lti/events', 'mod_lti/keys',
+        'core/str'],
+        function($, ajax, notification, templates, toolType, ltiEvents, KEYS, str) {
 
     var SELECTORS = {
         DELETE_BUTTON: '.delete',
@@ -226,23 +227,61 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/t
      * @return object jQuery Deferred object
      */
     var deleteType = function(element) {
+        var promise = $.Deferred();
         var typeId = getTypeId(element);
+        startLoading(element);
 
         if (typeId === "") {
             return $.Deferred().resolve();
         }
 
-        startLoading(element);
-        var promise = toolType.delete(typeId);
-
-        promise.done(function() {
-            stopLoading(element);
-            announceSuccess(element).done(function() {
-                element.remove();
-            }).fail(notification.exception);
-        });
-
-        promise.fail(function() { announceFailure(element); });
+        str.get_strings([
+                {
+                    key: 'delete',
+                    component: 'mod_lti'
+                },
+                {
+                    key: 'delete_confirmation',
+                    component: 'mod_lti'
+                },
+                {
+                    key: 'delete',
+                    component: 'mod_lti'
+                },
+                {
+                    key: 'cancel',
+                    component: 'core'
+                },
+            ])
+            .done(function(strs) {
+                    notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {
+                            toolType.delete(typeId)
+                                .done(function() {
+                                        stopLoading(element);
+                                        announceSuccess(element)
+                                            .done(function() {
+                                                    element.remove();
+                                                })
+                                            .fail(notification.exception)
+                                            .always(function() {
+                                                    // Always resolve because even if the announcement fails the type was deleted.
+                                                    promise.resolve();
+                                                });
+                                    })
+                                .fail(function(error) {
+                                        announceFailure(element);
+                                        promise.reject(error);
+                                    });
+                        }, function () {
+                                stopLoading(element);
+                                promise.resolve();
+                            });
+                })
+            .fail(function(error) {
+                    stopLoading(element);
+                    notification.exception(error);
+                    promise.reject(error);
+                });
 
         return promise;
     };
index 39a63f8..9277df4 100644 (file)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  * @since      3.1
  */
-define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/events', 'mod_lti/keys', 'mod_lti/tool_type'],
-        function($, ajax, notification, templates, ltiEvents, KEYS, toolType) {
+define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/events', 'mod_lti/keys', 'mod_lti/tool_type',
+        'mod_lti/tool_proxy', 'core/str'],
+        function($, ajax, notification, templates, ltiEvents, KEYS, toolType, toolProxy, str) {
 
     var SELECTORS = {
-        REGISTRATION_FEEDBACK_CONTAINER: '#registration-feedback-container',
         EXTERNAL_REGISTRATION_CONTAINER: '#external-registration-container',
         EXTERNAL_REGISTRATION_PAGE_CONTAINER: '#external-registration-page-container',
         CARTRIDGE_REGISTRATION_CONTAINER: '#cartridge-registration-container',
@@ -52,17 +52,6 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
         return $(SELECTORS.TOOL_CREATE_BUTTON);
     };
 
-    /**
-     * Get the registration feedback container element.
-     *
-     * @method getRegistrationFeedbackContainer
-     * @private
-     * @return object jQuery object
-     */
-    var getRegistrationFeedbackContainer = function() {
-        return $(SELECTORS.REGISTRATION_FEEDBACK_CONTAINER);
-    };
-
     /**
      * Get the tool list container element.
      *
@@ -155,11 +144,10 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
      * @method showExternalRegistration
      * @private
      */
-    var showExternalRegistration = function(url) {
+    var showExternalRegistration = function() {
         hideCartridgeRegistration();
         hideRegistrationChoices();
         getExternalRegistrationContainer().removeClass('hidden');
-        getExternalRegistrationContainer().find(SELECTORS.EXTERNAL_REGISTRATION_PAGE_CONTAINER).attr('data-registration-url', url);
         screenReaderAnnounce(getExternalRegistrationContainer());
     };
 
@@ -200,13 +188,6 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
      * @private
      */
     var showRegistrationChoices = function() {
-        if (isRegistrationFeedbackVisible()) {
-            // If the registration feedback is visible then we don't need
-            // to do anything because it will display this content when it's
-            // closed.
-            return;
-        }
-
         hideExternalRegistration();
         hideCartridgeRegistration();
         getRegistrationChoiceContainer().removeClass('hidden');
@@ -233,17 +214,6 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
         getToolListContainer().removeClass('hidden');
     };
 
-    /**
-     * Check if the registration feedback is being displayed.
-     *
-     * @method isRegistrationFeedbackVisible
-     * @private
-     * @return bool
-     */
-    var isRegistrationFeedbackVisible = function() {
-        return $.trim(getRegistrationFeedbackContainer().html());
-    };
-
     /**
      * Display the registration feedback alert and hide the other panels.
      *
@@ -251,27 +221,11 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
      * @private
      */
     var showRegistrationFeedback = function(data) {
-        templates.render('mod_lti/registration_feedback', data).done(function(html) {
-            hideExternalRegistration();
-            hideCartridgeRegistration();
-            hideRegistrationChoices();
-
-            var container = getRegistrationFeedbackContainer();
-            container.append(html);
-        }).fail(notification.exception);
-    };
-
-    /**
-     * Hide the registration feedback alert and restore the choices panel.
-     *
-     * @method showRegistrationFeedback
-     * @private
-     */
-    var clearRegistrationFeedback = function() {
-        var container = getRegistrationFeedbackContainer();
-        container.empty();
-
-        showRegistrationChoices();
+        var type = data.error ? 'error' : 'success';
+        notification.addNotification({
+            message: '<h4>' + data.status + '</h4><p>' + data.message + '</p>',
+            type: type
+        });
     };
 
     /**
@@ -303,16 +257,29 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
      * @private
      */
     var reloadToolList = function() {
+        var promise = $.Deferred();
         var container = getToolListContainer();
         startLoading(container);
 
-        toolType.query().done(function(types) {
-            templates.render('mod_lti/tool_list', {tools: types}).done(function(html, js) {
-                container.empty();
-                container.append(html);
-                templates.runTemplateJS(js);
-            }).fail(notification.exception);
-        }).fail(notification.exception).always(function() { stopLoading(container); });
+        $.when(
+                toolType.query(),
+                toolProxy.query({'orphanedonly': true})
+            )
+            .done(function(types, proxies) {
+                    templates.render('mod_lti/tool_list', {tools: types, proxies: proxies})
+                        .done(function(html, js) {
+                                container.empty();
+                                container.append(html);
+                                templates.runTemplateJS(js);
+                                promise.resolve();
+                            }).fail(promise.reject);
+                })
+            .fail(promise.reject);
+
+        promise.fail(notification.exception)
+            .always(function () {
+                    stopLoading(container);
+                });
     };
 
     /**
@@ -324,7 +291,7 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
      * @return object jQuery deferred object
      */
     var addTool = function() {
-        var url = getToolURL();
+        var url = $.trim(getToolURL());
 
         if (url === "") {
             return $.Deferred().resolve();
@@ -342,14 +309,22 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
                 $(SELECTORS.TOOL_URL).val('');
                 $(document).trigger(ltiEvents.START_CARTRIDGE_REGISTRATION, url);
             } else {
-                showExternalRegistration(url);
-                $(SELECTORS.TOOL_URL).val('');
-                $(document).trigger(ltiEvents.START_EXTERNAL_REGISTRATION);
-                hideToolList();
+                $(document).trigger(ltiEvents.START_EXTERNAL_REGISTRATION, {url: url});
             }
         });
 
-        promise.fail(notification.exception);
+        promise.fail(function () {
+            str.get_strings([{key: 'error', component: 'moodle'},
+                             {key: 'errorbadurl', component: 'mod_lti'}])
+                .done(function (s) {
+                        $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, {
+                                status: s[0],
+                                message: s[1],
+                                error: true
+                            });
+                    })
+                .fail(notification.exception);
+        });
 
         return promise;
     };
@@ -368,6 +343,12 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
             reloadToolList();
         });
 
+        $(document).on(ltiEvents.START_EXTERNAL_REGISTRATION, function() {
+            showExternalRegistration();
+            $(SELECTORS.TOOL_URL).val('');
+            hideToolList();
+        });
+
         $(document).on(ltiEvents.STOP_EXTERNAL_REGISTRATION, function() {
             showToolList();
             showRegistrationChoices();
@@ -392,19 +373,6 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e
             addTool();
         });
 
-        var feedbackContainer = getRegistrationFeedbackContainer();
-        feedbackContainer.click(function(e) {
-            e.preventDefault();
-            clearRegistrationFeedback();
-        });
-        feedbackContainer.keypress(function(e) {
-            if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {
-                if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {
-                    e.preventDefault();
-                    clearRegistrationFeedback();
-                }
-            }
-        });
     };
 
     return /** @alias module:mod_lti/cartridge_registration_form */ {
index 97d97fd..8bc62b3 100644 (file)
  */
 define(['core/ajax', 'core/notification'], function(ajax, notification) {
     return /** @alias module:mod_lti/tool_proxy */ {
+        /**
+         * Get a list of tool types from Moodle for the given
+         * search args.
+         *
+         * See also:
+         * mod/lti/classes/external.php get_tool_types_parameters()
+         *
+         * @method query
+         * @public
+         * @param object Search parameters
+         * @return object jQuery deferred object
+         */
+        query: function(args) {
+            var request = {
+                methodname: 'mod_lti_get_tool_proxies',
+                args: args || {}
+            };
+
+            var promise = ajax.call([request])[0];
+
+            promise.fail(notification.exception);
+
+            return promise;
+        },
         /**
          * Delete a tool proxy from Moodle.
          *
diff --git a/mod/lti/amd/src/tool_proxy_card_controller.js b/mod/lti/amd/src/tool_proxy_card_controller.js
new file mode 100644 (file)
index 0000000..c2b1a25
--- /dev/null
@@ -0,0 +1,284 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Controls all of the behaviour and interaction with a tool type card. These are
+ * listed on the LTI tool type management page.
+ *
+ * See template: mod_lti/tool_proxy_card
+ *
+ * @module     mod_lti/tool_proxy_card_controller
+ * @class      tool_card_controller
+ * @package    mod_lti
+ * @copyright  2016 John Okely <john@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      3.1
+ */
+define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/tool_proxy', 'mod_lti/events', 'mod_lti/keys',
+        'core/str'],
+        function($, ajax, notification, templates, toolProxy, ltiEvents, KEYS, str) {
+
+    var SELECTORS = {
+        DELETE_BUTTON: '.delete',
+        CAPABILITIES_CONTAINER: '.capabilities-container',
+        ACTIVATE_BUTTON: '.tool-card-footer a.activate',
+    };
+
+    // Timeout in seconds.
+    var ANNOUNCEMENT_TIMEOUT = 2000;
+
+    /**
+     * Return the delete button element.
+     *
+     * @method getDeleteButton
+     * @private
+     * @param object jQuery object representing the tool card.
+     * @return object jQuery object
+     */
+    var getDeleteButton = function(element) {
+        return element.find(SELECTORS.DELETE_BUTTON);
+    };
+
+    /**
+     * Return the activate button for the type.
+     *
+     * @method getActivateButton
+     * @private
+     * @param object jQuery object representing the tool card.
+     * @return object jQuery object
+     */
+    var getActivateButton = function(element) {
+        return element.find(SELECTORS.ACTIVATE_BUTTON);
+    };
+
+    /**
+     * Get the type id.
+     *
+     * @method getTypeId
+     * @private
+     * @param object jQuery object representing the tool card.
+     * @return string Type ID
+     */
+    var getTypeId = function(element) {
+        return element.attr('data-proxy-id');
+    };
+
+    /**
+     * Stop any announcement currently visible on the card.
+     *
+     * @method clearAllAnnouncements
+     * @private
+     * @param object jQuery object representing the tool card.
+     */
+    var clearAllAnnouncements = function(element) {
+        element.removeClass('announcement loading success fail capabilities');
+    };
+
+    /**
+     * Show the loading announcement.
+     *
+     * @method startLoading
+     * @private
+     * @param object jQuery object representing the tool card.
+     */
+    var startLoading = function(element) {
+        clearAllAnnouncements(element);
+        element.addClass('announcement loading');
+    };
+
+    /**
+     * Hide the loading announcement.
+     *
+     * @method stopLoading
+     * @private
+     * @param object jQuery object representing the tool card.
+     */
+    var stopLoading = function(element) {
+        element.removeClass('announcement loading');
+    };
+
+    /**
+     * Show the success announcement. The announcement is only
+     * visible for 2 seconds.
+     *
+     * @method announceSuccess
+     * @private
+     * @param object jQuery object representing the tool card.
+     * @return object jQuery Deferred object
+     */
+    var announceSuccess = function(element) {
+        var promise = $.Deferred();
+
+        clearAllAnnouncements(element);
+        element.addClass('announcement success');
+        setTimeout(function() {
+            element.removeClass('announcement success');
+            promise.resolve();
+        }, ANNOUNCEMENT_TIMEOUT);
+
+        return promise;
+    };
+
+    /**
+     * Show the failure announcement. The announcement is only
+     * visible for 2 seconds.
+     *
+     * @method announceFailure
+     * @private
+     * @param object jQuery object representing the tool card.
+     * @return object jQuery Deferred object
+     */
+    var announceFailure = function(element) {
+        var promise = $.Deferred();
+
+        clearAllAnnouncements(element);
+        element.addClass('announcement fail');
+        setTimeout(function() {
+                element.removeClass('announcement fail');
+                promise.resolve();
+            }, ANNOUNCEMENT_TIMEOUT);
+
+        return promise;
+    };
+
+    /**
+     * Delete the tool type from the Moodle server. Triggers a success
+     * or failure announcement depending on the result.
+     *
+     * @method deleteType
+     * @private
+     * @param object jQuery object representing the tool card.
+     * @return object jQuery Deferred object
+     */
+    var deleteType = function(element) {
+        var promise = $.Deferred();
+        var typeId = getTypeId(element);
+        startLoading(element);
+
+        if (typeId === "") {
+            return $.Deferred().resolve();
+        }
+
+        str.get_strings([
+                {
+                    key: 'delete',
+                    component: 'mod_lti'
+                },
+                {
+                    key: 'delete_confirmation',
+                    component: 'mod_lti'
+                },
+                {
+                    key: 'delete',
+                    component: 'mod_lti'
+                },
+                {
+                    key: 'cancel',
+                    component: 'core'
+                },
+            ])
+            .done(function(strs) {
+                    notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {
+                            toolProxy.delete(typeId)
+                                .done(function() {
+                                        stopLoading(element);
+                                        announceSuccess(element)
+                                            .done(function() {
+                                                    element.remove();
+                                                    promise.resolve();
+                                                })
+                                            .fail(notification.exception);
+                                    })
+                                .fail(function(error) {
+                                        announceFailure(element);
+                                        promise.reject(error);
+                                    });
+                    }, function () {
+                            stopLoading(element);
+                            promise.resolve();
+                        });
+                })
+            .fail(function(error) {
+                    stopLoading(element);
+                    notification.exception(error);
+                    promise.reject(error);
+                });
+
+        return promise;
+    };
+
+    /**
+     * The user wishes to activate this tool so show them the capabilities that
+     * they need to agree to or if there are none then set the tool type's state
+     * to active.
+     *
+     * @method activateToolType
+     * @private
+     * @param object jQuery object representing the tool card.
+     */
+    var activateToolType = function(element) {
+        var data = {proxyid: getTypeId(element)};
+        $(document).trigger(ltiEvents.START_EXTERNAL_REGISTRATION, data);
+    };
+
+    /**
+     * Sets up the listeners for user interaction on this tool type card.
+     *
+     * @method registerEventListeners
+     * @private
+     * @param object jQuery object representing the tool card.
+     */
+    var registerEventListeners = function(element) {
+        var deleteButton = getDeleteButton(element);
+        deleteButton.click(function(e) {
+                e.preventDefault();
+                deleteType(element);
+            });
+        deleteButton.keypress(function(e) {
+                if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {
+                    if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {
+                        e.preventDefault();
+                        deleteButton.click();
+                    }
+                }
+            });
+
+        var activateButton = getActivateButton(element);
+        activateButton.click(function(e) {
+                e.preventDefault();
+                activateToolType(element);
+            });
+        activateButton.keypress(function(e) {
+                if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {
+                    if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {
+                        e.preventDefault();
+                        activateButton.click();
+                    }
+                }
+            });
+    };
+
+    return /** @alias module:mod_lti/tool_card_controller */ {
+
+        /**
+         * Initialise this module.
+         *
+         * @param object jQuery object representing the tool card.
+         */
+        init: function(element) {
+            registerEventListeners(element);
+        }
+    };
+});
index d8605bc..15efac6 100644 (file)
@@ -137,6 +137,8 @@ define(['core/ajax', 'core/notification'], function(ajax, notification) {
         /**
          * Check if the given URL is a cartridge URL.
          *
+         * The promise will fail if the URL is unreachable, so you must handle the fail result.
+         *
          * @method isCartridge
          * @public
          * @param string URL
@@ -152,8 +154,6 @@ define(['core/ajax', 'core/notification'], function(ajax, notification) {
 
             var promise = ajax.call([request])[0];
 
-            promise.fail(notification.exception);
-
             return promise;
         },
 
index ff58cd5..fca7c77 100644 (file)
@@ -42,12 +42,12 @@ require_once($CFG->dirroot . '/mod/lti/locallib.php');
 class mod_lti_external extends external_api {
 
     /**
-     * Returns description of a tool type
+     * Returns structure be used for returning a tool type from a web service.
      *
      * @return external_function_parameters
      * @since Moodle 3.1
      */
-    private static function get_tool_type_return_parameters() {
+    private static function tool_type_return_structure() {
         return new external_single_structure(
             array(
                 'id' => new external_value(PARAM_INT, 'Tool type id'),
@@ -90,7 +90,7 @@ class mod_lti_external extends external_api {
      * @return external_function_parameters
      * @since Moodle 3.1
      */
-    private static function get_tool_proxy_return_parameters() {
+    private static function tool_proxy_return_structure() {
         return new external_function_parameters(
             array(
                 'id' => new external_value(PARAM_INT, 'Tool proxy id'),
@@ -113,6 +113,59 @@ class mod_lti_external extends external_api {
      * Returns description of method parameters
      *
      * @return external_function_parameters
+     * @since Moodle 3.1
+     */
+    public static function get_tool_proxies_parameters() {
+        return new external_function_parameters(
+            array(
+                'orphanedonly' => new external_value(PARAM_BOOL, 'Orphaned tool types only', VALUE_DEFAULT, 0)
+            )
+        );
+    }
+
+    /**
+     * Returns the tool types.
+     *
+     * @param bool $orphanedonly Retrieve only tool proxies that do not have a corresponding tool type
+     * @return array of tool types
+     * @since Moodle 3.1
+     * @throws moodle_exception
+     */
+    public static function get_tool_proxies($orphanedonly) {
+        global $PAGE;
+        $params = self::validate_parameters(self::get_tool_proxies_parameters(),
+                                            array(
+                                                'orphanedonly' => $orphanedonly
+                                            ));
+        $orphanedonly = $params['orphanedonly'];
+
+        $proxies = array();
+        $context = context_system::instance();
+
+        self::validate_context($context);
+        require_capability('moodle/site:config', $context);
+
+        $proxies = lti_get_tool_proxies($orphanedonly);
+
+        return array_map('serialise_tool_proxy', $proxies);
+    }
+
+    /**
+     * Returns description of method result value.
+     *
+     * @return external_description
+     * @since Moodle 3.1
+     */
+    public static function get_tool_proxies_returns() {
+        return new external_multiple_structure(
+            self::tool_type_return_structure()
+        );
+    }
+
+    /**
+     * Returns description of method parameters.
+     *
+     * @return external_function_parameters
      * @since Moodle 3.0
      */
     public static function get_tool_launch_data_parameters() {
@@ -495,7 +548,7 @@ class mod_lti_external extends external_api {
      * @since Moodle 3.1
      */
     public static function create_tool_proxy_returns() {
-        return self::get_tool_proxy_return_parameters();
+        return self::tool_proxy_return_structure();
     }
 
     /**
@@ -545,7 +598,7 @@ class mod_lti_external extends external_api {
      * @since Moodle 3.1
      */
     public static function delete_tool_proxy_returns() {
-        return self::get_tool_proxy_return_parameters();
+        return self::tool_proxy_return_structure();
     }
 
     /**
@@ -598,6 +651,7 @@ class mod_lti_external extends external_api {
                 'lti_version' => new external_value(PARAM_ALPHANUMEXT, 'LTI version'),
                 'reg_key' => new external_value(PARAM_TEXT, 'Tool proxy registration key'),
                 'reg_password' => new external_value(PARAM_TEXT, 'Tool proxy registration password'),
+                'reg_url' => new external_value(PARAM_TEXT, 'Tool proxy registration url'),
                 'tc_profile_url' => new external_value(PARAM_URL, 'Tool consumers profile URL'),
                 'launch_presentation_return_url' => new external_value(PARAM_URL, 'URL to redirect on registration completion'),
             )
@@ -657,7 +711,7 @@ class mod_lti_external extends external_api {
      */
     public static function get_tool_types_returns() {
         return new external_multiple_structure(
-            self::get_tool_type_return_parameters()
+            self::tool_type_return_structure()
         );
     }
 
@@ -745,7 +799,7 @@ class mod_lti_external extends external_api {
      * @since Moodle 3.1
      */
     public static function create_tool_type_returns() {
-        return self::get_tool_type_return_parameters();
+        return self::tool_type_return_structure();
     }
 
     /**
@@ -828,7 +882,7 @@ class mod_lti_external extends external_api {
      * @since Moodle 3.1
      */
     public static function update_tool_type_returns() {
-        return self::get_tool_type_return_parameters();
+        return self::tool_type_return_structure();
     }
 
     /**
index f49d465..b94178d 100644 (file)
@@ -56,6 +56,7 @@ class renderer extends plugin_renderer_base {
      * @return string html for the page
      */
     public function render_external_registration_return_page($page) {
-        return '';
+        $data = $page->export_for_template($this);
+        return parent::render_from_template('mod_lti/external_registration_return', $data);
     }
 }
index 9099df5..02d24ad 100644 (file)
@@ -54,12 +54,21 @@ $functions = array(
         'services'      => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
     ),
 
+    'mod_lti_get_tool_proxies' => array(
+        'classname'     => 'mod_lti_external',
+        'methodname'    => 'get_tool_proxies',
+        'description'   => 'Get a list of the tool proxies',
+        'type'          => 'read',
+        'capabilities'  => 'moodle/site:config',
+        'ajax'          => true
+    ),
+
     'mod_lti_create_tool_proxy' => array(
         'classname'     => 'mod_lti_external',
         'methodname'    => 'create_tool_proxy',
         'description'   => 'Create a tool proxy',
         'type'          => 'write',
-        'capabilities'  => 'mod/lti:manage',
+        'capabilities'  => 'moodle/site:config',
         'ajax'          => true
     ),
 
@@ -68,7 +77,7 @@ $functions = array(
         'methodname'    => 'delete_tool_proxy',
         'description'   => 'Delete a tool proxy',
         'type'          => 'write',
-        'capabilities'  => 'mod/lti:manage',
+        'capabilities'  => 'moodle/site:config',
         'ajax'          => true
     ),
 
@@ -77,7 +86,7 @@ $functions = array(
         'methodname'    => 'get_tool_proxy_registration_request',
         'description'   => 'Get a registration request for a tool proxy',
         'type'          => 'read',
-        'capabilities'  => 'mod/lti:manage',
+        'capabilities'  => 'moodle/site:config',
         'ajax'          => true
     ),
 
@@ -86,7 +95,7 @@ $functions = array(
         'methodname'    => 'get_tool_types',
         'description'   => 'Get a list of the tool types',
         'type'          => 'read',
-        'capabilities'  => 'mod/lti:manage',
+        'capabilities'  => 'moodle/site:config',
         'ajax'          => true
     ),
 
@@ -95,7 +104,7 @@ $functions = array(
         'methodname'    => 'create_tool_type',
         'description'   => 'Create a tool type',
         'type'          => 'write',
-        'capabilities'  => 'mod/lti:manage',
+        'capabilities'  => 'moodle/site:config',
         'ajax'          => true
     ),
 
@@ -104,7 +113,7 @@ $functions = array(
         'methodname'    => 'update_tool_type',
         'description'   => 'Update a tool type',
         'type'          => 'write',
-        'capabilities'  => 'mod/lti:manage',
+        'capabilities'  => 'moodle/site:config',
         'ajax'          => true
     ),
 
@@ -113,7 +122,7 @@ $functions = array(
         'methodname'    => 'delete_tool_type',
         'description'   => 'Delete a tool type',
         'type'          => 'write',
-        'capabilities'  => 'mod/lti:manage',
+        'capabilities'  => 'moodle/site:config',
         'ajax'          => true
     ),
 
@@ -122,7 +131,7 @@ $functions = array(
         'methodname'    => 'is_cartridge',
         'description'   => 'Determine if the given url is for a cartridge',
         'type'          => 'read',
-        'capabilities'  => 'mod/lti:manage',
+        'capabilities'  => 'moodle/site:config',
         'ajax'          => true
     ),
 );
index 8ccb450..b6c8fc1 100644 (file)
@@ -42,7 +42,13 @@ $pageurl = new moodle_url('/mod/lti/externalregistrationreturn.php');
 $PAGE->set_context($systemcontext);
 $PAGE->set_url($pageurl);
 $PAGE->set_pagelayout('maintenance');
-echo $OUTPUT->header();
+$output = $PAGE->get_renderer('mod_lti');
+echo $output->header();
+
 $params = array('message' => s($msg), 'error' => s($err), 'id' => $id, 'status' => s($status));
+
+$page = new \mod_lti\output\external_registration_return_page();
+echo $output->render($page);
+
 $PAGE->requires->js_call_amd('mod_lti/external_registration_return', 'init', $params);
-echo $OUTPUT->footer();
+echo $output->footer();
index f4fc716..bd699ae 100644 (file)
@@ -162,6 +162,7 @@ $string['embed_no_blocks'] = 'Embed, without blocks';
 $string['enableemailnotification'] = 'Send notification emails';
 $string['enableemailnotification_help'] = 'If enabled, students will receive email notification when their tool submissions are graded.';
 $string['enterkeyandsecret'] = 'Enter your consumer key and shared secret';
+$string['errorbadurl'] = 'URL is not a valid tool URL or cartridge.';
 $string['errormisconfig'] = 'Misconfigured tool. Please ask your Moodle administrator to fix the configuration of the tool.';
 $string['existing_window'] = 'Existing window';
 $string['extensions'] = 'LTI extension services';
index 35eea41..0b8ed0c 100644 (file)
@@ -302,6 +302,7 @@ function lti_build_registration_request($toolproxy) {
     $requestparams['lti_version'] = 'LTI-2p0';
     $requestparams['reg_key'] = $key;
     $requestparams['reg_password'] = $secret;
+    $requestparams['reg_url'] = $toolproxy->regurl;
 
     // Add the profile URL.
     $profileservice = lti_get_service_by_name('profile');
@@ -1623,6 +1624,30 @@ function lti_get_tool_proxy($id) {
     return $toolproxy;
 }
 
+/**
+ * Returns lti tool proxies.
+ *
+ * @param bool $orphanedonly Only retrieves tool proxies that have no type associated with them
+ * @return array of basicLTI types
+ */
+function lti_get_tool_proxies($orphanedonly) {
+    global $DB;
+
+    if ($orphanedonly) {
+        $tools = $DB->get_records('lti_types');
+        $usedproxyids = array_values($DB->get_fieldset_select('lti_types', 'toolproxyid', 'toolproxyid IS NOT NULL'));
+        $proxies = $DB->get_records('lti_tool_proxies', null, 'state DESC, timemodified DESC');
+        foreach ($proxies as $key => $value) {
+            if (in_array($value->id, $usedproxyids)) {
+                unset($proxies[$key]);
+            }
+        }
+        return $proxies;
+    } else {
+        return $DB->get_records('lti_tool_proxies', null, 'state DESC, timemodified DESC');
+    }
+}
+
 /**
  * Generates some of the tool proxy configuration based on the admin configuration details
  *
@@ -2273,6 +2298,19 @@ function get_tool_type_edit_url(stdClass $type) {
     return $url->out();
 }
 
+/**
+ * Returns the edit url for the given tool proxy.
+ *
+ * @param stdClass $proxy The tool proxy
+ *
+ * @return string The url to edit the tool type
+ */
+function get_tool_proxy_edit_url(stdClass $proxy) {
+    $url = new moodle_url('/mod/lti/registersettings.php',
+                          array('action' => 'update', 'id' => $proxy->id, 'sesskey' => sesskey(), 'returnto' => 'toolconfigure'));
+    return $url->out();
+}
+
 /**
  * Returns the course url for the given tool type
  *
@@ -2294,7 +2332,7 @@ function get_tool_type_course_url(stdClass $type) {
  *
  * @param stdClass $type The tool type
  *
- * @return string The url to the course of the tool type
+ * @return string The urls of the tool type
  */
 function get_tool_type_urls(stdClass $type) {
     $courseurl = get_tool_type_course_url($type);
@@ -2311,6 +2349,24 @@ function get_tool_type_urls(stdClass $type) {
     return $urls;
 }
 
+/**
+ * Returns the icon and edit urls for the tool proxy.
+ *
+ * @param stdClass $proxy The tool proxy
+ *
+ * @return string The urls of the tool proxy
+ */
+function get_tool_proxy_urls(stdClass $proxy) {
+    global $OUTPUT;
+
+    $urls = array(
+        'icon' => $OUTPUT->pix_url('icon', 'lti')->out(),
+        'edit' => get_tool_proxy_edit_url($proxy),
+    );
+
+    return $urls;
+}
+
 /**
  * Returns information on the current state of the tool type
  *
@@ -2432,6 +2488,34 @@ function serialise_tool_type(stdClass $type) {
     );
 }
 
+/**
+ * Serialises this tool proxy.
+ *
+ * @param stdClass $proxy The tool proxy
+ *
+ * @return array An array of values representing this type
+ */
+function serialise_tool_proxy(stdClass $proxy) {
+    return array(
+        'id' => $proxy->id,
+        'name' => $proxy->name,
+        'description' => $proxy->regurl . ' You will need to activate this proxy before adding a description',
+        'urls' => get_tool_proxy_urls($proxy),
+        'state' => array(
+            'text' => get_string('pending', 'mod_lti'),
+            'pending' => true,
+            'configured' => false,
+            'rejected' => false,
+            'unknown' => false
+        ),
+        'hascapabilitygroups' => true,
+        'capabilitygroups' => array(),
+        'courseid' => 0,
+        'instanceids' => array(),
+        'instancecount' => 0
+    );
+}
+
 /**
  * Loads the cartridge information into the tool type, if the launch url is for a cartridge file
  *
index e15d26a..66a23e0 100644 (file)
     min-height: 800px;
 }
 
-#external-registration-container .registration-loading-container {
+.loading-screen {
     text-align: center;
     padding: 3em;
 }
 
-#external-registration-container .registration-loading-container .loading-text, #external-registration-container .registration-loading-container .loader {
+.loading-screen .loading-text {
     font-size: 2em;
 }
 
-#external-registration-container .registration-loading-container .loader {
+.loading-screen .loader {
     margin-left: auto;
     margin-right: auto;
     margin-bottom: 1em;
     height: 2em;
     width: 2em;
+    font-size: 2em;
 }
 
 #registration-submit {
@@ -70,7 +71,7 @@
 }
 
 #registration-form-container {
-    min-height: 230px;
+    min-height: 260px;
 }
 
 #registration-form-container .well {
     margin-top: 20px;
 }
 
-#registration-feedback-container {
-    cursor: pointer;
-}
-
-#registration-feedback-container h4 {
-    text-transform: capitalize;
-}
-
 #tool-type-capabilities-container .registration-loading-container {
     display: none;
 }
 
 .tool-card-body .description {
     max-height: 100px;
+    word-wrap: break-word;
 }
 
 .tool-card-footer {
index ba7268d..6be8746 100644 (file)
@@ -41,9 +41,9 @@
 </div>
 
 <div id="tool-type-capabilities-container" class="hidden">
-    <div class="registration-loading-container">
+    <div class="registration-loading-container loading-screen">
         {{> mod_lti/loader }}
-        <p>{{#str}} loading, admin {{/str}}</p>
+        <p class="loading-text">{{#str}} loadinghelp, moodle {{/str}}</p>
     </div>
     <div id="tool-type-capabilities-template-container" aria-live="polite"></div>
 </div>
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
-    @template mod_lti/registration_feedback
+    @template mod_lti/external_registration_return
 
-    This template provides the layout for a feedback message in the
-    tool configuration page.
+    This template provides the layout for the external tool configuration page in the
+    LTI module.
 
     Classes required for JS:
-    * close
-    * alert
-    * alert-success
-    * alert-error
+    * none
 
     Data attributes required for JS:
     * none
 
     Context variables required for this template:
-    * status
-    * message
-
-    Example context (json):
-    {
-        "status": "Success",
-        "message": "Successfully created a new tool type!"
-    }
+    *
 
 }}
-<div class="alert
-    {{^error}}alert-success{{/error}}
-    {{#error}}alert-error{{/error}}" role="alert">
-    <button type="button" class="close" aria-label="{{#str}} closebuttontitle, moodle {{/str}}">&times;</button>
-    <h4>{{status}}</h4>
-    {{message}}
+<div id="external-registration-return-container">
+    <div class="registration-loading-container loading-screen">
+        {{> mod_lti/loader }}
+        <p class="loading-text">{{#str}} loadinghelp, moodle {{/str}}</p>
+    </div>
 </div>
+{{#js}}
+    require(['mod_lti/external_registration'], function(registration) {
+        registration.init();
+    });
+{{/js}}
index 6447e66..53c14b5 100644 (file)
@@ -34,6 +34,7 @@
 <div class="container-fluid">
     <div id="main-content-container">
         <div id="registration-form-container" aria-live="polite">
+            <div id="registration-feedback-container" aria-live="polite"></div>
             <div id="registration-choice-container" class="centered-menu">
                 <div class="well">
                     <p class="lead">{{#str}} autoaddtype, mod_lti {{/str}}</p>
@@ -57,7 +58,6 @@
                     <p><a href="{{{ managetoolsurl }}}">{{#str}} manage_tools, mod_lti {{/str}}</a><br/><a href="{{{ managetoolproxiesurl}}}">{{#str}} manage_tool_proxies, mod_lti {{/str}}</a></p>
                 </div>
             </div>
-            <div id="registration-feedback-container" aria-live="polite"></div>
             <div id="cartridge-registration-container" aria-live="polite" class="hidden">
                 {{> mod_lti/cartridge_registration_form }}
             </div>
index 7da4749..70f99a7 100644 (file)
@@ -41,6 +41,9 @@
 <div id="tool-notools-text" class="container-fluid">{{#str}} no_lti_tools, mod_lti {{/str}}</div>
 {{/tools}}
 <div id="tool-card-container" class="container-fluid">
+    {{#proxies}}
+        {{> mod_lti/tool_proxy_card }}
+    {{/proxies}}
     {{#tools}}
         {{> mod_lti/tool_card }}
     {{/tools}}
diff --git a/mod/lti/templates/tool_proxy_card.mustache b/mod/lti/templates/tool_proxy_card.mustache
new file mode 100644 (file)
index 0000000..7c15070
--- /dev/null
@@ -0,0 +1,104 @@
+{{!
+    This file is part of Moodle - http://moodle.org/
+
+    Moodle is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Moodle is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+    @template mod_lti/tool_proxy_card
+
+    This template provides the layout for a single tool card on the tool
+    configuration page.
+
+    Classes required for JS:
+    * none
+
+    Data attributes required for JS:
+    * none
+
+    Context variables required for this template:
+    * hascapabilitygroups
+    * state
+    ** pending|configured|rejected|unknown
+    ** text
+    * courseid
+    * urls
+    ** course
+    ** icon
+    * name
+    * description
+    * instancecount
+
+}}
+<div class="tool-card" data-proxy-id="{{id}}">
+    <div class="overlay-container">
+        <div class="img-container">
+            {{> mod_lti/loader }}
+            <div class="success-icon-container">{{#pix}} i/valid, core, {{#str}} success {{/str}}{{/pix}}</div>
+            <div class="fail-icon-container">{{#pix}} i/invalid, core, {{#str}} error {{/str}}{{/pix}}</div>
+        </div>
+        {{#hascapabilitygroups}}
+            {{> mod_lti/tool_type_capabilities_agree }}
+        {{/hascapabilitygroups}}
+    </div>
+    <div class="tool-card-content">
+        <div class="tool-card-header">
+            <div class="tool-card-subheader">
+                <div class="label
+                            {{#state.pending}}label-info{{/state.pending}}
+                            {{#state.configured}}label-success{{/state.configured}}
+                            {{#state.rejected}}label-important{{/state.rejected}}
+                            {{#state.unknown}}label-warning{{/state.unknown}}">
+                    {{state.text}}
+                </div>
+                <div class="tool-card-actions pull-right">
+                    {{#courseid}}
+                        <a href="{{{urls.course}}}" title="{{#str}} courselink, mod_lti {{/str}}">{{#pix}} i/course, core, {{#str}} courselink, mod_lti {{/str}}{{/pix}}</a>
+                    {{/courseid}}
+                    <a class="edit" href="{{{urls.edit}}}" title="{{#str}} edit {{/str}}">{{#pix}} t/edit, core, {{#str}} edit {{/str}}{{/pix}}</a>
+                    <a class="delete" href="#" title="{{#str}} delete {{/str}}">{{#pix}} t/delete, core, {{#str}} delete {{/str}}{{/pix}}</a>
+                </div>
+            </div>
+            <img class="tool-card-icon" src="{{{urls.icon}}}" alt="{{name}}"></img>
+            <div class="contenteditable-container">
+                <h4 class="name">{{name}}</h4>
+                <div class="overlay-container">{{> mod_lti/loader }}</div>
+            </div>
+        </div>
+        <div class="tool-card-body">
+            <div class="contenteditable-container">
+                <p class="description">{{description}}</p>
+                <div class="overlay-container">{{> mod_lti/loader }}</div>
+            </div>
+        </div>
+        <div class="tool-card-footer">
+            {{#state.pending}}
+                <a class="activate btn btn-success" href="#">{{#str}} activate, mod_lti {{/str}}</a>
+            {{/state.pending}}
+            {{^state.pending}}
+                {{#instancecount}}
+                    <p>{{#str}} toolisbeingused, mod_lti, {{instancecount}} {{/str}}</p>
+                {{/instancecount}}
+                {{^instancecount}}
+                    <p>{{#str}} toolisnotbeingused, mod_lti{{/str}}</p>
+                {{/instancecount}}
+            {{/state.pending}}
+        </div>
+    </div>
+</div>
+{{#js}}
+    require(['jquery', 'mod_lti/tool_proxy_card_controller'], function($, controller) {
+        var cardElement = $('[data-proxy-id={{id}}]');
+        controller.init(cardElement);
+    });
+{{/js}}
index 2eaa440..5a189e4 100644 (file)
@@ -31,7 +31,7 @@
 
 }}
 <div id="tool-proxy-registration-form-container">
-    <div class="registration-loading-container">
+    <div class="registration-loading-container loading-screen">
         {{> mod_lti/loader }}
         <p class="loading-text">{{#str}} loadinghelp, moodle {{/str}}</p>
         <p id="registration-loading-failed" class="hidden">{{#str}} register_warning, mod_lti {{/str}}</p>
index 39a039d..693405f 100644 (file)
@@ -34,7 +34,7 @@ require_once($CFG->dirroot.'/mod/lti/locallib.php');
 $action       = optional_param('action', '', PARAM_ALPHANUMEXT);
 $id           = optional_param('id', '', PARAM_INT);
 $tab          = optional_param('tab', '', PARAM_ALPHAEXT);
-$returnto     = optional_param('returnto', '', PARAM_ALPHANUM);
+$returnto     = optional_param('returnto', '', PARAM_ALPHA);
 
 if ($returnto == 'toolconfigure') {
     $returnurl = new moodle_url($CFG->wwwroot . '/mod/lti/toolconfigure.php');
index 03941fc..d3143b5 100644 (file)
@@ -56,7 +56,7 @@ require_once($CFG->dirroot.'/mod/lti/locallib.php');
 $action       = optional_param('action', null, PARAM_ALPHANUMEXT);
 $id           = optional_param('id', null, PARAM_INT);
 $tab          = optional_param('tab', '', PARAM_ALPHAEXT);
-$returnto     = optional_param('returnto', '', PARAM_ALPHANUM);
+$returnto     = optional_param('returnto', '', PARAM_ALPHA);
 
 if ($returnto == 'toolconfigure') {
     $returnurl = new moodle_url($CFG->wwwroot . '/mod/lti/toolconfigure.php');
index 0e98e78..0ada6ac 100644 (file)
@@ -48,7 +48,7 @@
 
 defined('MOODLE_INTERNAL') || die;
 
-$plugin->version   = 2016041800;    // The current module version (Date: YYYYMMDDXX).
+$plugin->version   = 2016041801;    // The current module version (Date: YYYYMMDDXX).
 $plugin->requires  = 2015111000;    // Requires this Moodle version.
 $plugin->component = 'mod_lti';     // Full name of the plugin (used for diagnostics).
 $plugin->cron      = 0;