Development Guide

Test-Signature Plugins

This feature is available for ILIAS 4.4.x+.

Introduction

Test-Signature Plugins allow students to sign their test submissions using a variety of mechanisms. The plugin is invoked at the end of a test and redirects control to the first active plugin on the slot.
The control flow - even across requests - is handed over to the plugin which does its things until it hands back the control to the test. This concept gives the plugins decent degrees of freedom in what "its things" are.
To describe the concept on a code level, the example presented here is a "FooSig"-plugin, which does not have real things it does. The plugin simply displays a form and reports back the uploaded contents. This reporting back of contents works in conjunction with the PDF-Archive-Service, which was also introduced with ILIAS 4.4. However, it is possible to implement plugins that take care of storing the signed files in a completely different way, e.g. on another server or even a third party service. These possibilities and the potential of the plugin slot will hopefully shine through a bit. If they don't, please feel free to contact the T&A-maintainers for assistance.

Directory Structure

The directory structure of the plugin is pretty much what you'd expect it to be:

Customizing / global / plugins / Modules / Test / Signature / 

FooSig/
classes/
class.ilFooSigPlugin.php
lang/
ilias_de.lang
ilias_en.lang
resources/
...
sql/
dbupdate.php
templates/
default/
tpl.foosig_main.html
plugin.php

Most of these things are optional. It may be, that you don't need language, dbupdate and templates. Pretty much the only real requirements are the plugin class and the plugin.php in the above sketched locations.

plugin.php
 
<?php
$id = 'tfoosig';
$version = '0.0.1';
$ilias_min_version = '4.4.0';
$ilias_max_version = '4.4.999';
$responsible = 'Maximilian Becker';
$responsible_mail = 'mbecker@databay.de';

The plugin.php is also pretty much what you expect it to be.

Plugin Class

The plugin class however is where all the new things happen, so it deserves its own section.
The plugin class derives from ilTestSignaturePlugin, which is located in Modules/Test/classes/class.ilTestSignaturePlugin.php
The parent classes impose the following two requirements on you:

/**
* Get Plugin Name. Must be same as in class name il<Name>Plugin
* and must correspond to plugins subdirectory name.
* Must be overwritten in plugin class of plugin
* (and should be made final)
* @return string Plugin Name
*/

public function getPluginName();
 
/**
* Passes the control to the plugin.
*
* The ilTestSignatureGUI, which receives the control-flow from ILIAS does not implement own commands.
* These are passed into this method, the plugin needs to make sense out of these.
*
* @param string|null $cmd Optional command for the plugin
*
* @return void
*/

public function invoke($cmd = null);

To get your plugin to work, you need to implement these. To support your development, the following methods were designed for you:

getLinkTargetForRessource($cmd, $ressource);
This method returns a link which points to a given file in the ressources-folder in your plugin. This request goes through ILIAS, so the location of the file is not directly disclosed to the users and regular security measures are in place. Please note that Java Applets or other request making mechanisms need to deal with getting proper credentials from the users browser if they run from a different scope. An alternative is to address the files directly.
 
getFormAction($default_cmd);
getLinkTargetForCmd($cmd);
As commands are dealt with in the invoke-method of your plugin class, these need to arrive there. Use this method with the command as parameter, to get addresses.
 
handInFileForArchiving($active_fi, $pass, $filename, $filepath);
If archiving is activated, files sent through this method will be stored.
 
redirectToTest($success);
At the end of the signing process, this method redirects participants back to the test.

Let's see how this all works together in the sample class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
 
require_once 'Modules/Test/classes/class.ilTestSignaturePlugin.php';
 
/**
* @author Maximilian Becker <mbecker@databay.de>
* @version $Id$
* @ingroup ModulesTest
*/

class ilFooSigPlugin extends ilTestSignaturePlugin
{
/**
* Get Plugin Name. Must be same as in class name il<Name>Plugin
* and must correspond to plugins subdirectory name.
* Must be overwritten in plugin class of plugin
* (and should be made final)
* @return string Plugin Name
*/

public function getPluginName()
{
return 'FooSig';
}

Require the parent class,  derive from it and implement getPluginName().

1
2
3
4
5
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
/**
* Passes the control to the plugin.
*
* The ilTestSignatureGUI, which receives the control-flow from ILIAS does not implement own commands.
* These are passed into this method, the plugin needs to make sense out of these.
*
* @param string|null $cmd Optional command for the plugin
*
* @return void
*/

public function invoke($cmd = null)
{
switch ($cmd)
{
case 'download': // Download a file from the ressources folder.
$this->handleDownloadRequest();
exit();
break;
 
case 'deliverSignatureSubject': // Download the file to be signed.
$this->handleSignatureSubjectDeliveryRequest();
exit();
break;
 
case 'upload_subject': // Upload the signed file.
$this->handleSignedSubjectSubmission();
exit();
 
case 'process_success': // Callback for a signature mechanism reporting success.
$this->handleProcessSuccessfulRequest();
exit();
 
case 'process_error': // Callback for a signature mechanism reporting an error.
$this->handleProcessErrorRequested();
exit();
break;
 
case 'invokeSignaturePlugin': // Default action, renders the plugins page.
default:
$this->renderPlugin();
}
}

The invoke method dispatches to the action handling methods. The mechanism has two ways of communicating with ILIAS. For simple processes, the method handling the "upload_subject" command can evaluate the success of the signature process, e.g. by inspecting the signature. It may in case of a successful signing directly redirect to the test.
Since signature mechanisms are not required to deliver a file back to ILIAS, another way to signal a success could be through a command "process_success". There are no limits or defined names here. Keep in mind, however, that you must have the commands here in sync with the command parameter at the helper methods. It could be beneficial to handle the command-strings for the commands in constants.
Also, keep in mind, that all requests to commands require an authenticated session. If they don't have one - like in the java case - they will be redirected to the login screen. Detect such cases with your webservers access log file.
 

1
2
3
4
5
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
/**
* Renders the plugin to the screen.
*/

protected function renderPlugin()
{
$template = new ilTemplate('Customizing/global/plugins/Modules/Test/Signature/FooSig/templates/default/tpl.il_foosig_main.html', true, true, false, array(), true);
$template->setVariable('SIGNATURE_DOCUMENT_URL', $this->getLinkTargetForCmd('deliverSignatureSubject'));
$template->setVariable('SIGNATURE_POSTBACK_URL', $this->getLinkTargetForCmd('upload_subject'));
$template->setVariable('SIGNATURE_SUCCESS_URL', $this->getLinkTargetForCmd('process_success'));
$template->setVariable('SIGNATURE_ERROR_URL', $this->getLinkTargetForCmd('process_error'));
$template->parseCurrentBlock();
 
$this->populatePluginCanvas( $template->get() );
$this->showPluginCanvas();
}
 
/**
* Handles a POST-request, that ships signed data (et. al.) back to ILIAS.
*/

protected function handleSignedSubjectSubmission()
{
global $ilUser; /** @var $ilUser ilObjUser */
$active = $this->getGUIObject()->getTest()->getActiveIdOfUser($ilUser->getId());
$pass = $this->getGUIObject()->getTest()->_getMaxPass($active);
$this->handInFileForArchiving($active, $pass, 'submission.pdf.asc', $_FILES['somename']['tmp_name']);
$this->handInFileForArchiving($active, $pass, 'submission.pdf', $_FILES['somename2']['tmp_name']);
}
 
/**
* Handles a GET-request which should deliver a file to be signed to the applet.
*/

protected function handleSignatureSubjectDeliveryRequest()
{
// Here we build the result that is going to be signed:
global $ilUser; /** @var $ilUser ilObjUser */
$active = $this->getGUIObject()->getTest()->getActiveIdOfUser($ilUser->getId());
require_once './Modules/Test/classes/class.ilTestEvaluationGUI.php';
$testevaluationgui = new ilTestEvaluationGUI($this->getGUIObject()->getTest());
$pass = $this->getGUIObject()->getTest()->_getMaxPass($active);
$results = $this->getGUIObject()->getTest()->getTestResult($active, $pass, false);
$results_output = $testevaluationgui->getPassListOfAnswers($results, $active, $pass, false, false, false, false);
 
// Here we transform the result to the desired format. Please note that the constant in the generatePDF signature
// instructs the output. No tinkering with temp files and the like.
$filename = 'test_submission';
require_once './Modules/Test/classes/class.ilTestPDFGenerator.php';
ilTestPDFGenerator::generatePDF($results_output, ilTestPDFGenerator::PDF_OUTPUT_INLINE, $filename.'.pdf');
}

renderPlugin is quite straightforward. It fills your template file with variables and delivers the html to the user with a call to $this->showPluginCanvas();
 
handleSignedSubjecSubmission() receives a request that holds the signed file or files. The method shows how to get $active-id and $pass for the handInFileForArchiving method.
 
handleSignatureSubjectDeliveryRequest() creates and delivers the file that is to be signed. In the example, it's a PDF file, it may however be a totally different file. Such a method could also create a file that can be downloaded by a third party website via a direct download link.
 

Please see the attached example for further details.

FooSig Plugin for ILIAS 4.4