BasicLTI for QuestionMark Perception

Using Sakai to connect to load-balanced QuestionMark Perception using BasicLTI.


  1. Provide single sign-on capability to avoid maintaining a separate password for each user;
  2. Sakia (at Oxford) allows ‘external’ users to be created  from their email addresses. Where these users are given permission with Sakai and where they have accounts in Perception, they would then be able to access assessments.


By adapting the QuestiionMark BasicLTI connector. This is a community edition project which the very helpful Steve Lay pointed us at:


  1. Create DB – as we have a load-balanced setup and a clustered MSSQL DB, we had to create another db in our MSSQL server to avoid introducing a single point of failure. Create the tables using the following (adapted from
    CREATE TABLE lti_consumer (
     consumer_key varchar(255) NOT NULL,
     name varchar(45) NOT NULL,
     secret varchar(32) NOT NULL,
     lti_version varchar(12) DEFAULT NULL,
     consumer_name varchar(255) DEFAULT NULL,
     consumer_version varchar(255) DEFAULT NULL,
     consumer_guid varchar(255) DEFAULT NULL,
     css_path varchar(255) DEFAULT NULL,
     protected tinyint NOT NULL,
     enabled tinyint NOT NULL,
     enable_from datetime DEFAULT NULL,
     enable_until datetime DEFAULT NULL,
     last_access date DEFAULT NULL,
     created datetime NOT NULL,
     updated datetime NOT NULL,
     PRIMARY KEY (consumer_key)
    CREATE TABLE lti_context (
     consumer_key varchar(255) NOT NULL,
     context_id varchar(255) NOT NULL,
     lti_context_id varchar(255) DEFAULT NULL,
     lti_resource_id varchar(255) DEFAULT NULL,
     title varchar(255) NOT NULL,
     settings text,
     primary_consumer_key varchar(255) DEFAULT NULL,
     primary_context_id varchar(255) DEFAULT NULL,
     share_approved tinyint DEFAULT NULL,
     created datetime NOT NULL,
     updated datetime NOT NULL,
     PRIMARY KEY (consumer_key, context_id)
    CREATE TABLE lti_user (
     consumer_key varchar(255) NOT NULL,
     context_id varchar(255) NOT NULL,
     user_id varchar(255) NOT NULL,
     lti_result_sourcedid varchar(255) NOT NULL,
     created datetime NOT NULL,
     updated datetime NOT NULL,
     PRIMARY KEY (consumer_key, context_id, user_id)
    CREATE TABLE lti_nonce (
     consumer_key varchar(255) NOT NULL,
     value varchar(32) NOT NULL,
     expires datetime NOT NULL,
     PRIMARY KEY (consumer_key, value)
    CREATE TABLE lti_share_key (
     share_key_id varchar(32) NOT NULL,
     primary_consumer_key varchar(255) NOT NULL,
     primary_context_id varchar(255) NOT NULL,
     auto_approve tinyint NOT NULL,
     expires datetime NOT NULL,
     PRIMARY KEY (share_key_id)
    ALTER TABLE lti_context
     ADD CONSTRAINT lti_context_consumer_FK1 FOREIGN KEY (consumer_key)
     REFERENCES lti_consumer (consumer_key);
    ALTER TABLE lti_context
     ADD CONSTRAINT lti_context_context_FK1 FOREIGN KEY (primary_consumer_key, primary_context_id)
     REFERENCES lti_context (consumer_key, context_id);
    ALTER TABLE lti_user
     ADD CONSTRAINT lti_user_context_FK1 FOREIGN KEY (consumer_key, context_id)
     REFERENCES lti_context (consumer_key, context_id);
    ALTER TABLE lti_nonce
     ADD CONSTRAINT lti_nonce_consumer_FK1 FOREIGN KEY (consumer_key)
     REFERENCES lti_consumer (consumer_key);
    ALTER TABLE lti_share_key
     ADD CONSTRAINT lti_share_key_context_FK1 FOREIGN KEY (primary_consumer_key, primary_context_id)
     REFERENCES lti_context (consumer_key, context_id);
  2. Create a user on the DB whose details you will enter below.
  3. Create a new administrator in Enterprise Manager which will be used by QMWISE
  4. Create an MD5 checksum for the QMWISE user as described here:
  5. Change settings in config.php:
    define('CONSUMER_KEY', 'testkey');define('CONSUMER_SECRET', 'testsecret');
    define('DB_NAME', 'sqlsrv:Server=localhost;Database=testdb');
    define('DB_USERNAME', 'dbusername');
    define('DB_PASSWORD', 'dbpassword');
    define('QMWISE_URL', 'http://localhost/QMWISe5/QMWISe.asmx');
    define('SECURITY_CLIENT_ID', 'ClientIDFromQMWISETestHarness');
    define('SECURITY_CHECKSUM', 'ChceksumFromQMWISETestHarness');
    define('DEBUG_MODE', true);
    define('QM_USERNAME_PREFIX', '');
    define('WEB_PATH', '/qmlti');

    DB_NAME should adhere to DSN conventions here:

    SECURITY_CLIENT_ID is the administrator which is used by QMWISE

    SECURITY_CHECKSUM is the chceksu which can be generated from the QMWISE test harness

    WEB_PATH:  we needed to change to subdirectory where we had placed lti connector code

  6. As detailed here ( you cannot write applications yourself that provide a trust header so switch this off under Administration | Server Management | Server Settings in Enterprise Manager. Don’t forget to reset IIS (Start | All Programs | Accessories right-click Command Prompt and Run as Administrator then type

    ) to ensure it picks up the change in settings.

  7. Upload qmlti folder into C:\inetpub\wwwroot on both of the load-balanced servers
  8. Copy the lti.pip file into the \repository\PerceptionRepository\pip folder on the storage area shared by both load-balanced servers
  9. At this stage, I wanted to be able to launch an assessment as an IMS ‘Learner’/QM Participant from the test_harness. I couldn’t work out how to do this so made the following changes (these will probably be superseded once I have worked out how to set up the tool as an IMS ‘Instructor’ so that the correct assessment is shown based upon the User/Context_ID):
    • added a custom parameters textarea to the test_harness.php (188):
      <div class="row">
      <div class="col1">
      Custom Parameters (each on separate line; param_name=param_value)
      <div class="col2">
      <textarea name="custom" rows="5" cols="50" onchange="onChange();" ><?php echo htmlentities($_SESSION['custom']); ?></textarea>

      ..and on line 45, something to save them:

    • Something to read them and turn them into request parameters in test_launch.php (48):
      $custom_params = explode('\n', str_replace('\n\r','\n',$_SESSION['custom']));
      foreach($custom_params as $custom_param){
      if(strpos($custom_param, '=')){
      $custom_param_parts = explode('=', $custom_param);
    • And changed the parameter read in index.php (65) to look for the assessment ID with ‘custom_’ prepended as this seems to be what the specification expects:
      $assessment_id = $tool_provider->context->getSetting(custom_ASSESSMENT_SETTING);
  10. Don’t forget that you’ll need to ‘Allow run from integration’ under ‘Control settings’ for any assessment that you want to launch with QMWISE.
  11. Now pouint your browser to http://yourserver/qmlti/test_harness.php. It should pick up your key and secret automatically from config.php so, to launch an assessment as a student, all you need to do is:
    • enter the username of a valid participant in the User Details | ID box;
    • Enter the ID of an assessment for which that student is scheduled in the new custom parameters box as follows:
    • Click ‘Save data’ your data
    • Then click Launch

It’s worth noting that doing things like this does breaks the strict separation between the various tiers as the PHP application is running on the QPLA servers and accessing the database directly.

Finally, a huge thanks to QuestionMark’s Steve Lay and to Stephen Vickers for his Basic LTI code.


2 Replies to “BasicLTI for QuestionMark Perception”

  1. Hi, a nice blog post.

    I do not believe that the BasicLTI connector takes care of limiting access to a test – say – once for summativer purposes. So, if we would like the BasicLTI connector to work in combination with Blackboard, restrictions in availability need to be found in the Blackboard system. Am I right?

    Would you be able to tell us whether the use of the Blackboard Adaptive Release function can be used such that a student is only allowed – say – one attempt on a QM test via de BasicLTI connection?

    We would be very obliged. Kind regards,

    Silvester Draaijer
    VU University Amsterdam

  2. Hi Silvester

    Thanks for getting in touch. You’re right – the version of the connector that I was using (I haven’t had time to look at the latest version yet) relies on the VLE (tool consumer) to limit access. I’m afraid I have no knowledge of Blackboard or its Adaptive Release function so don’t know whether it would do as you ask. However, if Blackboard couldn’t do it, there is certainly an opportunity to pass custom parameters through LTI which could be used, in combination perhaps with an entry in the db for each user, to limit number of attempts?

    Because we have concerns over giving too much power to the users of the VLE (over which we have only limited control), particularly with summative exams, what we have ended up doing is simply using Basic LTI to authenticate students from our VLE (Sakai) – we then set up schedules and accounts in Perception as normal and so would apply the ‘one attempt only’ rule in the schedule as normal.

    I’ll try and post our final solution here when I get a moment.

    Best wishes


Leave a Reply

Your email address will not be published. Required fields are marked *