Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Updating to code.lamsfoundation.org

...

No Format
String customCSV = WebUtil.readStrParam(request, "customCSV", true);

String splitCSV[] = customCSV.split(",");
if (splitCSV.length != 3)
{
	logger.error("mdlForum tool customCSV not in required (user,course,courseURL) form: " + customCSV);
	throw new ToolException("mdlForum tool cusomCSV not in required (user,course,courseURL) form: " + customCSV);
}
else
{
	userFromCSV = splitCSV[0];
	courseFromCSV = splitCSV[1];
	sectionFromCSV = splitCSV[2];
}

...

String responseUrl = mdlForumService.getConfigItem(MdlForumConfigItem.KEY_EXTERNAL_SERVER_URL).getConfigValue();
responseUrl += RELATIVE_MOODLE_AUTHOR_URL;

String returnUpdateUrl = URLEncoder.encode(
	TOOL_APP_URL + "/authoring.do?dispatch=updateContent" + "&"
	+AttributeNames.PARAM_TOOL_CONTENT_ID + "=" + toolContentID.toString(), "UTF8"
	);

responseUrl +=  "&lamsUpdateURL=" + returnUpdateUrl;

if (mdlForum.getExtSection() != null){
	responseUrl +=  "&section=" + mdlForum.getExtSection();
}
else{
	responseUrl +=  "&section=" + sectionFromCSV;
}

if (mdlForum.getExtToolContentId()!=null){
	responseUrl += "&update=" + mdlForum.getExtToolContentId().toString();
}
else{
	responseUrl += "&add=forum";
}

if (mdlForum.getExtCourseId() != null){
	responseUrl +="&course=" + mdlForum.getExtCourseId()
}
else{
	responseUrl +="&course=" + courseFromCSV;
}

log.debug("Sending to moodle forum edit page: " + responseUrl);
response.sendRedirect(responseUrl);

See complete AuthoringAction.java file.

Cloning Tool Content

For LAMS tools, there are several events that will trigger a request to clone the tool's content including starting a lesson, starting preview, copy-pasting an activity in author, and inserting a sequence in author. This is one of the most important extension points for a tool adapter tool as it takes care of a lot of the workflow and group implementation that LAMS is renowned for.

...

No Format
 /* ************ Methods from ToolSessionManager ************* */
public void createToolSession(Long toolSessionId, String toolSessionName,
		Long toolContentId) throws ToolException {
	if (logger.isDebugEnabled()) {
		logger.debug("entering method createToolSession:"
				+ " toolSessionId = " + toolSessionId
				+ " toolSessionName = " + toolSessionName
				+ " toolContentId = " + toolContentId);
	}

	MdlForumSession session = new MdlForumSession();
	session.setSessionId(toolSessionId);
	session.setSessionName(toolSessionName);


	// learner starts
	MdlForum mdlForum = mdlForumDAO.getByContentId(toolContentId);
	session.setMdlForum(mdlForum);

	try
	{
		// Get the required params, then call the eternal server
		HashMap<String, String> params = getRequiredExtServletParams(mdlForum);
		params.put(EXT_SERVER_PARAM_EXT_TOOL_CONTENT_ID, mdlForum.getExtToolContentId().toString());
		session.setExtSessionId(copyExternalToolContent(params));
	}
	catch(Exception e)
	{
		logger.error("Failed to call external server to copy tool content" + e);
		throw new ToolException("Failed to call external server to copy tool content" + e);
	}

	mdlForumSessionDAO.saveOrUpdate(session);
}


/**
 * Calls the external server to copy the content and return a new id
 * @param extToolContentId
 * @return
 */
public Long copyExternalToolContent(HashMap<String, String> params)
throws ToolException, Exception
{

 	String cloneServletUrl = mdlForumConfigItemDAO.getConfigItemByKey(MdlForumConfigItem.KEY_EXTERNAL_TOOL_SERVLET).getConfigValue();

 	// add the method to the params
 	params.put(EXT_SERVER_PARAM_METHOD, EXT_SERVER_METHOD_CLONE);

	// Make the request
 	InputStream is = WebUtility.getResponseInputStreamFromExternalServer(cloneServletUrl, params);
	BufferedReader isReader = new BufferedReader(new InputStreamReader(is));
	String str = isReader.readLine();
	if (str == null) {
	    throw new UserInfoFetchException("Fail to clone forum in .LRN:"
	            + " - No data returned from external server");
	}

	return Long.parseLong(str);
}

See complete MdlForumService.java file

Learner/Monitor

As you may have suspected another extension point that must be implemented is the learner and monitor interfaces. All the information you need to redirect to these pages should have been stored in the tool adapter's tables by the time you get to this point. So it should simply be a matter of constructing the learner and monitor URL based on the save parameters and then redirecting the user when the request is made.

...

No Format
public ActionForward unspecified(ActionMapping mapping, ActionForm form,
		HttpServletRequest request, HttpServletResponse response)
		throws Exception {

	//LearningForm learningForm = (LearningForm) form;

	// 'toolSessionID' and 'mode' paramters are expected to be present.
	// TODO need to catch exceptions and handle errors.
	//ToolAccessMode mode = WebUtil.readToolAccessModeParam(request,AttributeNames.PARAM_MODE, MODE_OPTIONAL);

	Long toolSessionID = WebUtil.readLongParam(request, AttributeNames.PARAM_TOOL_SESSION_ID);

	// set up mdlForumService
	if (mdlForumService == null) {
		mdlForumService = MdlForumServiceProxy.getMdlForumService(this.getServlet().getServletContext());
	}

	// Retrieve the session and content.
	MdlForumSession mdlForumSession = mdlForumService.getSessionBySessionId(toolSessionID);
	if (mdlForumSession == null) {
		throw new MdlForumException("Cannot retreive session with toolSessionID: "+ toolSessionID);
	}

	MdlForum mdlForum = mdlForumSession.getMdlForum();

	// check defineLater
	if (mdlForum.isDefineLater()) {
		return mapping.findForward("defineLater");
	}

	MdlForumDTO mdlForumDTO = new MdlForumDTO();
	request.setAttribute("mdlForumDTO", mdlForumDTO);

	// Set the content in use flag.
	if (!mdlForum.isContentInUse()) {
		mdlForum.setContentInUse(new Boolean(true));
		mdlForumService.saveOrUpdateMdlForum(mdlForum);
	}

	// check runOffline
	if (mdlForum.isRunOffline()) {
		return mapping.findForward("runOffline");
	}

	if (mdlForum.getExtToolContentId()!=null)
	{
		try{
			String responseUrl = mdlForumService.getConfigItem(MdlForumConfigItem.KEY_EXTERNAL_SERVER_URL).getConfigValue();
			responseUrl += RELATIVE_LEARNER_URL;

			String returnUrl = TOOL_APP_URL + "learning.do?"
				+ AttributeNames.PARAM_TOOL_SESSION_ID + "=" + toolSessionID.toString()
				+ "&dispatch=finishActivity";

			returnUrl = URLEncoder.encode(returnUrl, "UTF8");

			responseUrl += "&id=" + mdlForumSession.getExtSessionId()
				+ "&returnUrl=" + returnUrl;

			log.debug("Redirecting for mdl forum learner: " + responseUrl);
			response.sendRedirect(responseUrl);
		}
		catch (Exception e)
		{
			log.error("Could not redirect to mdl forum authoring", e);
		}
	}
	else
	{
		throw new MdlForumException ("External content id null for learner");
	}
	return null;
}

See complete LearningAction.java file

Export Learning Design

To export a LAMS learning design, the authoring tool content needs to be serialised into a file so it can be imported again later in a different context. LAMS will go through a sequence and call each tool to output its tool content. This is made a bit more complicated for tool adapter tools, since they do not have the content stored locally, they only have an external content id. When the call is made to export the tool adapter content they have to call the external tool adapter servlet to request the content in a server to server call.

...

No Format
 /**
 * Export the XML fragment for the tool's content, along with any files
 * needed for the content.
 *
 * @throws DataMissingException
 *             if no tool content matches the toolSessionId
 * @throws ToolException
 *             if any other error occurs
 */

public void exportToolContent(Long toolContentId, String rootPath)
		throws DataMissingException, ToolException {


	MdlForum mdlForum = mdlForumDAO.getByContentId(toolContentId);
	if (mdlForum == null) {
		mdlForum = getDefaultContent();
	}
	if (mdlForum == null)
		throw new DataMissingException("Unable to find default content for the mdlForum tool");

	// If no external content was found, export empty mdlForum
	// Otherwise, call the external servlet to get the export file
	if (mdlForum.getExtToolContentId() == null)
	{
		mdlForum.setExtToolContentId(null);
		mdlForum.setToolContentHandler(null);
		mdlForum.setMdlForumSessions(null);

		try {
			exportContentService.exportToolContent(toolContentId,
					mdlForum, mdlForumToolContentHandler, rootPath);
		} catch (ExportToolContentException e) {
			throw new ToolException(e);
		}
	}
	else
	{

		URLConnection conn = null;
		try
		{
			// Create the directory to store the export file
			String toolPath = FileUtil.getFullPath(rootPath,toolContentId.toString());
			FileUtil.createDirectory(toolPath);

			String exportServletUrl = mdlForumConfigItemDAO.getConfigItemByKey(MdlForumConfigItem.KEY_EXTERNAL_TOOL_SERVLET).getConfigValue();

			// setting these to arbitrary values since they are only used to construct the hash

			mdlForum.setExtCourseId("extCourse");
			mdlForum.setExtSection("0");
			mdlForum.setExtUsername("authUser");
			HashMap<String, String> params = this.getRequiredExtServletParams(mdlForum);
			params.put(EXT_SERVER_PARAM_METHOD, EXT_SERVER_METHOD_EXPORT);
			params.put(EXT_SERVER_PARAM_EXT_TOOL_CONTENT_ID, mdlForum.getExtToolContentId().toString());

			// Get the reponse stream from the external server (hopefully containing the export file
			InputStream in = WebUtility.getResponseInputStreamFromExternalServer(exportServletUrl, params);

			// Get the output stream to write the file for extport
			OutputStream out = new BufferedOutputStream(new FileOutputStream(toolPath + "/ext_tool.txt"));

			byte[] buffer = new byte[1024];
			int numRead;
			long numWritten = 0;
			while ((numRead = in.read(buffer)) != -1)
			{
				out.write(buffer, 0, numRead);
				numWritten += numRead;
			}
			logger.debug("Path to mdlForum export content: " + toolPath + "/ext_tool.txt");

			out.flush();
			out.close();
			in.close();
		}
		catch (Exception e)
		{
			logger.error("Problem exporting data from external .LRN servlet", e);
		}
	}
}

See complete MdlForumService.java file

 Import Learning Design

Once you have handled the export correctly, you must handle the import. For this, the exported files that are stored on the user's hard drive will have to be transported in a multi-part post to the external server. Again, here we will need to set up an action in the external tool adapter servlet. When a tool adapter tool is called to import tool content, it will be sent the path to a file, and the customCSV parameter. The customCSV parameter is included here because it is often the case that to create a new instance of an external tool, the course and user information is required. With this parameters you can construct the multipart request using the WebUtility.java file used for tool adapters.

...

No Format
File extExportFile = new File(toolContentPath + "/ext_tool.txt");

// if specially exported tooladapter file is found, send it to the external server
// otherwise, simply import an empty mdlForum
if (extExportFile.exists())
{

	try {

		String importServletUrl = mdlForumConfigItemDAO.getConfigItemByKey(MdlForumConfigItem.KEY_EXTERNAL_TOOL_SERVLET).getConfigValue();

		if (customCSV== null)
		{
			logger.error("Could not retrieve customCSV required for importing tool adapter tool. CustomCSV: " + customCSV);
			throw new ToolException("Could not retrieve customCSV required for importing tool adapter tool. CustomCSV: " + customCSV);
		}

		HashMap<String, String> params = getRequiredExtServletParams(customCSV);
		params.put(EXT_SERVER_PARAM_METHOD, EXT_SERVER_METHOD_IMPORT);

		// Do the external multipart post to upload the file to external LMS (returns an extToolContentId)
		InputStream is = WebUtility.performMultipartPost(extExportFile, EXT_SERVER_PARAM_UPLOAD_FILE,  importServletUrl, params);
		DataInputStream inStream = new DataInputStream ( is );
		String str = inStream.readLine();
		Long extContentId = Long.parseLong(str);
		inStream.close();

		// Save the resulting mdl forum
		MdlForum mdlForum = new MdlForum();
		mdlForum.setToolContentId(toolContentId);
		mdlForum.setCreateDate(new Date());
		mdlForum.setExtToolContentId(extContentId);
		saveOrUpdateMdlForum(mdlForum);

	} catch (Exception e) {
		logger.error("Problem passing mdlForum export file to external server", e);
		throw new ToolException(e);
	}
}

See complete MdlForumService.java file

Export Portfolio

Export Portfolio is another extension point that can be implemented although it is not essential as most LMS tool do not have a concept of export portfolio. Basically, another call should be made to the external servlet requesting the file, this request should pass the external tool content id and the user id.

...

No Format
private void doTeacherExport(HttpServletRequest request,
		HttpServletResponse response, String directoryName, Cookie[] cookies)
		throws MdlForumException {

	logger.debug("doExportTeacher: toolContentID:" + toolContentID);

	// check if toolContentID available
	if (toolContentID == null) {
		String error = "Tool Content ID is missing. Unable to continue";
		logger.error(error);
		throw new MdlForumException(error);
	}

	MdlForum mdlForum = mdlForumService.getMdlForumByContentId(toolContentID);
	MdlForumDTO mdlForumDTO = new MdlForumDTO(mdlForum);
	request.getSession().setAttribute("mdlForumDTO", mdlForumDTO);

	Set <MdlForumSession> sessions = mdlForum.getMdlForumSessions();
	for (MdlForumSession session : sessions)
	{
		try {
			String fullPath = directoryName + "/"+ session.getSessionName();
			exportFileFromExternalServer(request, response, session.getExtSessionId(), mdlForum,fullPath);
		} catch (Exception e) {
			logger.error("Could not fetch export file from external servlet", e);
			throw new MdlForumException("Could not fetch export file from external servlet", e);
		}
	}

	request.getSession().setAttribute("sessions", sessions);
}

private void exportFileFromExternalServer(HttpServletRequest request,
		HttpServletResponse response, Long extToolSessionId, MdlForum mdlForum, String fullPath) throws Exception
{
	String exportPortFolioServletUrl = mdlForumService.getConfigItem(MdlForumConfigItem.KEY_EXTERNAL_TOOL_SERVLET).getConfigValue();

	String extUsername="user"; // setting user to arbitrary values since they are only used to construct the hash

	HashMap<String, String> params = mdlForumService.getRequiredExtServletParams(mdlForum);
	params.put("method", IMdlForumService.EXT_SERVER_METHOD_EXPORT_PORTFOLIO);
	params.put("extToolContentID", extToolSessionId.toString());

	InputStream in = WebUtility.getResponseInputStreamFromExternalServer(exportPortFolioServletUrl, params);
	OutputStream out = new BufferedOutputStream(new FileOutputStream(fullPath));
	byte[] buffer = new byte[1024];
	int numRead;
	long numWritten = 0;
	logger.debug("Getting file...");
	while ((numRead = in.read(buffer)) != -1)
	{
		out.write(buffer, 0, numRead);
		logger.debug(new String(buffer));
		numWritten += numRead;
	}
	logger.debug("Path to mdlForum export portfolio content: " + fullPath);
	out.flush();
	out.close();
}

See complete ExportServlet.java file

Tool Outputs

Tool Outputs are also an options extension point that you may like to implement. Here you can follow the same steps as are outline in the tool outputs wiki page. But instead of the getToolOutputs() call being a local call, you will need to again call the tool adapter servlet and pass the external tool content id and the user id, then return any data you wish about what the student has achieved in the activity.

...

No Format
protected final static String OUTPUT_NAME_LEARNER_NUM_POSTS = "learner.number.of.posts";
protected final static String OUTPUT_NAME_LEARNER_NUM_WORDS = "learner.number.of.words";

public MdlForumOutputFactory() {}

/**
 * @see org.lamsfoundation.lams.tool.OutputDefinitionFactory#getToolOutputDefinitions(java.lang.Object)
 */
public SortedMap<String, ToolOutputDefinition> getToolOutputDefinitions(
		Object toolContentObject) throws ToolException {
	TreeMap<String, ToolOutputDefinition> definitionMap =  new TreeMap<String, ToolOutputDefinition>();

	ToolOutputDefinition definition = buildRangeDefinition(OUTPUT_NAME_LEARNER_NUM_POSTS, new Long(0), null);
	definitionMap.put(OUTPUT_NAME_LEARNER_NUM_POSTS, definition);

	ToolOutputDefinition definition2 = buildRangeDefinition(OUTPUT_NAME_LEARNER_NUM_WORDS, new Long(0), null);
	definitionMap.put(OUTPUT_NAME_LEARNER_NUM_WORDS, definition2);

	return definitionMap;
}
 
...
 
public ToolOutput getToolOutput(String name, IMdlForumService dlForumService,
		Long toolSessionId, Long learnerId, MdlForum getToolOutput, Long extSessionId) {
	if ( name != null)
	{
		return getExtToolOutput(name, dlForumService, getToolOutput, learnerId, extSessionId.toString(), toolSessionId);
	}
	return null;

}

public ToolOutput getExtToolOutput(String outputName, IMdlForumService mdlForumService, MdlForum mdlForum, Long userId, String extToolContentId, Long toolSessionId)
{
	int number = mdlForumService.getExternalToolOutputInt(outputName, mdlForum, userId, extToolContentId, toolSessionId);
	return new ToolOutput(outputName, getI18NText(outputName, true), new Long(number));
}

 See complete MdlForumOutputFactory.java file

Changed Java Files

Here is an itemised list of java files, and what has been changed in order to make the tool adapter work. It should be noted that the tool adapter tools were developed using the tool builder, with the Notebook tool being the template used.

When reading this section, all directories are relative to the lams_tool_mdlforum/src/java/org/lamsfoundation/lams/tool/mdfrum directory. See http://lamscvscode.melcoe.mq.edu.aulamsfoundation.org/fisheye/browse/lams/tool_adapters/moodle/lams_tool_mdlforum/src/java/org/lamsfoundation/lams/tool/mdfrum

/dao

This is the "data access object" directory, the java files under this directory talk through hibernate to the database. These files are not implemented differently to other tools

...

Currently there are two Tool Adapters, one for Moodle and one for .LRN. The Java code for these Tool Adapters can be browse in our CVS server. For changes to the LMS Tools to work with these Tool Adapters, see the Moodle Tool Adapter and .LRN Tool Adapters] pages respectively. Details for our CVS server are here.