Index: lams_build/lib/lams/lams.jar
===================================================================
diff -u -rbb597b8155375e6ac4dfe280f630d323b6e5e575 -rf26fb3937b73bfdefd25a6166863ea188d5f8cb9
Binary files differ
Index: lams_central/src/java/org/lamsfoundation/lams/web/QuestionsAction.java
===================================================================
diff -u -r0dbc1d60bc9b43c26c431c9a2e15981ca0863d46 -rf26fb3937b73bfdefd25a6166863ea188d5f8cb9
--- lams_central/src/java/org/lamsfoundation/lams/web/QuestionsAction.java	(.../QuestionsAction.java)	(revision 0dbc1d60bc9b43c26c431c9a2e15981ca0863d46)
+++ lams_central/src/java/org/lamsfoundation/lams/web/QuestionsAction.java	(.../QuestionsAction.java)	(revision f26fb3937b73bfdefd25a6166863ea188d5f8cb9)
@@ -28,8 +28,8 @@
  * Runs extraction of chosen IMS QTI zip file and prepares form for user to manually choose interesting question.
  * 
  * @struts.action path="/questions" validate="false"
- * @struts.action-forward name="questionChoice" path="/questionChoice.jsp"
- * @struts.action-forward name="questionFile" path="/questionFile.jsp"
+ * @struts.action-forward name="questionChoice" path="/questions/questionChoice.jsp"
+ * @struts.action-forward name="questionFile" path="/questions/questionFile.jsp"
  */
 public class QuestionsAction extends Action {
     @SuppressWarnings("unchecked")
@@ -58,7 +58,10 @@
 	    }
 	}
 
+	// this parameter is not really used at the moment
 	request.setAttribute("returnURL", returnURL);
+	
+	// show only chosen types of questions
 	request.setAttribute("limitType", limitTypeParam);
 
 	// user did not choose a file
Fisheye: Tag f26fb3937b73bfdefd25a6166863ea188d5f8cb9 refers to a dead (removed) revision in file `lams_central/web/questionChoice.jsp'.
Fisheye: No comparison available.  Pass `N' to diff?
Fisheye: Tag f26fb3937b73bfdefd25a6166863ea188d5f8cb9 refers to a dead (removed) revision in file `lams_central/web/questionFile.jsp'.
Fisheye: No comparison available.  Pass `N' to diff?
Index: lams_central/web/questions/imsmanifest_template.xml
===================================================================
diff -u
--- lams_central/web/questions/imsmanifest_template.xml	(revision 0)
+++ lams_central/web/questions/imsmanifest_template.xml	(revision f26fb3937b73bfdefd25a6166863ea188d5f8cb9)
@@ -0,0 +1,33 @@
+
+
+	
+		IMS Content
+		1.1.3
+		
+			
+				
+					[ID]
+				
+				
+					[TITLE] (LAMS IMS QTI export)
+				
+			
+		
+	
+	
+		
+			default
+			- 
+				Exam 1
+			
 
+		
+	
+	
+		
+			[FILE_LIST]
+		
+	
+
\ No newline at end of file
Index: lams_central/web/questions/questionChoice.jsp
===================================================================
diff -u
--- lams_central/web/questions/questionChoice.jsp	(revision 0)
+++ lams_central/web/questions/questionChoice.jsp	(revision f26fb3937b73bfdefd25a6166863ea188d5f8cb9)
@@ -0,0 +1,248 @@
+
+
+<%@ page language="java" pageEncoding="UTF-8" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="tags-lams" prefix="lams" %>
+<%@ taglib uri="tags-fmt" prefix="fmt" %>
+<%@ taglib uri="tags-core" prefix="c" %>
+<%@ taglib uri="tags-function" prefix="fn" %>
+
+
+
+	 :: 
+		
+	
+	
+	
+
+
+
+   
+
+
+
\ No newline at end of file
Index: lams_central/web/questions/questionFile.jsp
===================================================================
diff -u
--- lams_central/web/questions/questionFile.jsp	(revision 0)
+++ lams_central/web/questions/questionFile.jsp	(revision f26fb3937b73bfdefd25a6166863ea188d5f8cb9)
@@ -0,0 +1,62 @@
+
+
+<%@ page language="java" pageEncoding="UTF-8" contentType="text/html;charset=utf-8" %>
+<%@ taglib uri="tags-lams" prefix="lams" %>
+<%@ taglib uri="tags-fmt" prefix="fmt" %>
+<%@ taglib uri="tags-html" prefix="html" %>
+<%@ taglib uri="tags-core" prefix="c"%>
+
+
+
+	 :: 
+	
+		
+	
+	
+	
+	
+
+
+
+   
+
+
+
\ No newline at end of file
Index: lams_common/src/java/org/lamsfoundation/lams/questions/Question.java
===================================================================
diff -u -r0dbc1d60bc9b43c26c431c9a2e15981ca0863d46 -rf26fb3937b73bfdefd25a6166863ea188d5f8cb9
--- lams_common/src/java/org/lamsfoundation/lams/questions/Question.java	(.../Question.java)	(revision 0dbc1d60bc9b43c26c431c9a2e15981ca0863d46)
+++ lams_common/src/java/org/lamsfoundation/lams/questions/Question.java	(.../Question.java)	(revision f26fb3937b73bfdefd25a6166863ea188d5f8cb9)
@@ -32,6 +32,9 @@
 import org.apache.commons.lang.builder.HashCodeBuilder;
 
 public class Question {
+    // just for convenience when returning from methods
+    public static final Question[] QUESTION_ARRAY_TYPE = new Question[] {};
+    
     public static final String QUESTION_TYPE_MULTIPLE_CHOICE = "mc";
     public static final String QUESTION_TYPE_MULTIPLE_RESPONSE = "mr";
     public static final String QUESTION_TYPE_TRUE_FALSE = "tf";
Index: lams_common/src/java/org/lamsfoundation/lams/questions/QuestionExporter.java
===================================================================
diff -u
--- lams_common/src/java/org/lamsfoundation/lams/questions/QuestionExporter.java	(revision 0)
+++ lams_common/src/java/org/lamsfoundation/lams/questions/QuestionExporter.java	(revision f26fb3937b73bfdefd25a6166863ea188d5f8cb9)
@@ -0,0 +1,469 @@
+/****************************************************************
+ * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org)
+ * =============================================================
+ * License Information: http://lamsfoundation.org/licensing/lams/2.0/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA
+ *
+ * http://www.gnu.org/licenses/gpl.txt
+ * ****************************************************************
+ */
+
+/* $Id$ */
+package org.lamsfoundation.lams.questions;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.lamsfoundation.lams.util.CentralConstants;
+import org.lamsfoundation.lams.util.Configuration;
+import org.lamsfoundation.lams.util.ConfigurationKeys;
+import org.lamsfoundation.lams.util.FileUtil;
+import org.lamsfoundation.lams.util.zipfile.ZipFileUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Packs questions and answers into files. They can be later used in question-based 3rd party applications. Currently it
+ * supports only IMS QTI but other methods can be added as needed.
+ * 
+ * @author Marcin Cieslak
+ * 
+ */
+public class QuestionExporter {
+    private static final Logger log = Logger.getLogger(QuestionExporter.class);
+
+    private static final Pattern IMAGE_PATTERN = Pattern.compile("",
+	    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
+    private static final String IMAGE_MARKER = "[matimage]";
+
+    private static final String EXPORT_TEMP_FOLDER_SUFFIX = "qti";
+
+    private static final String EAR_IMAGE_FOLDER = Configuration.get(ConfigurationKeys.LAMS_EAR_DIR) + File.separator
+	    + FileUtil.LAMS_WWW_DIR;
+    private static final File MANIFEST_TEMPLATE_FILE = new File(Configuration.get(ConfigurationKeys.LAMS_EAR_DIR)
+	    + File.separator + "lams-central.war" + File.separator + "questions" + File.separator
+	    + "imsmanifest_template.xml");
+
+    private String packageTitle = null;
+    private Question[] questions = null;
+
+    private Document doc = null;
+    private Integer itemId = null;
+    private Map images = new TreeMap();
+
+    public QuestionExporter(String title, Question[] questions) {
+	if (StringUtils.isBlank(title)) {
+	    this.packageTitle = "export";
+	} else {
+	    // make sure the title can be used everywhere
+	    this.packageTitle = FileUtil.stripInvalidChars(title).replaceAll(" ", "_");
+	}
+
+	this.questions = questions;
+    }
+
+    /**
+     * Writes the exported QTI package to HTTP response, so it can be downloaded by user via browser.
+     */
+    public void exportQTIPackage(HttpServletRequest request, HttpServletResponse response) {
+	String packagePath = exportQTIPackage();
+	File packageFile = new File(packagePath);
+
+	try {
+	    String fileName = FileUtil.getFileName(packagePath);
+	    fileName = FileUtil.encodeFilenameForDownload(request, fileName);
+	    response.setContentType(CentralConstants.RESPONSE_CONTENT_TYPE_DOWNLOAD);
+	    response.setHeader(CentralConstants.HEADER_CONTENT_DISPOSITION, CentralConstants.HEADER_CONTENT_ATTACHMENT
+		    + fileName);
+
+	    // write out the ZIP to respose error
+	    FileUtils.copyFile(packageFile, response.getOutputStream());
+
+	    // remove the directory containing the ZIP from file system
+	    FileUtils.deleteDirectory(packageFile.getParentFile());
+	} catch (IOException e) {
+	    QuestionExporter.log.error("Error while exporti QTI package", e);
+	}
+    }
+
+    /**
+     * Builds a QTI ZIP package (manifest, QTI file, resources) with the given questions.
+     * 
+     * @return Path to the created ZIP file
+     */
+    public String exportQTIPackage() {
+	if (log.isDebugEnabled()) {
+	    log.debug("Exporting QTI ZIP package \"" + packageTitle + "\"");
+	}
+	try {
+	    String rootDir = FileUtil.createTempDirectory(QuestionExporter.EXPORT_TEMP_FOLDER_SUFFIX);
+	    File dir = new File(rootDir, "content");
+
+	    // main QTI file
+	    String xmlFileName = packageTitle + ".xml";
+	    File xmlFile = new File(dir, xmlFileName);
+	    String xmlFileContent = exportQTIFile();
+	    FileUtils.writeStringToFile(xmlFile, xmlFileContent, "UTF-8");
+
+	    File manifestFile = new File(dir, "imsmanifest.xml");
+	    String manifestContent = createManifest();
+	    FileUtils.writeStringToFile(manifestFile, manifestContent, "UTF-8");
+
+	    // copy images used in activities from lams-www to ZIP folder
+	    for (String imageName : images.keySet()) {
+		File imageFile = new File(dir, imageName);
+		FileUtils.copyFile(images.get(imageName), imageFile);
+	    }
+
+	    String targetZipFileName = "lams_qti_" + packageTitle + ".zip";
+
+	    return ZipFileUtil.createZipFile(targetZipFileName, dir.getAbsolutePath(), rootDir);
+	} catch (Exception e) {
+	    QuestionExporter.log.error("Error while exporti QTI package", e);
+	}
+
+	return null;
+    }
+
+    /**
+     * Builds QTI XML file containing structured questions & answers content.
+     * 
+     * @return XML file content
+     */
+    public String exportQTIFile() {
+	itemId = 1000;
+	DocumentBuilder docBuilder;
+	try {
+	    docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+	    doc = docBuilder.newDocument();
+	} catch (ParserConfigurationException e) {
+	    QuestionExporter.log.error("Error while instantinating Document Builder", e);
+	    return null;
+	}
+
+	Element rootElem = (Element) doc.appendChild(doc.createElement("questestinterop"));
+	Element assessmentElem = (Element) rootElem.appendChild(doc.createElement("assessment"));
+	assessmentElem.setAttribute("title", packageTitle);
+	assessmentElem.setAttribute("ident", "A1001");
+	Element sectionElem = (Element) assessmentElem.appendChild(doc.createElement("section"));
+	sectionElem.setAttribute("title", "Main");
+	sectionElem.setAttribute("ident", "S1002");
+
+	for (Question question : questions) {
+	    Element itemElem = null;
+	    if (Question.QUESTION_TYPE_MULTIPLE_CHOICE.equals(question.getType())) {
+		itemElem = exportMultipleChoiceQuestion(question);
+	    }
+
+	    if (itemElem == null) {
+		QuestionExporter.log.warn("Unknow type \"" + question.getType() + " of question \""
+			+ question.getTitle() + "\"");
+	    } else {
+		sectionElem.appendChild(itemElem);
+	    }
+	}
+
+	return writeOutDoc();
+    }
+
+    /**
+     * Creates a XML element with contents of a single multiple choice question.
+     */
+    private Element exportMultipleChoiceQuestion(Question question) {
+	Element itemElem = doc.createElement("item");
+	itemElem.setAttribute("title", question.getTitle());
+	itemId++;
+	itemElem.setAttribute("ident", "QUE_" + itemId);
+
+	// question text
+	Element presentationElem = (Element) itemElem.appendChild(doc.createElement("presentation"));
+	if (!StringUtils.isBlank(question.getText())) {
+	    Element materialElem = (Element) presentationElem.appendChild(doc.createElement("material"));
+	    appendMaterialElements(materialElem, question.getText());
+	}
+
+	itemId++;
+	String responseLidIdentifier = "QUE_" + itemId + "_RL";
+	Element responseLidElem = (Element) presentationElem.appendChild(doc.createElement("response_lid"));
+	responseLidElem.setAttribute("ident", responseLidIdentifier);
+	responseLidElem.setAttribute("rcardinality", "Single");
+	responseLidElem.setAttribute("rtiming", "No");
+
+	// question feedback (displayed no matter what answer was choosed)
+	List feedbackList = new ArrayList();
+	String incorrectFeedbackLabel = null;
+	Element overallFeedbackElem = null;
+	if (!StringUtils.isBlank(question.getFeedback())) {
+	    overallFeedbackElem = createFeedbackElem("_ALL", question.getFeedback());
+	    feedbackList.add(overallFeedbackElem);
+	}
+
+	Element renderChoiceElem = (Element) responseLidElem.appendChild(doc.createElement("render_choice"));
+	short answerId = 0;
+	List respconditionList = new ArrayList(question.getAnswers().size());
+
+	// iterate through answers, collecting some info along the way
+	for (Answer answer : question.getAnswers()) {
+	    Element responseLabelElem = (Element) renderChoiceElem.appendChild(doc.createElement("response_label"));
+	    itemId++;
+	    answerId++;
+	    String answerLabel = "QUE_" + itemId + "_A" + answerId;
+	    responseLabelElem.setAttribute("ident", answerLabel);
+
+	    // answer text
+	    if (!StringUtils.isBlank(answer.getText())) {
+		Element materialElem = (Element) responseLabelElem.appendChild(doc.createElement("material"));
+		appendMaterialElements(materialElem, answer.getText());
+	    }
+
+	    // just labels for feedback for correct/incorrect answer
+	    boolean isCorrect = answer.getScore() > 0;
+	    Element feedbackElem = null;
+	    if (!StringUtils.isBlank(answer.getFeedback())) {
+		feedbackElem = createFeedbackElem((isCorrect ? "_C" : "_IC"), answer.getFeedback());
+		feedbackList.add(feedbackElem);
+
+		if (!isCorrect && (incorrectFeedbackLabel == null)) {
+		    incorrectFeedbackLabel = feedbackElem.getAttribute("ident");
+		}
+	    }
+
+	    // mark which answer is correct by setting score for each of them
+	    Element respconditionElem = doc.createElement("respcondition");
+	    Element conditionvarElem = (Element) respconditionElem.appendChild(doc.createElement("conditionvar"));
+	    Element varequalElem = (Element) conditionvarElem.appendChild(doc.createElement("varequal"));
+	    varequalElem.setAttribute("respident", responseLidIdentifier);
+	    varequalElem.setTextContent(answerLabel);
+
+	    Element setvarElem = (Element) respconditionElem.appendChild(doc.createElement("setvar"));
+	    setvarElem.setAttribute("varname", "que_score");
+	    setvarElem.setAttribute("action", isCorrect ? "Set" : "Add");
+	    setvarElem.setTextContent(String.valueOf(answer.getScore()));
+
+	    // link feedback for correct/incorrect answer
+	    if (feedbackElem != null) {
+		Element displayfeedbackElem = (Element) respconditionElem.appendChild(doc
+			.createElement("displayfeedback"));
+		displayfeedbackElem.setAttribute("feedbacktype", "Response");
+		displayfeedbackElem.setAttribute("linkrefid", feedbackElem.getAttribute("ident"));
+	    } else if (!isCorrect && (incorrectFeedbackLabel != null)) {
+		Element displayfeedbackElem = (Element) respconditionElem.appendChild(doc
+			.createElement("displayfeedback"));
+		displayfeedbackElem.setAttribute("feedbacktype", "Response");
+		displayfeedbackElem.setAttribute("linkrefid", incorrectFeedbackLabel);
+	    }
+
+	    if (overallFeedbackElem != null) {
+		Element displayfeedbackElem = (Element) respconditionElem.appendChild(doc
+			.createElement("displayfeedback"));
+		displayfeedbackElem.setAttribute("feedbacktype", "Response");
+		displayfeedbackElem.setAttribute("linkrefid", overallFeedbackElem.getAttribute("ident"));
+	    }
+
+	    respconditionList.add(respconditionElem);
+	}
+
+	if (overallFeedbackElem != null) {
+	    Element respconditionElem = doc.createElement("respcondition");
+	    Element conditionvarElem = (Element) respconditionElem.appendChild(doc.createElement("conditionvar"));
+	    conditionvarElem.appendChild(doc.createElement("elem"));
+	    Element displayfeedbackElem = (Element) respconditionElem.appendChild(doc.createElement("displayfeedback"));
+	    displayfeedbackElem.setAttribute("feedbacktype", "Response");
+	    displayfeedbackElem.setAttribute("linkrefid", overallFeedbackElem.getAttribute("ident"));
+
+	    respconditionList.add(respconditionElem);
+	}
+
+	Element resprocessingElem = (Element) itemElem.appendChild(doc.createElement("resprocessing"));
+	Element outcomesElem = (Element) resprocessingElem.appendChild(doc.createElement("outcomes"));
+	Element decvarElem = (Element) outcomesElem.appendChild(doc.createElement("decvar"));
+	decvarElem.setAttribute("vartype", "decimal");
+	decvarElem.setAttribute("defaultval", "0");
+	decvarElem.setAttribute("varname", "que_score");
+
+	// write out elements collected during answer iteration
+	for (Element respconditionElem : respconditionList) {
+	    resprocessingElem.appendChild(respconditionElem);
+	}
+
+	for (Element feedbackElem : feedbackList) {
+	    itemElem.appendChild(feedbackElem);
+	}
+
+	return itemElem;
+    }
+
+    /**
+     * Creates a feedback XML element.
+     */
+    private Element createFeedbackElem(String labelSuffix, String feedback) {
+	itemId++;
+	String label = "QUE_" + itemId + labelSuffix;
+	Element feedbackElem = doc.createElement("itemfeedback");
+	feedbackElem.setAttribute("ident", label);
+	Element materialElem = (Element) feedbackElem.appendChild(doc.createElement("material"));
+	appendMaterialElements(materialElem, feedback);
+
+	return feedbackElem;
+    }
+
+    /**
+     * Transforms a DOM object to String representation.
+     */
+    private String writeOutDoc() {
+	DOMSource domSource = new DOMSource(doc);
+	StringWriter writer = new StringWriter();
+	StreamResult streamResult = new StreamResult(writer);
+	TransformerFactory tf = TransformerFactory.newInstance();
+	try {
+	    Transformer transformer = tf.newTransformer();
+	    transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+	    // a bit of beautification
+	    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+	    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
+	    transformer.transform(domSource, streamResult);
+	} catch (Exception e) {
+	    QuestionExporter.log.error("Error while writing out XML document", e);
+	    return null;
+	}
+
+	String result = writer.toString();
+	try {
+	    writer.close();
+	} catch (IOException e) {
+	    QuestionExporter.log.warn("Writer could not be closed", e);
+	}
+
+	return result;
+    }
+
+    /**
+     * Extracts images from HTML text (probably created by CKEditor) and substitutes them with markers, so further
+     * processing knows how to handle them.
+     */
+    private String[] parseImages(String text) {
+	List result = new ArrayList();
+	int index = 0;
+	// looks for images stored in LAMS WWW secure folder
+	Matcher matcher = QuestionExporter.IMAGE_PATTERN.matcher(text);
+
+	while (matcher.find()) {
+	    // add HTML which is before the image
+	    result.add(text.substring(index, matcher.start()));
+	    index = matcher.end();
+
+	    // find the image in file system
+	    String relativePath = matcher.group(1);
+	    File image = new File(QuestionExporter.EAR_IMAGE_FOLDER, relativePath);
+	    if (!image.isFile() || !image.canRead()) {
+		QuestionExporter.log.warn("Image could not be parsed: " + matcher.group());
+		continue;
+	    }
+
+	    // was it already added?
+	    String imageName = null;
+	    for (String key : images.keySet()) {
+		if (image.equals(images.get(key))) {
+		    imageName = key;
+		    break;
+		}
+	    }
+
+	    // if it wasn't added, store the reference so ZIP packing knows where to copy from and how to name the file
+	    if (imageName == null) {
+		String baseImageName = FileUtil.getFileName(relativePath);
+		imageName = baseImageName;
+		short prefix = 1;
+		while (images.containsKey(imageName)) {
+		    // if the name is the same, use an arbitrary prefix
+		    imageName = prefix + "_" + baseImageName;
+		    prefix++;
+		}
+
+		images.put(imageName, image);
+	    }
+
+	    result.add(QuestionExporter.IMAGE_MARKER + imageName);
+	}
+
+	// write out the rest of HTML text
+	if (index < text.length()) {
+	    result.add(text.substring(index));
+	}
+
+	return result.toArray(new String[] {});
+    }
+
+    /**
+     * Appends material XML element, i.e. list of HTML & image parts
+     */
+    private void appendMaterialElements(Element materialElem, String text) {
+	String[] answerParts = parseImages(text);
+	for (String answerPart : answerParts) {
+	    if (answerPart.startsWith(QuestionExporter.IMAGE_MARKER)) {
+		String imageName = answerPart.substring(QuestionExporter.IMAGE_MARKER.length());
+		String imageType = "image/" + FileUtil.getFileExtension(imageName);
+		Element matimageElem = (Element) materialElem.appendChild(doc.createElement("matimage"));
+		matimageElem.setAttribute("imagtype", imageType);
+		matimageElem.setAttribute("uri", imageName);
+	    } else {
+		Element mattextElem = (Element) materialElem.appendChild(doc.createElement("mattext"));
+		mattextElem.setAttribute("texttype", "text/html");
+		mattextElem.appendChild(doc.createCDATASection(answerPart));
+	    }
+	}
+    }
+
+    /**
+     * Fill the existing template file with current data.
+     * @return contents of XML template file
+     */
+    private String createManifest() throws IOException {
+	String id = UUID.randomUUID().toString();
+	String fileName = packageTitle + ".xml";
+
+	StringBuilder resourceEntries = new StringBuilder("\n");
+	for (String imageName : images.keySet()) {
+	    resourceEntries.append("").append("\n");
+	}
+
+	String manifest = FileUtils.readFileToString(QuestionExporter.MANIFEST_TEMPLATE_FILE);
+	manifest = manifest.replace("[ID]", id).replace("[TITLE]", packageTitle).replace("[FILE_NAME]", fileName)
+		.replace("[FILE_LIST]", resourceEntries);
+	return manifest;
+    }
+}
\ No newline at end of file
Index: lams_common/src/java/org/lamsfoundation/lams/questions/QuestionParser.java
===================================================================
diff -u -r04a5075bd2cb89afd1f7b323d8e03ef4fa2f83e3 -rf26fb3937b73bfdefd25a6166863ea188d5f8cb9
--- lams_common/src/java/org/lamsfoundation/lams/questions/QuestionParser.java	(.../QuestionParser.java)	(revision 04a5075bd2cb89afd1f7b323d8e03ef4fa2f83e3)
+++ lams_common/src/java/org/lamsfoundation/lams/questions/QuestionParser.java	(.../QuestionParser.java)	(revision f26fb3937b73bfdefd25a6166863ea188d5f8cb9)
@@ -69,8 +69,6 @@
 public class QuestionParser {
     private static Logger log = Logger.getLogger(QuestionParser.class);
 
-    // just for convenience when returning from methods
-    private static final Question[] QUESTION_ARRAY_TYPE = new Question[] {};
     // can be anything
     private static final String TEMP_PACKAGE_NAME_PREFIX = "QTI_PACKAGE_";
     private static final Pattern IMAGE_PATTERN = Pattern.compile("\\[IMAGE: (.*)\\]");
@@ -124,7 +122,7 @@
 	    }
 	}
 
-	return result.toArray(QuestionParser.QUESTION_ARRAY_TYPE);
+	return result.toArray(Question.QUESTION_ARRAY_TYPE);
     }
 
     /**
@@ -362,7 +360,7 @@
 	    result.add(question);
 	}
 
-	return result.toArray(QuestionParser.QUESTION_ARRAY_TYPE);
+	return result.toArray(Question.QUESTION_ARRAY_TYPE);
     }
 
     /**
@@ -463,7 +461,7 @@
 	    }
 	}
 
-	return result.toArray(QuestionParser.QUESTION_ARRAY_TYPE);
+	return result.toArray(Question.QUESTION_ARRAY_TYPE);
     }
 
     /**
Index: lams_tool_lamc/conf/language/lams/ApplicationResources.properties
===================================================================
diff -u -red84afe5b317b35b156848df66a1a9332af0f4b1 -rf26fb3937b73bfdefd25a6166863ea188d5f8cb9
--- lams_tool_lamc/conf/language/lams/ApplicationResources.properties	(.../ApplicationResources.properties)	(revision ed84afe5b317b35b156848df66a1a9332af0f4b1)
+++ lams_tool_lamc/conf/language/lams/ApplicationResources.properties	(.../ApplicationResources.properties)	(revision f26fb3937b73bfdefd25a6166863ea188d5f8cb9)
@@ -261,6 +261,7 @@
 monitor.summary.date.restriction.set  =Deadline has been set
 monitor.summary.date.restriction.removed  =Deadline has been removed
 label.authoring.import.qti  =Import IMS QTI
+label.authoring.export.qti  =Export IMS QTI
 error.correct.answer.blank  =Please correct this: Correct answer cannot be blank.
 label.submit  =Finish
 label.report.by.question  =Report by question
Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/web/McAction.java
===================================================================
diff -u -r9375ad2403d36eb4af0ddd61ad395765f15e2e67 -rf26fb3937b73bfdefd25a6166863ea188d5f8cb9
--- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/web/McAction.java	(.../McAction.java)	(revision 9375ad2403d36eb4af0ddd61ad395765f15e2e67)
+++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/web/McAction.java	(.../McAction.java)	(revision f26fb3937b73bfdefd25a6166863ea188d5f8cb9)
@@ -52,6 +52,7 @@
 import org.lamsfoundation.lams.contentrepository.client.IToolContentHandler;
 import org.lamsfoundation.lams.questions.Answer;
 import org.lamsfoundation.lams.questions.Question;
+import org.lamsfoundation.lams.questions.QuestionExporter;
 import org.lamsfoundation.lams.questions.QuestionParser;
 import org.lamsfoundation.lams.tool.exception.ToolException;
 import org.lamsfoundation.lams.tool.mc.EditActivityDTO;
@@ -469,7 +470,7 @@
 		for (Answer answer : question.getAnswers()) {
 		    McCandidateAnswersDTO mcCandidateAnswersDTO = new McCandidateAnswersDTO();
 		    String answerText = QuestionParser.processHTMLField(answer.getText(), false, contentFolderID,
-				question.getResourcesFolderPath());
+			    question.getResourcesFolderPath());
 		    if (answerText == null) {
 			LamsDispatchAction.log.warn("Skipping a blank answer");
 			continue;
@@ -531,9 +532,52 @@
 		defaultContentIdStr, mcService, httpSessionID, listQuestionContentDTO);
 
 	request.setAttribute(McAppConstants.TOTAL_QUESTION_COUNT, new Integer(listQuestionContentDTO.size()));
-	return (mapping.findForward(McAppConstants.LOAD));
+	return mapping.findForward(McAppConstants.LOAD);
     }
 
+    /**
+     * Prepares MC questions for QTI packing
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public ActionForward exportQTI(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+	    HttpServletResponse response) throws IOException {
+	String httpSessionID = request.getParameter("httpSessionID");
+	SessionMap sessionMap = (SessionMap) request.getSession().getAttribute(httpSessionID);
+
+	List listQuestionContentDTO = (List) sessionMap
+		.get(McAppConstants.LIST_QUESTION_CONTENT_DTO_KEY);
+	List questions = new LinkedList();
+
+	for (McQuestionContentDTO mcQuestion : listQuestionContentDTO) {
+	    Question question = new Question();
+
+	    question.setType(Question.QUESTION_TYPE_MULTIPLE_CHOICE);
+	    question.setTitle("Question " + mcQuestion.getDisplayOrder());
+	    question.setText(mcQuestion.getQuestion());
+	    question.setFeedback(mcQuestion.getFeedback());
+	    List answers = new ArrayList();
+
+	    for (McCandidateAnswersDTO mcAnswer : (List) mcQuestion.getListCandidateAnswersDTO()) {
+		Answer answer = new Answer();
+		answer.setText(mcAnswer.getCandidateAnswer());
+		answer.setScore("Correct".equalsIgnoreCase(mcAnswer.getCorrect()) ? Float.parseFloat(mcQuestion
+			.getMark()) : 0);
+
+		answers.add(answer);
+		question.setAnswers(answers);
+	    }
+
+	    // put the question in the right place
+	    questions.add(Integer.parseInt(mcQuestion.getDisplayOrder()) - 1, question);
+	}
+
+	String title = request.getParameter("title");
+	QuestionExporter exporter = new QuestionExporter(title, questions.toArray(Question.QUESTION_ARRAY_TYPE));
+	exporter.exportQTIPackage(request, response);
+
+	return null;
+    }
+
     protected void commonSaveCode(HttpServletRequest request, McGeneralAuthoringDTO mcGeneralAuthoringDTO,
 	    McAuthoringForm mcAuthoringForm, SessionMap sessionMap, String activeModule, String strToolContentID,
 	    String defaultContentIdStr, IMcService mcService, String httpSessionID, List listQuestionContentDTO) {
@@ -1235,7 +1279,6 @@
 
 	return (mapping.findForward("itemList"));
     }
- 
 
     /**
      * 
Index: lams_tool_lamc/web/authoring/BasicContent.jsp
===================================================================
diff -u -r87ff0b33fa2d006a084b2ae9b7ff14d4c4be0f6d -rf26fb3937b73bfdefd25a6166863ea188d5f8cb9
--- lams_tool_lamc/web/authoring/BasicContent.jsp	(.../BasicContent.jsp)	(revision 87ff0b33fa2d006a084b2ae9b7ff14d4c4be0f6d)
+++ lams_tool_lamc/web/authoring/BasicContent.jsp	(.../BasicContent.jsp)	(revision f26fb3937b73bfdefd25a6166863ea188d5f8cb9)
@@ -44,29 +44,23 @@
 	};
         
     function importQTI(){
-    	window.open('questionFile.jsp?limitType=mc',
+    	window.open('questions/questionFile.jsp?limitType=mc',
     			    'QuestionFile','width=500,height=200,scrollbars=yes');
     }
 	
     function saveQTI(formHTML, formName) {
-    	var form = $($.parseHTML(formHTML));
-		$.ajax({
-			type: "POST",
-			url: '',
-			data: form.serializeArray(),
-			success: function(response) {
-				$(questionListTargetDiv).html(response);
-				refreshThickbox();
-			}
-		});
-    }
-    
-    function saveQTI(formHTML, formName) {
     	document.body.innerHTML += formHTML;
     	var form = document.getElementById(formName);
     	form.action = '';
     	form.submit();
     }
+    
+    function exportQTI(){
+    	var frame = document.getElementById("downloadFileDummyIframe"),
+    		title = encodeURIComponent(document.getElementsByName("title")[0].value);
+    	frame.src = ''
+    			+ '&title=' + title;
+    }
 
 
 
@@ -111,6 +105,9 @@
 		
 			
 		
+		
+			
+		
 	
 	
 	
@@ -126,4 +123,7 @@
 		
 			 
 	
-
\ No newline at end of file
+
+
+
+
\ No newline at end of file